Merge branch 'master' into multicam

This commit is contained in:
itsmattkc
2022-09-25 16:39:03 -07:00
100 changed files with 2450 additions and 977 deletions
+4 -2
View File
@@ -24,14 +24,16 @@
namespace olive {
ColorButton::ColorButton(ColorManager* color_manager, QWidget *parent) :
ColorButton::ColorButton(ColorManager* color_manager, bool show_dialog_on_click, QWidget *parent) :
QPushButton(parent),
color_manager_(color_manager),
color_processor_(nullptr)
{
setAutoFillBackground(true);
connect(this, &ColorButton::clicked, this, &ColorButton::ShowColorDialog);
if (show_dialog_on_click) {
connect(this, &ColorButton::clicked, this, &ColorButton::ShowColorDialog);
}
SetColor(Color(1.0f, 1.0f, 1.0f));
}
+5 -1
View File
@@ -32,7 +32,11 @@ class ColorButton : public QPushButton
{
Q_OBJECT
public:
ColorButton(ColorManager* color_manager, QWidget* parent = nullptr);
ColorButton(ColorManager* color_manager, bool show_dialog_on_click, QWidget* parent = nullptr);
ColorButton(ColorManager* color_manager, QWidget* parent = nullptr) :
ColorButton(color_manager, true, parent)
{
}
const ManagedColor& GetColor() const;
-2
View File
@@ -20,7 +20,5 @@ set(OLIVE_SOURCES
widget/colorlabelmenu/colorcodingcombobox.h
widget/colorlabelmenu/colorlabelmenu.cpp
widget/colorlabelmenu/colorlabelmenu.h
widget/colorlabelmenu/colorlabelmenuitem.cpp
widget/colorlabelmenu/colorlabelmenuitem.h
PARENT_SCOPE
)
+15 -9
View File
@@ -21,6 +21,7 @@
#include "colorlabelmenu.h"
#include <QEvent>
#include <QPainter>
#include <QWidgetAction>
#include "ui/colorcoding.h"
@@ -30,17 +31,22 @@ namespace olive {
ColorLabelMenu::ColorLabelMenu(QWidget *parent) :
Menu(parent)
{
// Used for size calculations
int box_size = fontMetrics().height();
color_items_.resize(ColorCoding::standard_colors().size());
for (int i=0; i<ColorCoding::standard_colors().size(); i++) {
ColorLabelMenuItem* item = new ColorLabelMenuItem();
item->SetColor(ColorCoding::standard_colors().at(i));
color_items_.append(item);
QPixmap p(box_size, box_size);
QWidgetAction* a = new QWidgetAction(this);
Menu::ConformItem(a, QStringLiteral("colorlabel%1").arg(i), this, &ColorLabelMenu::ActionTriggered);
QPainter painter(&p);
painter.setPen(Qt::black);
painter.setBrush(ColorCoding::standard_colors().at(i).toQColor());
painter.drawRect(p.rect().adjusted(0, 0, -1, -1));
QAction *a = AddItem(QStringLiteral("colorlabel%1").arg(i), this, &ColorLabelMenu::ActionTriggered);
a->setIcon(p);
a->setData(i);
a->setDefaultWidget(item);
this->addAction(a);
color_items_.replace(i, a);
}
Retranslate();
@@ -60,7 +66,7 @@ void ColorLabelMenu::Retranslate()
this->setTitle(tr("Color"));
for (int i=0; i<color_items_.size(); i++) {
color_items_.at(i)->SetText(ColorCoding::GetColorName(i));
color_items_.at(i)->setText(ColorCoding::GetColorName(i));
}
}
+1 -2
View File
@@ -21,7 +21,6 @@
#ifndef COLORLABELMENU_H
#define COLORLABELMENU_H
#include "colorlabelmenuitem.h"
#include "widget/menu/menu.h"
namespace olive {
@@ -40,7 +39,7 @@ signals:
private:
void Retranslate();
QVector<ColorLabelMenuItem*> color_items_;
QVector<QAction*> color_items_;
private slots:
void ActionTriggered();
@@ -1,57 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 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 "colorlabelmenuitem.h"
#include <QHBoxLayout>
#include "ui/style/style.h"
namespace olive {
ColorLabelMenuItem::ColorLabelMenuItem(QWidget* parent) :
QWidget(parent)
{
int text_height = fontMetrics().height();
int padding = text_height/4;
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setMargin(padding);
layout->setSpacing(padding);
box_ = new ColorPreviewBox();
box_->setFixedSize(text_height, text_height);
layout->addWidget(box_);
label_ = new QLabel();
layout->addWidget(label_);
}
void ColorLabelMenuItem::SetText(const QString &text)
{
label_->setText(text);
}
void ColorLabelMenuItem::SetColor(const Color &color)
{
box_->SetColor(color);
}
}
@@ -1,48 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 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/>.
***/
#ifndef COLORLABELMENUITEM_H
#define COLORLABELMENUITEM_H
#include <QLabel>
#include <QWidget>
#include "widget/colorwheel/colorpreviewbox.h"
namespace olive {
class ColorLabelMenuItem : public QWidget
{
public:
ColorLabelMenuItem(QWidget* parent = nullptr);
void SetText(const QString& text);
void SetColor(const Color& color);
private:
ColorPreviewBox* box_;
QLabel* label_;
};
}
#endif // COLORLABELMENUITEM_H
+2
View File
@@ -22,6 +22,8 @@ set(OLIVE_SOURCES
widget/colorwheel/colorpreviewbox.cpp
widget/colorwheel/colorspacechooser.h
widget/colorwheel/colorspacechooser.cpp
widget/colorwheel/colorswatchchooser.h
widget/colorwheel/colorswatchchooser.cpp
widget/colorwheel/colorswatchwidget.h
widget/colorwheel/colorswatchwidget.cpp
widget/colorwheel/colorvalueswidget.h
@@ -0,0 +1,223 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 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 "colorswatchchooser.h"
#include <QGridLayout>
#include "common/filefunctions.h"
#include "widget/menu/menu.h"
namespace olive {
const int kDefaultColorCount = 16;
const Color kDefaultColors[kDefaultColorCount] = {
Color(1.0, 1.0, 1.0),
Color(1.0, 1.0, 0.0),
Color(1.0, 0.5, 0.0),
Color(1.0, 0.0, 0.0),
Color(1.0, 0.0, 1.0),
Color(0.5, 0.0, 1.0),
Color(0.0, 0.0, 1.0),
Color(0.0, 0.5, 1.0),
Color(0.0, 1.0, 0.0),
Color(0.0, 0.5, 0.0),
Color(0.5, 0.25, 0.0),
Color(0.75, 0.5, 0.25),
Color(0.75, 0.75, 0.75),
Color(0.5, 0.5, 0.5),
Color(0.25, 0.25, 0.25),
Color(0.0, 0.0, 0.0)
};
ColorSwatchChooser::ColorSwatchChooser(ColorManager *manager, QWidget *parent) :
QWidget(parent)
{
auto layout = new QGridLayout(this);
for (int x=0; x<kColCount; x++) {
for (int y=0; y<kRowCount; y++) {
// Create button
auto b = new ColorButton(manager, false);
b->setFixedWidth(b->sizeHint().height()/2*3);
b->setContextMenuPolicy(Qt::CustomContextMenu);
layout->addWidget(b, y, x);
// Save button in buttons array
int btn_index = x + kColCount*y;
buttons_[btn_index] = b;
// Set default color
SetDefaultColor(btn_index);
// Connect clicks
connect(b, &ColorButton::clicked, this, &ColorSwatchChooser::HandleButtonClick);
connect(b, &ColorButton::customContextMenuRequested, this, &ColorSwatchChooser::HandleContextMenu);
}
}
LoadSwatches();
}
void ColorSwatchChooser::SetDefaultColor(int index)
{
if (index < kDefaultColorCount) {
buttons_[index]->SetColor(kDefaultColors[index]);
} else {
buttons_[index]->SetColor(Color(1.0, 1.0, 1.0));
}
}
void ColorSwatchChooser::HandleButtonClick()
{
auto b = static_cast<ColorButton*>(sender());
emit ColorClicked(b->GetColor());
SetCurrentColor(b->GetColor());
}
void ColorSwatchChooser::HandleContextMenu()
{
Menu m(this);
auto save_action = m.addAction(tr("Save Color Here"));
connect(save_action, &QAction::triggered, this, &ColorSwatchChooser::SaveCurrentColor);
m.addSeparator();
auto reset_action = m.addAction(tr("Reset To Default"));
connect(reset_action, &QAction::triggered, this, &ColorSwatchChooser::ResetMenuButton);
menu_btn_ = static_cast<ColorButton*>(sender());
m.exec(QCursor::pos());
}
void ColorSwatchChooser::SaveCurrentColor()
{
menu_btn_->SetColor(current_);
SaveSwatches();
}
void ColorSwatchChooser::ResetMenuButton()
{
for (int i=0; i<kBtnCount; i++) {
if (buttons_[i] == menu_btn_) {
SetDefaultColor(i);
break;
}
}
}
QString ColorSwatchChooser::GetSwatchFilename()
{
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("swatch"));
}
void ColorSwatchChooser::LoadSwatches()
{
QFile f(GetSwatchFilename());
if (f.open(QFile::ReadOnly)) {
QDataStream d(&f);
uint version;
d >> version;
if (version == 1) {
int index = 0;
while (index < kBtnCount && !d.atEnd()) {
Color::DataType r;
QString s;
ManagedColor c;
ColorTransform t;
bool is_display;
c.set_alpha(1.0);
d >> r;
c.set_red(r);
d >> r;
c.set_green(r);
d >> r;
c.set_blue(r);
d >> s;
c.set_color_input(s);
d >> is_display;
if (is_display) {
QString display, view, look;
d >> display;
d >> view;
d >> look;
c.set_color_output(ColorTransform(display, view, look));
} else {
d >> s;
c.set_color_output(ColorTransform(s));
}
buttons_[index]->SetColor(c);
index++;
}
}
f.close();
}
}
void ColorSwatchChooser::SaveSwatches()
{
QString fn = GetSwatchFilename();
QFile f(fn);
if (f.open(QFile::WriteOnly)) {
QDataStream d(&f);
const uint version = 1;
d << version;
for (int i=0; i<kBtnCount; i++) {
const ManagedColor &c = buttons_[i]->GetColor();
d << c.red();
d << c.green();
d << c.blue();
d << c.color_input();
d << c.color_output().is_display();
if (c.color_output().is_display()) {
d << c.color_output().display();
d << c.color_output().view();
d << c.color_output().look();
} else {
d << c.color_output().output();
}
}
f.close();
} else {
qCritical() << "Failed to open swatch file" << fn << "for writing";
}
}
}
@@ -0,0 +1,73 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 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/>.
***/
#ifndef COLORSWATCHCHOOSER_H
#define COLORSWATCHCHOOSER_H
#include "node/color/colormanager/colormanager.h"
#include "widget/colorbutton/colorbutton.h"
namespace olive {
class ColorSwatchChooser : public QWidget
{
Q_OBJECT
public:
ColorSwatchChooser(ColorManager *manager, QWidget *parent = nullptr);
public slots:
void SetCurrentColor(const ManagedColor &c)
{
current_ = c;
}
signals:
void ColorClicked(const ManagedColor &c);
private:
void SetDefaultColor(int index);
static QString GetSwatchFilename();
void LoadSwatches();
void SaveSwatches();
static const int kRowCount = 4;
static const int kColCount = 8;
static const int kBtnCount = kRowCount*kColCount;
ColorButton *buttons_[kBtnCount];
ManagedColor current_;
ColorButton *menu_btn_;
private slots:
void HandleButtonClick();
void HandleContextMenu();
void SaveCurrentColor();
void ResetMenuButton();
};
}
#endif // COLORSWATCHCHOOSER_H
+72 -9
View File
@@ -270,14 +270,16 @@ ColorValuesTab::ColorValuesTab(bool with_legacy_option, QWidget *parent) :
row++;
hex_lbl_ = new QLabel(tr("Hex"));
hex_lbl_ = new QLabel(tr("Web"));
layout->addWidget(hex_lbl_, row, 0);
hex_slider_ = new StringSlider();
connect(hex_slider_, &StringSlider::ValueChanged, this, &ColorValuesTab::HexChanged);
layout->addWidget(hex_slider_, row, 1);
LegacyChanged(AreSlidersLegacyValues());
if (legacy_box_) {
LegacyChanged(AreSlidersLegacyValues());
}
}
Color ColorValuesTab::GetColor() const
@@ -371,11 +373,20 @@ void ColorValuesTab::LegacyChanged(bool legacy)
s->SetDragMultiplier(drag_multiplier);
}
hex_lbl_->setVisible(legacy);
hex_slider_->setVisible(legacy);
UpdateHex();
}
QString RGBValToString(double d)
{
QString s = QString::number(d);
if (!s.contains('.')) {
s.append(QStringLiteral(".0"));
}
return s;
}
void ColorValuesTab::UpdateHex()
{
if (AreSlidersLegacyValues()) {
@@ -390,9 +401,39 @@ void ColorValuesTab::UpdateHex()
hex_slider_->SetValue(QStringLiteral("%1").arg(rgb, 6, 16, QLatin1Char('0')).toUpper());
}
} else {
hex_slider_->SetValue(QStringLiteral("rgb(%1, %2, %3)").arg(RGBValToString(red_slider_->GetValue()), RGBValToString(green_slider_->GetValue()), RGBValToString(blue_slider_->GetValue())));
}
}
bool ParseRGBString(QString s, double *r, double *g, double *b)
{
// Trim whitespace
s = s.trimmed();
s.remove(QStringLiteral("rgba"), Qt::CaseInsensitive);
s.remove(QStringLiteral("rgb"), Qt::CaseInsensitive);
s.remove('(');
s.remove(')');
QStringList vals = s.split(',');
if (vals.size() < 3) {
return false;
}
bool ok;
*r = vals.at(0).toDouble(&ok);
if (!ok) return false;
*g = vals.at(1).toDouble(&ok);
if (!ok) return false;
*b = vals.at(2).toDouble(&ok);
if (!ok) return false;
return true;
}
void ColorValuesTab::HexChanged(const QString &s)
{
bool ok;
@@ -403,15 +444,37 @@ void ColorValuesTab::HexChanged(const QString &s)
uint32_t g = (hex & 0x00FF00) >> 8;
uint32_t b = (hex & 0x0000FF);
red_slider_->SetValue(r);
green_slider_->SetValue(g);
blue_slider_->SetValue(b);
if (AreSlidersLegacyValues()) {
red_slider_->SetValue(r);
green_slider_->SetValue(g);
blue_slider_->SetValue(b);
} else {
red_slider_->SetValue(double(r)/kLegacyMultiplier);
green_slider_->SetValue(double(g)/kLegacyMultiplier);
blue_slider_->SetValue(double(b)/kLegacyMultiplier);
}
emit ColorChanged(GetColor());
} else {
// Return to original value
UpdateHex();
// Attempt to parse rgb/rgba
double r, g, b;
if (ParseRGBString(s, &r, &g, &b)) {
if (AreSlidersLegacyValues()) {
red_slider_->SetValue(r*kLegacyMultiplier);
green_slider_->SetValue(g*kLegacyMultiplier);
blue_slider_->SetValue(b*kLegacyMultiplier);
} else {
red_slider_->SetValue(r);
green_slider_->SetValue(g);
blue_slider_->SetValue(b);
}
emit ColorChanged(GetColor());
}
}
// Conform string to our formatting
UpdateHex();
}
bool ColorValuesTab::AreSlidersLegacyValues() const
+199 -198
View File
@@ -439,8 +439,6 @@ TimelineViewMouseEvent TimelineView::CreateMouseEvent(const QPoint& pos, Qt::Mou
void TimelineView::DrawBlocks(QPainter *painter, bool foreground)
{
rational start_time = SceneToTime(GetTimelineLeftBound());
rational end_time = SceneToTime(GetTimelineRightBound());
@@ -478,219 +476,222 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q
block_right - block_left,
block_height);
QColor shadow_color = block->color().toQColor().darker();
QColor shadow_color = block->is_enabled() ? block->color().toQColor().darker() : QColor(Qt::darkGray).darker();
QFontMetrics fm = fontMetrics();
int text_height = fm.height();
int text_padding = text_height/4; // This ties into the track minimum height being 1.5
int text_total_height = text_height + text_padding + text_padding;
if (foreground) {
painter->setBrush(Qt::NoBrush);
QString using_label = block->GetLabelOrName();
QRectF text_rect = r.adjusted(text_padding, text_padding, -text_padding, -text_padding);
painter->setPen(block->is_enabled() ? ColorCoding::GetUISelectorColor(block->color()) : Qt::lightGray);
painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignTop, using_label);
if (block->HasLinks()) {
int text_width = qMin(qRound(text_rect.width()),
QtUtils::QFontMetricsWidth(fm, using_label));
int underline_y = text_rect.y() + text_height;
painter->drawLine(text_rect.x(), underline_y, text_width + text_rect.x(), underline_y);
}
qreal line_bottom = block_top+block_height-1;
painter->setPen(Qt::white);
painter->drawLine(block_left, block_top, block_right, block_top);
painter->drawLine(block_left, block_top, block_left, line_bottom);
painter->setPen(shadow_color);
painter->drawLine(block_left, line_bottom, block_right, line_bottom);
painter->drawLine(block_right, line_bottom, block_right, block_top);
if (r.width() <= 3) {
painter->fillRect(r, shadow_color);
} else {
painter->setPen(Qt::NoPen);
painter->setBrush(block->is_enabled() ? block->brush(block_top, block_top + block_height) : Qt::gray);
painter->drawRect(r);
QFontMetrics fm = fontMetrics();
int text_height = fm.height();
int text_padding = text_height/4; // This ties into the track minimum height being 1.5
int text_total_height = text_height + text_padding + text_padding;
if (ClipBlock *clip = dynamic_cast<ClipBlock*>(block)) {
QRect preview_rect = r.toRect();
if (foreground) {
painter->setBrush(Qt::NoBrush);
// Draw clip thumbnails
if (clip->GetTrackType() == Track::kVideo
&& OLIVE_CONFIG("TimelineThumbnailMode").toInt() != Timeline::kThumbnailOff
&& preview_rect.height() > r.height()/3) {
if (const FrameHashCache *thumbs = clip->thumbnails()) {
// Start thumbnails underneath clip name
preview_rect.adjust(0, text_total_height, 0, 0);
QString using_label = block->GetLabelOrName();
QRect thumb_rect;
painter->setRenderHint(QPainter::SmoothPixmapTransform);
painter->setClipRect(preview_rect);
QRectF text_rect = r.adjusted(text_padding, text_padding, -text_padding, -text_padding);
painter->setPen(block->is_enabled() ? ColorCoding::GetUISelectorColor(block->color()) : Qt::lightGray);
painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignTop, using_label);
if (OLIVE_CONFIG("TimelineThumbnailMode") == Timeline::kThumbnailOn) {
if (block->HasLinks()) {
int text_width = qMin(qRound(text_rect.width()),
QtUtils::QFontMetricsWidth(fm, using_label));
Sequence *s = clip->track()->sequence();
int width = s->GetVideoParams().width();
int height = s->GetVideoParams().height();
int start;
if (height > 0) { // Prevent divide by zero/invalid params
double scale = double(preview_rect.height())/double(height);
thumb_rect.setWidth(width * scale);
start = (((preview_rect.left() - int(qFloor(block_in))) / thumb_rect.width()) * thumb_rect.width()) + qFloor(block_in);
} else {
start = preview_rect.left();
}
int underline_y = text_rect.y() + text_height;
for (int i=start; i<preview_rect.right(); i+=thumb_rect.width()+1) {
rational time_here = SceneToTime(i - block_in, GetScale(), connected_track_list_->parent()->GetVideoParams().frame_rate_as_time_base()) + media_in;
DrawThumbnail(painter, thumbs, time_here, i, preview_rect, &thumb_rect);
}
} else {
rational time = clip->media_range().in();
time = Timecode::snap_time_to_timebase(time, thumbs->GetTimebase(), Timecode::kFloor);
DrawThumbnail(painter, thumbs, time, block_left, preview_rect, &thumb_rect);
}
painter->setClipping(false);
}
painter->drawLine(text_rect.x(), underline_y, text_width + text_rect.x(), underline_y);
}
// Draw waveform
if (clip->GetTrackType() == Track::kAudio
&& OLIVE_CONFIG("TimelineWaveformMode").toInt() == Timeline::kWaveformsEnabled) {
if (const AudioWaveformCache *wave = clip->waveform()) {
rational waveform_start = SceneToTime(block_left - block_in, GetScale(), connected_track_list_->parent()->GetAudioParams().sample_rate_as_time_base()) + media_in;
painter->setPen(shadow_color);
qreal line_bottom = block_top+block_height-1;
wave->Draw(painter, preview_rect, this->GetScale(), waveform_start);
}
}
// Draw zebra stripes and markers
if (clip->connected_viewer()) {
if (!clip->connected_viewer()->GetLength().isNull()) {
painter->setPen(shadow_color);
if (clip->media_in() < 0) {
qreal zebra_right = TimeToScene(clip->in() - clip->media_in());
switch (clip->loop_mode()) {
case Decoder::kLoopModeOff:
// Draw stripes for sections of clip < 0
if (zebra_right > GetTimelineLeftBound()) {
DrawZebraStripes(painter, QRectF(block_left, block_top, zebra_right - block_left, block_height));
}
break;
case Decoder::kLoopModeLoop:
for (qreal i=zebra_right; i>block_left; i-=TimeToScene(clip->connected_viewer()->GetLength())) {
painter->drawLine(i, block_top, i, block_top + block_height);
}
break;
case Decoder::kLoopModeClamp:
painter->drawLine(zebra_right, block_top, zebra_right, block_top + block_height);
break;
}
}
if (clip->length() + clip->media_in() > clip->connected_viewer()->GetLength()) {
qreal zebra_left = TimeToScene(clip->out() - (clip->media_in() + clip->length() - clip->connected_viewer()->GetLength()));
switch (clip->loop_mode()) {
case Decoder::kLoopModeOff:
// Draw stripes for sections for clip > clip length
if (zebra_left < GetTimelineRightBound()) {
DrawZebraStripes(painter, QRectF(zebra_left, block_top, block_right - zebra_left, block_height));
}
break;
case Decoder::kLoopModeLoop:
for (qreal i=zebra_left; i<block_right; i+=TimeToScene(clip->connected_viewer()->GetLength())) {
painter->drawLine(i, block_top, i, block_top + block_height);
}
break;
case Decoder::kLoopModeClamp:
painter->drawLine(zebra_left, block_top, zebra_left, block_top + block_height);
break;
}
}
}
TimelineMarkerList *marker_list = clip->connected_viewer()->GetMarkers();
if (!marker_list->empty()) {
clip_marker_rects_.clear();
for (auto it=marker_list->cbegin(); it!=marker_list->cend(); it++) {
TimelineMarker *marker = *it;
// Make sure marker is within In/Out points of the clip
if (marker->time().in() >= clip->media_in() && marker->time().out() <= clip->media_in() + clip->length()) {
QPoint marker_pt(TimeToScene(clip->in() - clip->media_in() + marker->time().in()), block_top + block_height);
painter->setClipRect(r);
QRect marker_rect = marker->Draw(painter, marker_pt, -1, GetScale(), false);
clip_marker_rects_.insert(marker, marker_rect);
painter->setClipping(false);
}
}
}
}
if (const FrameHashCache *cache = clip->connected_video_cache()) {
if (cache->HasValidatedRanges()) {
QRect cache_rect = r.adjusted(0, r.height() - PlaybackCache::GetCacheIndicatorHeight(), 0, 0).toRect();
cache->Draw(painter, clip->media_in(), GetScale(), cache_rect);
}
}
}
// For transitions, show lines representing a transition
if (TransitionBlock* transition = dynamic_cast<TransitionBlock*>(block)) {
QVector<QLineF> lines;
if (transition->connected_in_block()) {
lines.append(QLineF(r.bottomLeft(), r.topRight()));
}
if (transition->connected_out_block()) {
lines.append(QLineF(r.topLeft(), r.bottomRight()));
}
painter->setPen(Qt::white);
painter->drawLine(block_left, block_top, block_right, block_top);
painter->drawLine(block_left, block_top, block_left, line_bottom);
painter->setPen(shadow_color);
painter->drawLines(lines);
}
if (transition_overlay_out_ == block || transition_overlay_in_ == block) {
QRectF transition_overlay_rect = r;
qreal transition_overlay_width = TimeToScene(block->length()) * 0.5;
if (transition_overlay_out_ && transition_overlay_in_) {
// This is a dual transition, use the smallest width
Block *other_block = (transition_overlay_out_ == block) ? transition_overlay_in_ : transition_overlay_out_;
qreal other_width = TimeToScene(other_block->length()) * 0.5;
transition_overlay_width = qMin(transition_overlay_width, other_width);
}
if (transition_overlay_out_ == block) {
transition_overlay_rect.setLeft(transition_overlay_rect.right() - transition_overlay_width);
} else {
transition_overlay_rect.setRight(transition_overlay_rect.left() + transition_overlay_width);
}
painter->drawLine(block_left, line_bottom, block_right, line_bottom);
painter->drawLine(block_right, line_bottom, block_right, block_top);
} else {
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(0, 0, 0, 64));
painter->setBrush(block->is_enabled() ? block->brush(block_top, block_top + block_height) : Qt::gray);
painter->drawRect(r);
painter->drawRect(transition_overlay_rect);
if (ClipBlock *clip = dynamic_cast<ClipBlock*>(block)) {
QRect preview_rect = r.toRect();
// Draw clip thumbnails
if (clip->GetTrackType() == Track::kVideo
&& OLIVE_CONFIG("TimelineThumbnailMode").toInt() != Timeline::kThumbnailOff
&& preview_rect.height() > r.height()/3) {
if (const FrameHashCache *thumbs = clip->thumbnails()) {
// Start thumbnails underneath clip name
preview_rect.adjust(0, text_total_height, 0, 0);
QRect thumb_rect;
painter->setRenderHint(QPainter::SmoothPixmapTransform);
painter->setClipRect(preview_rect);
if (OLIVE_CONFIG("TimelineThumbnailMode") == Timeline::kThumbnailOn) {
Sequence *s = clip->track()->sequence();
int width = s->GetVideoParams().width();
int height = s->GetVideoParams().height();
int start;
if (height > 0) { // Prevent divide by zero/invalid params
double scale = double(preview_rect.height())/double(height);
thumb_rect.setWidth(width * scale);
start = (((preview_rect.left() - int(qFloor(block_in))) / thumb_rect.width()) * thumb_rect.width()) + qFloor(block_in);
} else {
start = preview_rect.left();
}
for (int i=start; i<preview_rect.right(); i+=thumb_rect.width()+1) {
rational time_here = SceneToTime(i - block_in, GetScale(), connected_track_list_->parent()->GetVideoParams().frame_rate_as_time_base()) + media_in;
DrawThumbnail(painter, thumbs, time_here, i, preview_rect, &thumb_rect);
}
} else {
rational time = clip->media_range().in();
time = Timecode::snap_time_to_timebase(time, thumbs->GetTimebase(), Timecode::kFloor);
DrawThumbnail(painter, thumbs, time, block_left, preview_rect, &thumb_rect);
}
painter->setClipping(false);
}
}
// Draw waveform
if (clip->GetTrackType() == Track::kAudio
&& OLIVE_CONFIG("TimelineWaveformMode").toInt() == Timeline::kWaveformsEnabled) {
if (const AudioWaveformCache *wave = clip->waveform()) {
rational waveform_start = SceneToTime(block_left - block_in, GetScale(), connected_track_list_->parent()->GetAudioParams().sample_rate_as_time_base()) + media_in;
painter->setPen(shadow_color);
wave->Draw(painter, preview_rect, this->GetScale(), waveform_start);
}
}
// Draw zebra stripes and markers
if (clip->connected_viewer()) {
if (!clip->connected_viewer()->GetLength().isNull()) {
painter->setPen(shadow_color);
if (clip->media_in() < 0) {
qreal zebra_right = TimeToScene(clip->in() - clip->media_in());
switch (clip->loop_mode()) {
case Decoder::kLoopModeOff:
// Draw stripes for sections of clip < 0
if (zebra_right > GetTimelineLeftBound()) {
DrawZebraStripes(painter, QRectF(block_left, block_top, zebra_right - block_left, block_height));
}
break;
case Decoder::kLoopModeLoop:
for (qreal i=zebra_right; i>block_left; i-=TimeToScene(clip->connected_viewer()->GetLength())) {
painter->drawLine(i, block_top, i, block_top + block_height);
}
break;
case Decoder::kLoopModeClamp:
painter->drawLine(zebra_right, block_top, zebra_right, block_top + block_height);
break;
}
}
if (clip->length() + clip->media_in() > clip->connected_viewer()->GetLength()) {
qreal zebra_left = TimeToScene(clip->out() - (clip->media_in() + clip->length() - clip->connected_viewer()->GetLength()));
switch (clip->loop_mode()) {
case Decoder::kLoopModeOff:
// Draw stripes for sections for clip > clip length
if (zebra_left < GetTimelineRightBound()) {
DrawZebraStripes(painter, QRectF(zebra_left, block_top, block_right - zebra_left, block_height));
}
break;
case Decoder::kLoopModeLoop:
for (qreal i=zebra_left; i<block_right; i+=TimeToScene(clip->connected_viewer()->GetLength())) {
painter->drawLine(i, block_top, i, block_top + block_height);
}
break;
case Decoder::kLoopModeClamp:
painter->drawLine(zebra_left, block_top, zebra_left, block_top + block_height);
break;
}
}
}
TimelineMarkerList *marker_list = clip->connected_viewer()->GetMarkers();
if (!marker_list->empty()) {
clip_marker_rects_.clear();
for (auto it=marker_list->cbegin(); it!=marker_list->cend(); it++) {
TimelineMarker *marker = *it;
// Make sure marker is within In/Out points of the clip
if (marker->time().in() >= clip->media_in() && marker->time().out() <= clip->media_in() + clip->length()) {
QPoint marker_pt(TimeToScene(clip->in() - clip->media_in() + marker->time().in()), block_top + block_height);
painter->setClipRect(r);
QRect marker_rect = marker->Draw(painter, marker_pt, -1, GetScale(), false);
clip_marker_rects_.insert(marker, marker_rect);
painter->setClipping(false);
}
}
}
}
if (const FrameHashCache *cache = clip->connected_video_cache()) {
if (cache->HasValidatedRanges()) {
QRect cache_rect = r.adjusted(0, r.height() - PlaybackCache::GetCacheIndicatorHeight(), 0, 0).toRect();
cache->Draw(painter, clip->media_in(), GetScale(), cache_rect);
}
}
}
// For transitions, show lines representing a transition
if (TransitionBlock* transition = dynamic_cast<TransitionBlock*>(block)) {
QVector<QLineF> lines;
if (transition->connected_in_block()) {
lines.append(QLineF(r.bottomLeft(), r.topRight()));
}
if (transition->connected_out_block()) {
lines.append(QLineF(r.topLeft(), r.bottomRight()));
}
painter->setPen(shadow_color);
painter->drawLines(lines);
}
if (transition_overlay_out_ == block || transition_overlay_in_ == block) {
QRectF transition_overlay_rect = r;
qreal transition_overlay_width = TimeToScene(block->length()) * 0.5;
if (transition_overlay_out_ && transition_overlay_in_) {
// This is a dual transition, use the smallest width
Block *other_block = (transition_overlay_out_ == block) ? transition_overlay_in_ : transition_overlay_out_;
qreal other_width = TimeToScene(other_block->length()) * 0.5;
transition_overlay_width = qMin(transition_overlay_width, other_width);
}
if (transition_overlay_out_ == block) {
transition_overlay_rect.setLeft(transition_overlay_rect.right() - transition_overlay_width);
} else {
transition_overlay_rect.setRight(transition_overlay_rect.left() + transition_overlay_width);
}
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(0, 0, 0, 64));
painter->drawRect(transition_overlay_rect);
}
}
}
}
}
@@ -119,7 +119,7 @@ private:
void DrawBlock(QPainter *painter, bool foreground, Block *block, qreal top, qreal height)
{
ClipBlock *cb = dynamic_cast<ClipBlock*>(block);
DrawBlock(painter, foreground, block, top, height, block->in(), block->out(), cb ? cb->media_in() : 0);
return DrawBlock(painter, foreground, block, top, height, block->in(), block->out(), cb ? cb->media_in() : 0);
}
void DrawZebraStripes(QPainter *painter, const QRectF &r);