Rather than plugging a matrix into the video input node, the matrix is now multiplied by the video input using a math node. This is probably more sensible from a user perspective. This also means the renderer is tolerant of texture sizes that are not equal to the sequence size, however most nodes will downsample the texture to the sequence size (and if not, it will be downsampled once it is cached). Textures will still *always* be in reference space and the sequence's format. This seems like the best compromise between backend and frontend congruity.
219 lines
7.4 KiB
C++
219 lines
7.4 KiB
C++
/***
|
|
|
|
Olive - Non-Linear Video Editor
|
|
Copyright (C) 2019 Olive Team
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
#include "timelineviewblockitem.h"
|
|
|
|
#include <QBrush>
|
|
#include <QCoreApplication>
|
|
#include <QDir>
|
|
#include <QFloat16>
|
|
#include <QGraphicsScene>
|
|
#include <QGraphicsSceneMouseEvent>
|
|
#include <QPainter>
|
|
#include <QStyleOptionGraphicsItem>
|
|
|
|
#include "common/qtutils.h"
|
|
#include "config/config.h"
|
|
#include "core.h"
|
|
#include "node/block/transition/transition.h"
|
|
#include "widget/viewer/audiowaveformview.h"
|
|
|
|
OLIVE_NAMESPACE_ENTER
|
|
|
|
TimelineViewBlockItem::TimelineViewBlockItem(Block *block, QGraphicsItem* parent) :
|
|
TimelineViewRect(parent),
|
|
block_(block)
|
|
{
|
|
setBrush(Qt::white);
|
|
setCursor(Qt::DragMoveCursor);
|
|
setFlag(QGraphicsItem::ItemIsSelectable,
|
|
block_->type() == Block::kClip
|
|
|| block_->type() == Block::kGap
|
|
|| block_->type() == Block::kTransition);
|
|
|
|
UpdateRect();
|
|
}
|
|
|
|
Block *TimelineViewBlockItem::block() const
|
|
{
|
|
return block_;
|
|
}
|
|
|
|
void TimelineViewBlockItem::UpdateRect()
|
|
{
|
|
double item_left = TimeToScene(block_->in());
|
|
double item_width = TimeToScene(block_->length());
|
|
|
|
// -1 on width and height so we don't overlap any adjacent clips
|
|
setRect(0, y_, item_width - 1, height_);
|
|
setPos(item_left, 0.0);
|
|
|
|
setToolTip(QCoreApplication::translate("TimelineViewBlockItem",
|
|
"%1\n\nIn: %2\nOut: %3\nLength: %4").arg(block_->Name(),
|
|
Timecode::time_to_timecode(block_->in(), timebase(), Core::instance()->GetTimecodeDisplay()),
|
|
Timecode::time_to_timecode(block_->out(), timebase(), Core::instance()->GetTimecodeDisplay()),
|
|
Timecode::time_to_timecode(block_->out() - block_->in(), timebase(), Core::instance()->GetTimecodeDisplay())));
|
|
}
|
|
|
|
void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
|
|
{
|
|
switch (block_->type()) {
|
|
case Block::kClip:
|
|
{
|
|
QLinearGradient grad;
|
|
grad.setStart(0, rect().top());
|
|
grad.setFinalStop(0, rect().bottom());
|
|
|
|
if (block_->is_enabled()) {
|
|
grad.setColorAt(0.0, QColor(160, 160, 240));
|
|
grad.setColorAt(1.0, QColor(128, 128, 192));
|
|
} else {
|
|
grad.setColorAt(0.0, QColor(160, 160, 160));
|
|
grad.setColorAt(1.0, QColor(128, 128, 128));
|
|
}
|
|
|
|
painter->fillRect(rect(), grad);
|
|
|
|
if (option->state & QStyle::State_Selected) {
|
|
painter->fillRect(rect(), QColor(0, 0, 0, 64));
|
|
}
|
|
|
|
// Draw waveform if one is available
|
|
QString wave_fn = QDir(QDir(Config::Current()["DiskCachePath"].toString()).filePath("waveform")).filePath(QString::number(reinterpret_cast<quintptr>(block_)));
|
|
QFile wave_file(wave_fn);
|
|
if (wave_file.open(QFile::ReadOnly)){
|
|
painter->setPen(QColor(64, 64, 64));
|
|
|
|
QByteArray w = wave_file.readAll();
|
|
|
|
wave_file.close();
|
|
|
|
// Read metadata
|
|
SampleSummer::Info info;
|
|
QFile wave_meta(wave_fn.append(QStringLiteral(".meta")));
|
|
if (wave_meta.open(QFile::ReadOnly)) {
|
|
wave_meta.read(reinterpret_cast<char*>(&info), sizeof(SampleSummer::Info));
|
|
wave_meta.close();
|
|
}
|
|
|
|
// Prevent divide by zero
|
|
if (info.channels) {
|
|
AudioWaveformView::DrawWaveform(painter,
|
|
rect().toRect(),
|
|
this->GetScale(),
|
|
reinterpret_cast<const SampleSummer::Sum*>(w.constData()),
|
|
w.size() / sizeof(SampleSummer::Sum),
|
|
info.channels);
|
|
}
|
|
}
|
|
|
|
painter->setPen(Qt::white);
|
|
painter->drawLine(rect().topLeft(), QPointF(rect().right(), rect().top()));
|
|
painter->drawLine(rect().topLeft(), QPointF(rect().left(), rect().bottom() - 1));
|
|
|
|
// Draw text
|
|
if (block_->is_enabled()) {
|
|
painter->setPen(Qt::white);
|
|
} else {
|
|
painter->setPen(Qt::lightGray);
|
|
}
|
|
|
|
int text_top = TrackOutput::GetTrackHeightMinimum() / 2 - painter->fontMetrics().height() / 2;
|
|
QRectF text_rect = rect();
|
|
text_rect.adjust(0, text_top, 0, 0);
|
|
painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignTop, block_->block_name());
|
|
|
|
// Linked clips are underlined
|
|
if (block_->HasLinks()) {
|
|
QFontMetrics fm = painter->fontMetrics();
|
|
int text_width = qMin(qRound(rect().width()), QFontMetricsWidth(fm, block_->block_name()));
|
|
|
|
QPointF underline_start = rect().topLeft() + QPointF(0, fm.height());
|
|
QPointF underline_end = underline_start + QPointF(text_width, 0);
|
|
|
|
painter->drawLine(underline_start, underline_end);
|
|
}
|
|
|
|
painter->setPen(QColor(64, 64, 64));
|
|
painter->drawLine(QPointF(rect().left(), rect().bottom() - 1), QPointF(rect().right(), rect().bottom() - 1));
|
|
painter->drawLine(QPointF(rect().right(), rect().bottom() - 1), QPointF(rect().right(), rect().top()));
|
|
break;
|
|
}
|
|
case Block::kGap:
|
|
if (option->state & QStyle::State_Selected) {
|
|
// FIXME: Make this palette or CSS
|
|
painter->fillRect(rect(), QColor(255, 255, 255, 128));
|
|
}
|
|
break;
|
|
case Block::kTransition:
|
|
{
|
|
QLinearGradient grad;
|
|
grad.setStart(0, rect().top());
|
|
grad.setFinalStop(0, rect().bottom());
|
|
grad.setColorAt(0.0, QColor(192, 160, 224));
|
|
grad.setColorAt(1.0, QColor(160, 128, 192));
|
|
painter->setBrush(grad);
|
|
painter->setPen(QPen(QColor(96, 80, 112), 1));
|
|
painter->drawRect(rect());
|
|
|
|
if (option->state & QStyle::State_Selected) {
|
|
painter->fillRect(rect(), QColor(0, 0, 0, 64));
|
|
}
|
|
|
|
// Draw lines antialiased
|
|
painter->setRenderHint(QPainter::Antialiasing);
|
|
|
|
TransitionBlock* t = static_cast<TransitionBlock*>(block_);
|
|
|
|
if (t->connected_out_block() && t->connected_in_block()) {
|
|
|
|
// Draw line between out offset and in offset
|
|
qreal crossover_line = rect().left();
|
|
crossover_line += TimeToScene(t->out_offset());
|
|
painter->drawLine(qRound(crossover_line),
|
|
qRound(rect().top()),
|
|
qRound(crossover_line),
|
|
qRound(rect().bottom()));
|
|
|
|
// Draw lines to mid point
|
|
QPointF mid_point(crossover_line, rect().center().y());
|
|
painter->drawLine(rect().topLeft(), mid_point);
|
|
painter->drawLine(rect().bottomLeft(), mid_point);
|
|
painter->drawLine(rect().topRight(), mid_point);
|
|
painter->drawLine(rect().bottomRight(), mid_point);
|
|
|
|
} else if (t->connected_out_block()) {
|
|
|
|
// Transition fades something out, we'll draw a line
|
|
painter->drawLine(rect().topLeft(), rect().bottomRight());
|
|
|
|
} else if (t->connected_in_block()) {
|
|
|
|
// Transition fades something in, we'll draw a line
|
|
painter->drawLine(rect().bottomLeft(), rect().topRight());
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
OLIVE_NAMESPACE_EXIT
|