Merge branch 'master' into multicam
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
#include "common/timerange.h"
|
||||
#include "node/block/subtitle/subtitle.h"
|
||||
#include "render/audioparams.h"
|
||||
#include "render/colortransform.h"
|
||||
#include "render/subtitleparams.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
|
||||
@@ -116,9 +116,6 @@ void Core::DeclareTypesForQt()
|
||||
qRegisterMetaType<olive::TimeRange>();
|
||||
qRegisterMetaType<Color>();
|
||||
qRegisterMetaType<olive::AudioVisualWaveform>();
|
||||
qRegisterMetaType<olive::SampleJob>();
|
||||
qRegisterMetaType<olive::ShaderJob>();
|
||||
qRegisterMetaType<olive::GenerateJob>();
|
||||
qRegisterMetaType<olive::VideoParams>();
|
||||
qRegisterMetaType<olive::VideoParams::Interlacing>();
|
||||
qRegisterMetaType<olive::MainWindowLayoutInfo>();
|
||||
|
||||
@@ -72,6 +72,8 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
|
||||
label->setWordWrap(true);
|
||||
label->setOpenExternalLinks(true);
|
||||
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
|
||||
label->setCursor(Qt::IBeamCursor);
|
||||
horiz_layout->addWidget(label);
|
||||
|
||||
layout->addLayout(horiz_layout);
|
||||
|
||||
@@ -40,9 +40,13 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
|
||||
splitter->setChildrenCollapsible(false);
|
||||
layout->addWidget(splitter);
|
||||
|
||||
QWidget* wheel_area = new QWidget();
|
||||
QHBoxLayout* wheel_layout = new QHBoxLayout(wheel_area);
|
||||
splitter->addWidget(wheel_area);
|
||||
QWidget* graphics_area = new QWidget();
|
||||
splitter->addWidget(graphics_area);
|
||||
|
||||
QVBoxLayout *graphics_layout = new QVBoxLayout(graphics_area);
|
||||
|
||||
QHBoxLayout* wheel_layout = new QHBoxLayout();
|
||||
graphics_layout->addLayout(wheel_layout);
|
||||
|
||||
color_wheel_ = new ColorWheelWidget();
|
||||
wheel_layout->addWidget(color_wheel_);
|
||||
@@ -51,6 +55,17 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
|
||||
hsv_value_gradient_->setFixedWidth(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral("HHH")));
|
||||
wheel_layout->addWidget(hsv_value_gradient_);
|
||||
|
||||
QHBoxLayout *swatch_layout = new QHBoxLayout();
|
||||
graphics_layout->addLayout(swatch_layout);
|
||||
|
||||
swatch_layout->addStretch();
|
||||
|
||||
swatch_ = new ColorSwatchChooser(color_manager_);
|
||||
swatch_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
swatch_layout->addWidget(swatch_);
|
||||
|
||||
swatch_layout->addStretch();
|
||||
|
||||
QWidget* value_area = new QWidget();
|
||||
QVBoxLayout* value_layout = new QVBoxLayout(value_area);
|
||||
value_layout->setSpacing(0);
|
||||
@@ -61,8 +76,6 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
|
||||
value_layout->addWidget(color_values_widget_);
|
||||
|
||||
chooser_ = new ColorSpaceChooser(color_manager_);
|
||||
chooser_->set_input(start.color_input());
|
||||
chooser_->set_output(start.color_output());
|
||||
|
||||
value_layout->addWidget(chooser_);
|
||||
|
||||
@@ -71,10 +84,16 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
|
||||
|
||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
|
||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||
connect(color_wheel_, &ColorWheelWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_values_widget_, &ColorValuesWidget::SetColor);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||
connect(hsv_value_gradient_, &ColorGradientWidget::SelectedColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||
connect(color_values_widget_, &ColorValuesWidget::ColorChanged, swatch_, &ColorSwatchChooser::SetCurrentColor);
|
||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, hsv_value_gradient_, &ColorGradientWidget::SetSelectedColor);
|
||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_wheel_, &ColorWheelWidget::SetSelectedColor);
|
||||
connect(swatch_, &ColorSwatchChooser::ColorClicked, color_values_widget_, &ColorValuesWidget::SetColor);
|
||||
|
||||
connect(color_wheel_, &ColorWheelWidget::DiameterChanged, hsv_value_gradient_, &ColorGradientWidget::setFixedHeight);
|
||||
|
||||
@@ -83,6 +102,20 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
SetColor(start);
|
||||
|
||||
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this, &ColorDialog::ColorSpaceChanged);
|
||||
ColorSpaceChanged(chooser_->input(), chooser_->output());
|
||||
|
||||
// Set default size ratio to 2:1
|
||||
resize(sizeHint().height() * 2, sizeHint().height());
|
||||
}
|
||||
|
||||
void ColorDialog::SetColor(const ManagedColor &start)
|
||||
{
|
||||
chooser_->set_input(start.color_input());
|
||||
chooser_->set_output(start.color_output());
|
||||
|
||||
Color managed_start;
|
||||
|
||||
if (start.color_input().isEmpty()) {
|
||||
@@ -103,12 +136,7 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start,
|
||||
color_wheel_->SetSelectedColor(managed_start);
|
||||
hsv_value_gradient_->SetSelectedColor(managed_start);
|
||||
color_values_widget_->SetColor(managed_start);
|
||||
|
||||
connect(chooser_, &ColorSpaceChooser::ColorSpaceChanged, this, &ColorDialog::ColorSpaceChanged);
|
||||
ColorSpaceChanged(chooser_->input(), chooser_->output());
|
||||
|
||||
// Set default size ratio to 2:1
|
||||
resize(sizeHint().height() * 2, sizeHint().height());
|
||||
swatch_->SetCurrentColor(managed_start);
|
||||
}
|
||||
|
||||
ManagedColor ColorDialog::GetSelectedColor() const
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "render/managedcolor.h"
|
||||
#include "widget/colorwheel/colorgradientwidget.h"
|
||||
#include "widget/colorwheel/colorspacechooser.h"
|
||||
#include "widget/colorwheel/colorswatchchooser.h"
|
||||
#include "widget/colorwheel/colorvalueswidget.h"
|
||||
#include "widget/colorwheel/colorwheelwidget.h"
|
||||
|
||||
@@ -69,6 +70,9 @@ public:
|
||||
|
||||
ColorTransform GetColorSpaceOutput() const;
|
||||
|
||||
public slots:
|
||||
void SetColor(const ManagedColor &c);
|
||||
|
||||
private:
|
||||
ColorManager* color_manager_;
|
||||
|
||||
@@ -82,6 +86,8 @@ private:
|
||||
|
||||
ColorSpaceChooser* chooser_;
|
||||
|
||||
ColorSwatchChooser *swatch_;
|
||||
|
||||
private slots:
|
||||
void ColorSpaceChanged(const QString& input, const ColorTransform &output);
|
||||
|
||||
|
||||
@@ -20,75 +20,120 @@
|
||||
|
||||
#include "exportformatcombobox.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "ui/icons/icons.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
ExportFormatComboBox::ExportFormatComboBox(Mode mode, QWidget *parent) :
|
||||
QComboBox(parent)
|
||||
{
|
||||
custom_menu_ = new Menu(this);
|
||||
|
||||
// Populate combobox formats
|
||||
for (int i=0; i<ExportFormat::kFormatCount; i++) {
|
||||
ExportFormat::Format f = static_cast<ExportFormat::Format>(i);
|
||||
switch (mode) {
|
||||
case kShowAllFormats:
|
||||
custom_menu_->addAction(CreateHeader(icon::Video, tr("Video")));
|
||||
PopulateType(Track::kVideo);
|
||||
custom_menu_->addSeparator();
|
||||
|
||||
switch (mode) {
|
||||
case kShowAllFormats:
|
||||
break;
|
||||
case kShowAudioOnly:
|
||||
if (!ExportFormat::GetVideoCodecs(f).isEmpty()
|
||||
|| !ExportFormat::GetSubtitleCodecs(f).isEmpty()
|
||||
|| ExportFormat::GetAudioCodecs(f).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case kShowVideoOnly:
|
||||
if (ExportFormat::GetVideoCodecs(f).isEmpty()
|
||||
|| !ExportFormat::GetSubtitleCodecs(f).isEmpty()
|
||||
|| !ExportFormat::GetAudioCodecs(f).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case kShowSubtitlesOnly:
|
||||
if (!ExportFormat::GetVideoCodecs(f).isEmpty()
|
||||
|| ExportFormat::GetSubtitleCodecs(f).isEmpty()
|
||||
|| !ExportFormat::GetAudioCodecs(f).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
custom_menu_->addAction(CreateHeader(icon::Audio, tr("Audio")));
|
||||
PopulateType(Track::kAudio);
|
||||
custom_menu_->addSeparator();
|
||||
|
||||
QString format_name = ExportFormat::GetName(f);
|
||||
|
||||
bool inserted = false;
|
||||
|
||||
// Sort formats alphabetically
|
||||
for (int j=0; j<count(); j++) {
|
||||
if (itemText(j) > format_name) {
|
||||
insertItem(j, format_name, i);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inserted) {
|
||||
addItem(format_name, i);
|
||||
}
|
||||
custom_menu_->addAction(CreateHeader(icon::Subtitles, tr("Subtitle")));
|
||||
PopulateType(Track::kSubtitle);
|
||||
break;
|
||||
case kShowAudioOnly:
|
||||
PopulateType(Track::kAudio);
|
||||
break;
|
||||
case kShowVideoOnly:
|
||||
PopulateType(Track::kVideo);
|
||||
break;
|
||||
case kShowSubtitlesOnly:
|
||||
PopulateType(Track::kSubtitle);
|
||||
break;
|
||||
}
|
||||
|
||||
connect(this, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ExportFormatComboBox::HandleIndexChange);
|
||||
connect(custom_menu_, &Menu::triggered, this, &ExportFormatComboBox::HandleIndexChange);
|
||||
}
|
||||
|
||||
void ExportFormatComboBox::showPopup()
|
||||
{
|
||||
custom_menu_->setMinimumWidth(this->width());
|
||||
custom_menu_->exec(mapToGlobal(QPoint(0, 0)));
|
||||
}
|
||||
|
||||
void ExportFormatComboBox::SetFormat(ExportFormat::Format fmt)
|
||||
{
|
||||
for (int i=0; i<count(); i++) {
|
||||
if (itemData(i).toInt() == fmt) {
|
||||
setCurrentIndex(i);
|
||||
break;
|
||||
current_ = fmt;
|
||||
clear();
|
||||
addItem(ExportFormat::GetName(current_));
|
||||
}
|
||||
|
||||
void ExportFormatComboBox::HandleIndexChange(QAction *a)
|
||||
{
|
||||
ExportFormat::Format f = static_cast<ExportFormat::Format>(a->data().toInt());
|
||||
SetFormat(f);
|
||||
emit FormatChanged(f);
|
||||
}
|
||||
|
||||
void ExportFormatComboBox::PopulateType(Track::Type type)
|
||||
{
|
||||
for (int i=0; i<ExportFormat::kFormatCount; i++) {
|
||||
ExportFormat::Format f = static_cast<ExportFormat::Format>(i);
|
||||
|
||||
if (type == Track::kVideo
|
||||
&& !ExportFormat::GetVideoCodecs(f).isEmpty()) {
|
||||
// Do nothing
|
||||
} else if (type == Track::kAudio
|
||||
&& ExportFormat::GetVideoCodecs(f).isEmpty()
|
||||
&& !ExportFormat::GetAudioCodecs(f).isEmpty()) {
|
||||
// Do nothing
|
||||
} else if (type == Track::kSubtitle
|
||||
&& ExportFormat::GetVideoCodecs(f).isEmpty()
|
||||
&& ExportFormat::GetAudioCodecs(f).isEmpty()
|
||||
&& !ExportFormat::GetSubtitleCodecs(f).isEmpty()) {
|
||||
// Do nothing
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString format_name = ExportFormat::GetName(f);
|
||||
|
||||
QAction *a = custom_menu_->addAction(format_name);
|
||||
a->setData(i);
|
||||
a->setIconVisibleInMenu(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ExportFormatComboBox::HandleIndexChange(int index)
|
||||
QWidgetAction *ExportFormatComboBox::CreateHeader(const QIcon &icon, const QString &title)
|
||||
{
|
||||
emit FormatChanged(static_cast<ExportFormat::Format>(itemData(index).toInt()));
|
||||
QWidgetAction *a = new QWidgetAction(this);
|
||||
|
||||
QWidget *w = new QWidget();
|
||||
QHBoxLayout *layout = new QHBoxLayout(w);
|
||||
|
||||
QLabel *icon_lbl = new QLabel();
|
||||
|
||||
QLabel *text_lbl = new QLabel(title);
|
||||
text_lbl->setAlignment(Qt::AlignCenter);
|
||||
QFont f = text_lbl->font();
|
||||
f.setWeight(QFont::Bold);
|
||||
text_lbl->setFont(f);
|
||||
|
||||
icon_lbl->setPixmap(icon.pixmap(text_lbl->sizeHint()));
|
||||
|
||||
layout->addStretch();
|
||||
layout->addWidget(icon_lbl);
|
||||
layout->addWidget(text_lbl);
|
||||
layout->addStretch();
|
||||
|
||||
a->setDefaultWidget(w);
|
||||
a->setEnabled(false);
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,8 +22,11 @@
|
||||
#define EXPORTFORMATCOMBOBOX_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QWidgetAction>
|
||||
|
||||
#include "codec/exportformat.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "widget/menu/menu.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -45,9 +48,11 @@ public:
|
||||
|
||||
ExportFormat::Format GetFormat() const
|
||||
{
|
||||
return static_cast<ExportFormat::Format>(currentData().toInt());
|
||||
return current_;
|
||||
}
|
||||
|
||||
void showPopup();
|
||||
|
||||
signals:
|
||||
void FormatChanged(ExportFormat::Format fmt);
|
||||
|
||||
@@ -55,7 +60,16 @@ public slots:
|
||||
void SetFormat(ExportFormat::Format fmt);
|
||||
|
||||
private slots:
|
||||
void HandleIndexChange(int index);
|
||||
void HandleIndexChange(QAction *a);
|
||||
|
||||
private:
|
||||
void PopulateType(Track::Type type);
|
||||
|
||||
QWidgetAction *CreateHeader(const QIcon &icon, const QString &title);
|
||||
|
||||
Menu *custom_menu_;
|
||||
|
||||
ExportFormat::Format current_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(const std::vector<TimelineMarker
|
||||
layout->addWidget(buttons, row, 0, 1, 2);
|
||||
|
||||
setWindowTitle(tr("Edit Markers"));
|
||||
label_edit_->setFocus();
|
||||
}
|
||||
|
||||
void MarkerPropertiesDialog::accept()
|
||||
|
||||
@@ -64,8 +64,6 @@ QString PanNode::Description() const
|
||||
|
||||
void PanNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
Q_UNUSED(globals)
|
||||
|
||||
// Create a sample job
|
||||
SampleBuffer samples = value[kSamplesInput].toSamples();
|
||||
if (samples.is_allocated()) {
|
||||
@@ -85,7 +83,7 @@ void PanNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
|
||||
table->Push(NodeValue(NodeValue::kSamples, samples, this));
|
||||
} else {
|
||||
// Requires job
|
||||
table->Push(NodeValue::kSamples, SampleJob(kSamplesInput, value), this);
|
||||
table->Push(NodeValue::kSamples, SampleJob(globals.time(), kSamplesInput, value), this);
|
||||
}
|
||||
} else {
|
||||
// Pass right through
|
||||
|
||||
@@ -63,8 +63,6 @@ QString VolumeNode::Description() const
|
||||
|
||||
void VolumeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
Q_UNUSED(globals)
|
||||
|
||||
// Create a sample job
|
||||
SampleBuffer buffer = value[kSamplesInput].toSamples();
|
||||
|
||||
@@ -80,7 +78,7 @@ void VolumeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, No
|
||||
table->Push(NodeValue::kSamples, QVariant::fromValue(buffer), this);
|
||||
} else {
|
||||
// Requires job
|
||||
SampleJob job(kSamplesInput, value);
|
||||
SampleJob job(globals.time(), kSamplesInput, value);
|
||||
job.Insert(kVolumeInput, value);
|
||||
table->Push(NodeValue::kSamples, QVariant::fromValue(job), this);
|
||||
}
|
||||
|
||||
@@ -56,9 +56,9 @@ ShaderCode DipToColorTransition::GetShaderCode(const ShaderRequest &request) con
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/diptoblack.frag"), QString());
|
||||
}
|
||||
|
||||
void DipToColorTransition::ShaderJobEvent(const NodeValueRow &value, ShaderJob &job) const
|
||||
void DipToColorTransition::ShaderJobEvent(const NodeValueRow &value, ShaderJob *job) const
|
||||
{
|
||||
job.Insert(kColorInput, value);
|
||||
job->Insert(kColorInput, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
static const QString kColorInput;
|
||||
|
||||
protected:
|
||||
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob& job) const override;
|
||||
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob *job) const override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -182,10 +182,10 @@ void TransitionBlock::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
double time = globals.time().in().toDouble();
|
||||
InsertTransitionTimes(&job, time);
|
||||
|
||||
ShaderJobEvent(value, job);
|
||||
ShaderJobEvent(value, &job);
|
||||
|
||||
job_type = NodeValue::kTexture;
|
||||
push_job = QVariant::fromValue(job);
|
||||
push_job = QVariant::fromValue(Texture::Job(globals.vparams(), job));
|
||||
} else if (data_type == NodeValue::kSamples) {
|
||||
// This must be an audio transition
|
||||
SampleBuffer from_samples = out_buffer.toSamples();
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
static const QString kCenterInput;
|
||||
|
||||
protected:
|
||||
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob& job) const {}
|
||||
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob *job) const {}
|
||||
|
||||
virtual void SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const {}
|
||||
|
||||
|
||||
@@ -60,13 +60,15 @@ void OCIOBaseNode::RemovedFromGraph()
|
||||
|
||||
void OCIOBaseNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
if (value[kTextureInput].toTexture() && processor_) {
|
||||
auto tex_met = value[kTextureInput];
|
||||
TexturePtr t = tex_met.toTexture();
|
||||
if (t && processor_) {
|
||||
ColorTransformJob job;
|
||||
|
||||
job.SetColorProcessor(processor_);
|
||||
job.SetInputTexture(value[kTextureInput].toTexture());
|
||||
job.SetInputTexture(tex_met);
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, t->toJob(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,50 +155,50 @@ void OCIOGradingTransformLinearNode::GenerateProcessor()
|
||||
|
||||
void OCIOGradingTransformLinearNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
if (value[kTextureInput].toTexture() && processor()) {
|
||||
ColorTransformJob job;
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
if (processor()) {
|
||||
ColorTransformJob job(value);
|
||||
|
||||
job.SetColorProcessor(processor());
|
||||
job.SetInputTexture(value[kTextureInput].toTexture());
|
||||
job.SetColorProcessor(processor());
|
||||
job.SetInputTexture(value[kTextureInput]);
|
||||
|
||||
job.Insert(value);
|
||||
const int MASTER_CHANNEL = 0;
|
||||
const int RED_CHANNEL = 1;
|
||||
const int GREEN_CHANNEL = 2;
|
||||
const int BLUE_CHANNEL = 3;
|
||||
|
||||
const int MASTER_CHANNEL = 0;
|
||||
const int RED_CHANNEL = 1;
|
||||
const int GREEN_CHANNEL = 2;
|
||||
const int BLUE_CHANNEL = 3;
|
||||
// Oddly, OCIO uses RGBMs when setting the GradingPrimary on the CPU, but uses vec3s on the GPU.
|
||||
// Even more oddly, the conversion from RGBM to vec3 does not appear to have a public API.
|
||||
// Therefore, this code has been duplicated from OCIO here:
|
||||
// https://github.com/AcademySoftwareFoundation/OpenColorIO/blob/3abbe5b20521169580fcfe3692aca81859859953/src/OpenColorIO/ops/gradingprimary/GradingPrimary.cpp#L157
|
||||
QVector4D offset = value[kOffsetInput].toVec4();
|
||||
offset[RED_CHANNEL] += offset[MASTER_CHANNEL];
|
||||
offset[GREEN_CHANNEL] += offset[MASTER_CHANNEL];
|
||||
offset[BLUE_CHANNEL] += offset[MASTER_CHANNEL];
|
||||
job.Insert(kOffsetInput, NodeValue(NodeValue::kVec3, QVector3D(offset[RED_CHANNEL], offset[GREEN_CHANNEL], offset[BLUE_CHANNEL])));
|
||||
|
||||
// Oddly, OCIO uses RGBMs when setting the GradingPrimary on the CPU, but uses vec3s on the GPU.
|
||||
// Even more oddly, the conversion from RGBM to vec3 does not appear to have a public API.
|
||||
// Therefore, this code has been duplicated from OCIO here:
|
||||
// https://github.com/AcademySoftwareFoundation/OpenColorIO/blob/3abbe5b20521169580fcfe3692aca81859859953/src/OpenColorIO/ops/gradingprimary/GradingPrimary.cpp#L157
|
||||
QVector4D offset = value[kOffsetInput].toVec4();
|
||||
offset[RED_CHANNEL] += offset[MASTER_CHANNEL];
|
||||
offset[GREEN_CHANNEL] += offset[MASTER_CHANNEL];
|
||||
offset[BLUE_CHANNEL] += offset[MASTER_CHANNEL];
|
||||
job.Insert(kOffsetInput, NodeValue(NodeValue::kVec3, QVector3D(offset[RED_CHANNEL], offset[GREEN_CHANNEL], offset[BLUE_CHANNEL])));
|
||||
QVector4D exposure = value[kExposureInput].toVec4();
|
||||
exposure[RED_CHANNEL] = std::pow(2.0f, exposure[MASTER_CHANNEL] + exposure[RED_CHANNEL]);
|
||||
exposure[GREEN_CHANNEL] = std::pow(2.0f, exposure[MASTER_CHANNEL] + exposure[GREEN_CHANNEL]);
|
||||
exposure[BLUE_CHANNEL] = std::pow(2.0f, exposure[MASTER_CHANNEL] + exposure[BLUE_CHANNEL]);
|
||||
job.Insert(kExposureInput, NodeValue(NodeValue::kVec3, QVector3D(exposure[RED_CHANNEL], exposure[GREEN_CHANNEL], exposure[BLUE_CHANNEL])));
|
||||
|
||||
QVector4D exposure = value[kExposureInput].toVec4();
|
||||
exposure[RED_CHANNEL] = std::pow(2.0f, exposure[MASTER_CHANNEL] + exposure[RED_CHANNEL]);
|
||||
exposure[GREEN_CHANNEL] = std::pow(2.0f, exposure[MASTER_CHANNEL] + exposure[GREEN_CHANNEL]);
|
||||
exposure[BLUE_CHANNEL] = std::pow(2.0f, exposure[MASTER_CHANNEL] + exposure[BLUE_CHANNEL]);
|
||||
job.Insert(kExposureInput, NodeValue(NodeValue::kVec3, QVector3D(exposure[RED_CHANNEL], exposure[GREEN_CHANNEL], exposure[BLUE_CHANNEL])));
|
||||
QVector4D contrast = value[kContrastInput].toVec4();
|
||||
contrast[RED_CHANNEL] *= contrast[MASTER_CHANNEL];
|
||||
contrast[GREEN_CHANNEL] *= contrast[MASTER_CHANNEL];
|
||||
contrast[BLUE_CHANNEL] *= contrast[MASTER_CHANNEL];
|
||||
job.Insert(kContrastInput, NodeValue(NodeValue::kVec3, QVector3D(contrast[RED_CHANNEL], contrast[GREEN_CHANNEL], contrast[BLUE_CHANNEL])));
|
||||
|
||||
QVector4D contrast = value[kContrastInput].toVec4();
|
||||
contrast[RED_CHANNEL] *= contrast[MASTER_CHANNEL];
|
||||
contrast[GREEN_CHANNEL] *= contrast[MASTER_CHANNEL];
|
||||
contrast[BLUE_CHANNEL] *= contrast[MASTER_CHANNEL];
|
||||
job.Insert(kContrastInput, NodeValue(NodeValue::kVec3, QVector3D(contrast[RED_CHANNEL], contrast[GREEN_CHANNEL], contrast[BLUE_CHANNEL])));
|
||||
if (!value[kClampBlackEnableInput].toBool()) {
|
||||
job.Insert(kClampBlackInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampBlack()));
|
||||
}
|
||||
|
||||
if (!value[kClampBlackEnableInput].toBool()) {
|
||||
job.Insert(kClampBlackInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampBlack()));
|
||||
if (!value[kClampWhiteEnableInput].toBool()) {
|
||||
job.Insert(kClampWhiteInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampWhite()));
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
}
|
||||
|
||||
if (!value[kClampWhiteEnableInput].toBool()) {
|
||||
job.Insert(kClampWhiteInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampWhite()));
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,11 @@ add_subdirectory(cornerpin)
|
||||
add_subdirectory(crop)
|
||||
add_subdirectory(flip)
|
||||
add_subdirectory(mask)
|
||||
add_subdirectory(ripple)
|
||||
add_subdirectory(swirl)
|
||||
add_subdirectory(tile)
|
||||
add_subdirectory(transform)
|
||||
add_subdirectory(wave)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
|
||||
@@ -69,40 +69,39 @@ void CornerPinDistortNode::Retranslate()
|
||||
|
||||
void CornerPinDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
job.Insert(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||
|
||||
// Convert slider values to their pixel values and then convert to clip space (-1.0 ... 1.0) for overriding the
|
||||
// vertex coordinates.
|
||||
const QVector2D &resolution = globals.resolution();
|
||||
QVector2D half_resolution = resolution * 0.5;
|
||||
QVector2D top_left = QVector2D(ValueToPixel(0, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||
QVector2D top_right = QVector2D(ValueToPixel(1, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||
QVector2D bottom_right = QVector2D(ValueToPixel(2, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||
QVector2D bottom_left = QVector2D(ValueToPixel(3, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||
|
||||
// Override default vertex coordinates.
|
||||
QVector<float> adjusted_vertices = {top_left.x(), top_left.y(), 0.0f,
|
||||
top_right.x(), top_right.y(), 0.0f,
|
||||
bottom_right.x(), bottom_right.y(), 0.0f,
|
||||
|
||||
top_left.x(), top_left.y(), 0.0f,
|
||||
bottom_left.x(), bottom_left.y(), 0.0f,
|
||||
bottom_right.x(), bottom_right.y(), 0.0f};
|
||||
job.SetVertexCoordinates(adjusted_vertices);
|
||||
|
||||
// If no texture do nothing
|
||||
if (job.Get(kTextureInput).toTexture()) {
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
// In the special case that all sliders are in their default position just
|
||||
// push the texture.
|
||||
if (!(job.Get(kTopLeftInput).toVec2().isNull()
|
||||
&& job.Get(kTopRightInput).toVec2().isNull() &&
|
||||
job.Get(kBottomRightInput).toVec2().isNull() &&
|
||||
job.Get(kBottomLeftInput).toVec2().isNull())) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
if (!(value[kTopLeftInput].toVec2().isNull()
|
||||
&& value[kTopRightInput].toVec2().isNull() &&
|
||||
value[kBottomRightInput].toVec2().isNull() &&
|
||||
value[kBottomLeftInput].toVec2().isNull())) {
|
||||
ShaderJob job(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this));
|
||||
|
||||
// Convert slider values to their pixel values and then convert to clip space (-1.0 ... 1.0) for overriding the
|
||||
// vertex coordinates.
|
||||
const QVector2D &resolution = tex->virtual_resolution();
|
||||
QVector2D half_resolution = resolution * 0.5;
|
||||
QVector2D top_left = QVector2D(ValueToPixel(0, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||
QVector2D top_right = QVector2D(ValueToPixel(1, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||
QVector2D bottom_right = QVector2D(ValueToPixel(2, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||
QVector2D bottom_left = QVector2D(ValueToPixel(3, value, resolution)) / half_resolution - QVector2D(1.0, 1.0);
|
||||
|
||||
// Override default vertex coordinates.
|
||||
QVector<float> adjusted_vertices = {top_left.x(), top_left.y(), 0.0f,
|
||||
top_right.x(), top_right.y(), 0.0f,
|
||||
bottom_right.x(), bottom_right.y(), 0.0f,
|
||||
|
||||
top_left.x(), top_left.y(), 0.0f,
|
||||
bottom_left.x(), bottom_left.y(), 0.0f,
|
||||
bottom_right.x(), bottom_right.y(), 0.0f};
|
||||
job.SetVertexCoordinates(adjusted_vertices);
|
||||
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
} else {
|
||||
table->Push(job.Get(kTextureInput));
|
||||
table->Push(value[kTextureInput]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,27 +150,29 @@ void CornerPinDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardM
|
||||
|
||||
void CornerPinDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
const QVector2D &resolution = globals.resolution();
|
||||
if (TexturePtr tex = row[kTextureInput].toTexture()) {
|
||||
const QVector2D &resolution = tex->virtual_resolution();
|
||||
|
||||
QPointF top_left = ValueToPixel(0, row, resolution);
|
||||
QPointF top_right = ValueToPixel(1, row, resolution);
|
||||
QPointF bottom_right = ValueToPixel(2, row, resolution);
|
||||
QPointF bottom_left = ValueToPixel(3, row, resolution);
|
||||
QPointF top_left = ValueToPixel(0, row, resolution);
|
||||
QPointF top_right = ValueToPixel(1, row, resolution);
|
||||
QPointF bottom_right = ValueToPixel(2, row, resolution);
|
||||
QPointF bottom_left = ValueToPixel(3, row, resolution);
|
||||
|
||||
// Add the correct offset to each slider
|
||||
SetInputProperty(kTopLeftInput, QStringLiteral("offset"), QVector2D(0.0, 0.0));
|
||||
SetInputProperty(kTopRightInput, QStringLiteral("offset"), QVector2D(resolution.x() , 0.0));
|
||||
SetInputProperty(kBottomRightInput, QStringLiteral("offset"), resolution);
|
||||
SetInputProperty(kBottomLeftInput, QStringLiteral("offset"), QVector2D(0.0, resolution.y()));
|
||||
// Add the correct offset to each slider
|
||||
SetInputProperty(kTopLeftInput, QStringLiteral("offset"), QVector2D(0.0, 0.0));
|
||||
SetInputProperty(kTopRightInput, QStringLiteral("offset"), QVector2D(resolution.x() , 0.0));
|
||||
SetInputProperty(kBottomRightInput, QStringLiteral("offset"), resolution);
|
||||
SetInputProperty(kBottomLeftInput, QStringLiteral("offset"), QVector2D(0.0, resolution.y()));
|
||||
|
||||
// Draw bounding box
|
||||
gizmo_whole_rect_->SetPolygon(QPolygonF({top_left, top_right, bottom_right, bottom_left, top_left}));
|
||||
// Draw bounding box
|
||||
gizmo_whole_rect_->SetPolygon(QPolygonF({top_left, top_right, bottom_right, bottom_left, top_left}));
|
||||
|
||||
// Create handles
|
||||
gizmo_resize_handle_[0]->SetPoint(top_left);
|
||||
gizmo_resize_handle_[1]->SetPoint(top_right);
|
||||
gizmo_resize_handle_[2]->SetPoint(bottom_right);
|
||||
gizmo_resize_handle_[3]->SetPoint(bottom_left);
|
||||
// Create handles
|
||||
gizmo_resize_handle_[0]->SetPoint(top_left);
|
||||
gizmo_resize_handle_[1]->SetPoint(top_right);
|
||||
gizmo_resize_handle_[2]->SetPoint(bottom_right);
|
||||
gizmo_resize_handle_[3]->SetPoint(bottom_left);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,7 +79,6 @@ void CropDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
{
|
||||
ShaderJob job;
|
||||
job.Insert(value);
|
||||
job.SetWillChangeImageSize(false);
|
||||
|
||||
if (TexturePtr texture = job.Get(kTextureInput).toTexture()) {
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture->params().width(), texture->params().height()), this));
|
||||
@@ -88,7 +87,7 @@ void CropDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
|| !qIsNull(job.Get(kRightInput).toDouble())
|
||||
|| !qIsNull(job.Get(kTopInput).toDouble())
|
||||
|| !qIsNull(job.Get(kBottomInput).toDouble())) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, texture->toJob(job), this);
|
||||
} else {
|
||||
table->Push(job.Get(kTextureInput));
|
||||
}
|
||||
@@ -103,32 +102,35 @@ ShaderCode CropDistortNode::GetShaderCode(const ShaderRequest &request) const
|
||||
|
||||
void CropDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
const QVector2D &resolution = globals.resolution();
|
||||
if (TexturePtr tex = row[kTextureInput].toTexture()) {
|
||||
const QVector2D &resolution = tex->virtual_resolution();
|
||||
temp_resolution_ = resolution;
|
||||
|
||||
double left_pt = resolution.x() * row[kLeftInput].toDouble();
|
||||
double top_pt = resolution.y() * row[kTopInput].toDouble();
|
||||
double right_pt = resolution.x() * (1.0 - row[kRightInput].toDouble());
|
||||
double bottom_pt = resolution.y() * (1.0 - row[kBottomInput].toDouble());
|
||||
double center_x_pt = mid(left_pt, right_pt);
|
||||
double center_y_pt = mid(top_pt, bottom_pt);
|
||||
double left_pt = resolution.x() * row[kLeftInput].toDouble();
|
||||
double top_pt = resolution.y() * row[kTopInput].toDouble();
|
||||
double right_pt = resolution.x() * (1.0 - row[kRightInput].toDouble());
|
||||
double bottom_pt = resolution.y() * (1.0 - row[kBottomInput].toDouble());
|
||||
double center_x_pt = mid(left_pt, right_pt);
|
||||
double center_y_pt = mid(top_pt, bottom_pt);
|
||||
|
||||
point_gizmo_[kGizmoScaleTopLeft]->SetPoint(QPointF(left_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleTopCenter]->SetPoint(QPointF(center_x_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleTopRight]->SetPoint(QPointF(right_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleBottomLeft]->SetPoint(QPointF(left_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleBottomCenter]->SetPoint(QPointF(center_x_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleBottomRight]->SetPoint(QPointF(right_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleCenterLeft]->SetPoint(QPointF(left_pt, center_y_pt));
|
||||
point_gizmo_[kGizmoScaleCenterRight]->SetPoint(QPointF(right_pt, center_y_pt));
|
||||
point_gizmo_[kGizmoScaleTopLeft]->SetPoint(QPointF(left_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleTopCenter]->SetPoint(QPointF(center_x_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleTopRight]->SetPoint(QPointF(right_pt, top_pt));
|
||||
point_gizmo_[kGizmoScaleBottomLeft]->SetPoint(QPointF(left_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleBottomCenter]->SetPoint(QPointF(center_x_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleBottomRight]->SetPoint(QPointF(right_pt, bottom_pt));
|
||||
point_gizmo_[kGizmoScaleCenterLeft]->SetPoint(QPointF(left_pt, center_y_pt));
|
||||
point_gizmo_[kGizmoScaleCenterRight]->SetPoint(QPointF(right_pt, center_y_pt));
|
||||
|
||||
poly_gizmo_->SetPolygon(QRectF(left_pt, top_pt, right_pt - left_pt, bottom_pt - top_pt));
|
||||
poly_gizmo_->SetPolygon(QRectF(left_pt, top_pt, right_pt - left_pt, bottom_pt - top_pt));
|
||||
}
|
||||
}
|
||||
|
||||
void CropDistortNode::GizmoDragMove(double x_diff, double y_diff, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo*>(sender());
|
||||
|
||||
QVector2D res = gizmo->GetGlobals().resolution();
|
||||
QVector2D res = temp_resolution_;
|
||||
x_diff /= res.x();
|
||||
y_diff /= res.y();
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ private:
|
||||
// Gizmo variables
|
||||
PointGizmo *point_gizmo_[kGizmoScaleCount];
|
||||
PolygonGizmo *poly_gizmo_;
|
||||
QVector2D temp_resolution_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -77,21 +77,16 @@ ShaderCode FlipDistortNode::GetShaderCode(const ShaderRequest &request) const
|
||||
|
||||
void FlipDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
|
||||
job.Insert(value);
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (job.Get(kTextureInput).toTexture()) {
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (job.Get(kHorizontalInput).toBool() || job.Get(kVerticalInput).toBool()) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
if (value[kHorizontalInput].toBool() || value[kVerticalInput].toBool()) {
|
||||
table->Push(NodeValue::kTexture, tex->toJob(ShaderJob(value)), this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->Push(job.Get(kTextureInput));
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->Push(value[kTextureInput]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,12 +27,15 @@ namespace olive {
|
||||
#define super PolygonGenerator
|
||||
|
||||
const QString MaskDistortNode::kFeatherInput = QStringLiteral("feather_in");
|
||||
const QString MaskDistortNode::kInvertInput = QStringLiteral("invert_in");
|
||||
|
||||
MaskDistortNode::MaskDistortNode()
|
||||
{
|
||||
// Mask should always be (1.0, 1.0, 1.0) for multiply to work correctly
|
||||
SetInputFlags(kColorInput, InputFlags(GetInputFlags(kColorInput) | kInputFlagHidden));
|
||||
|
||||
AddInput(kInvertInput, NodeValue::kBoolean, false);
|
||||
|
||||
AddInput(kFeatherInput, NodeValue::kFloat, 0.0);
|
||||
SetInputProperty(kFeatherInput, QStringLiteral("min"), 0.0);
|
||||
}
|
||||
@@ -43,6 +46,8 @@ ShaderCode MaskDistortNode::GetShaderCode(const ShaderRequest &request) const
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/multiply.frag")));
|
||||
} else if (request.id == QStringLiteral("feather")) {
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/blur.frag")));
|
||||
} else if (request.id == QStringLiteral("invert")) {
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/invertrgb.frag")));
|
||||
} else {
|
||||
return super::GetShaderCode(request);
|
||||
}
|
||||
@@ -53,14 +58,25 @@ void MaskDistortNode::Retranslate()
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kBaseInput, tr("Texture"));
|
||||
SetInputName(kInvertInput, tr("Invert"));
|
||||
SetInputName(kFeatherInput, tr("Feather"));
|
||||
}
|
||||
|
||||
void MaskDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job = GetGenerateJob(value);
|
||||
TexturePtr texture = value[kBaseInput].toTexture();
|
||||
|
||||
if (value[kBaseInput].toTexture()) {
|
||||
VideoParams job_params = texture ? texture->params() : globals.vparams();
|
||||
NodeValue job(NodeValue::kTexture, Texture::Job(job_params, GetGenerateJob(value, job_params)), this);
|
||||
|
||||
if (value[kInvertInput].toBool()) {
|
||||
ShaderJob invert;
|
||||
invert.SetShaderID(QStringLiteral("invert"));
|
||||
invert.Insert(QStringLiteral("tex_in"), job);
|
||||
job.set_value(Texture::Job(job_params, invert));
|
||||
}
|
||||
|
||||
if (texture) {
|
||||
// Push as merge node
|
||||
ShaderJob merge;
|
||||
|
||||
@@ -72,21 +88,23 @@ void MaskDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
ShaderJob feather;
|
||||
|
||||
feather.SetShaderID(QStringLiteral("feather"));
|
||||
feather.Insert(BlurFilterNode::kTextureInput, NodeValue(NodeValue::kTexture, job, this));
|
||||
feather.Insert(BlurFilterNode::kTextureInput, job);
|
||||
feather.Insert(BlurFilterNode::kMethodInput, NodeValue(NodeValue::kInt, int(BlurFilterNode::kGaussian), this));
|
||||
feather.Insert(BlurFilterNode::kHorizInput, NodeValue(NodeValue::kBoolean, true, this));
|
||||
feather.Insert(BlurFilterNode::kVertInput, NodeValue(NodeValue::kBoolean, true, this));
|
||||
feather.Insert(BlurFilterNode::kRepeatEdgePixelsInput, NodeValue(NodeValue::kBoolean, true, this));
|
||||
feather.Insert(BlurFilterNode::kRadiusInput, NodeValue(NodeValue::kFloat, value[kFeatherInput].toDouble(), this));
|
||||
feather.SetIterations(2, BlurFilterNode::kTextureInput);
|
||||
feather.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||
feather.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, texture ? texture->virtual_resolution() : globals.square_resolution(), this));
|
||||
|
||||
merge.Insert(QStringLiteral("tex_b"), NodeValue(NodeValue::kTexture, feather, this));
|
||||
merge.Insert(QStringLiteral("tex_b"), NodeValue(NodeValue::kTexture, Texture::Job(job_params, feather), this));
|
||||
} else {
|
||||
merge.Insert(QStringLiteral("tex_b"), NodeValue(NodeValue::kTexture, job, this));
|
||||
merge.Insert(QStringLiteral("tex_b"), job);
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(merge), this);
|
||||
table->Push(NodeValue::kTexture, Texture::Job(job_params, merge), this);
|
||||
} else {
|
||||
table->Push(job);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
static const QString kInvertInput;
|
||||
static const QString kFeatherInput;
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/ripple/rippledistortnode.cpp
|
||||
node/distort/ripple/rippledistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,128 @@
|
||||
/***
|
||||
|
||||
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 "rippledistortnode.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString RippleDistortNode::kTextureInput = QStringLiteral("tex_in");
|
||||
const QString RippleDistortNode::kEvolutionInput = QStringLiteral("evolution_in");
|
||||
const QString RippleDistortNode::kIntensityInput = QStringLiteral("intensity_in");
|
||||
const QString RippleDistortNode::kFrequencyInput = QStringLiteral("frequency_in");
|
||||
const QString RippleDistortNode::kPositionInput = QStringLiteral("position_in");
|
||||
const QString RippleDistortNode::kStretchInput = QStringLiteral("stretch_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
RippleDistortNode::RippleDistortNode()
|
||||
{
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kEvolutionInput, NodeValue::kFloat, 0);
|
||||
AddInput(kIntensityInput, NodeValue::kFloat, 100);
|
||||
|
||||
AddInput(kFrequencyInput, NodeValue::kFloat, 1);
|
||||
SetInputProperty(kFrequencyInput, QStringLiteral("base"), 0.01);
|
||||
|
||||
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0));
|
||||
AddInput(kStretchInput, NodeValue::kBoolean, false);
|
||||
|
||||
SetFlags(kVideoEffect);
|
||||
SetEffectInput(kTextureInput);
|
||||
|
||||
gizmo_ = AddDraggableGizmo<PointGizmo>({
|
||||
NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1),
|
||||
});
|
||||
gizmo_->SetShape(PointGizmo::kAnchorPoint);
|
||||
}
|
||||
|
||||
QString RippleDistortNode::Name() const
|
||||
{
|
||||
return tr("Ripple");
|
||||
}
|
||||
|
||||
QString RippleDistortNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.oliveeditor.Olive.ripple");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> RippleDistortNode::Category() const
|
||||
{
|
||||
return {kCategoryDistort};
|
||||
}
|
||||
|
||||
QString RippleDistortNode::Description() const
|
||||
{
|
||||
return tr("Distorts an image with a ripple effect.");
|
||||
}
|
||||
|
||||
void RippleDistortNode::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
SetInputName(kFrequencyInput, tr("Frequency"));
|
||||
SetInputName(kIntensityInput, tr("Intensity"));
|
||||
SetInputName(kEvolutionInput, tr("Evolution"));
|
||||
SetInputName(kPositionInput, tr("Position"));
|
||||
SetInputName(kStretchInput, tr("Stretch"));
|
||||
}
|
||||
|
||||
ShaderCode RippleDistortNode::GetShaderCode(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/ripple.frag"));
|
||||
}
|
||||
|
||||
void RippleDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (!qIsNull(value[kIntensityInput].toDouble())) {
|
||||
ShaderJob job(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this));
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->Push(value[kTextureInput]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RippleDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
if (TexturePtr tex = row[kTextureInput].toTexture()) {
|
||||
QPointF half_res(tex->virtual_resolution().x()/2, tex->virtual_resolution().y()/2);
|
||||
gizmo_->SetPoint(half_res + row[kPositionInput].toVec2().toPointF());
|
||||
}
|
||||
}
|
||||
|
||||
void RippleDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
NodeInputDragger &x_drag = gizmo_->GetDraggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo_->GetDraggers()[1];
|
||||
|
||||
x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
|
||||
y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/***
|
||||
|
||||
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 RIPPLEDISTORTNODE_H
|
||||
#define RIPPLEDISTORTNODE_H
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class RippleDistortNode : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RippleDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(RippleDistortNode)
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kEvolutionInput;
|
||||
static const QString kIntensityInput;
|
||||
static const QString kFrequencyInput;
|
||||
static const QString kPositionInput;
|
||||
static const QString kStretchInput;
|
||||
|
||||
protected slots:
|
||||
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
PointGizmo *gizmo_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // RIPPLEDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/swirl/swirldistortnode.cpp
|
||||
node/distort/swirl/swirldistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,122 @@
|
||||
/***
|
||||
|
||||
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 "swirldistortnode.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString SwirlDistortNode::kTextureInput = QStringLiteral("tex_in");
|
||||
const QString SwirlDistortNode::kRadiusInput = QStringLiteral("radius_in");
|
||||
const QString SwirlDistortNode::kAngleInput = QStringLiteral("angle_in");
|
||||
const QString SwirlDistortNode::kPositionInput = QStringLiteral("pos_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
SwirlDistortNode::SwirlDistortNode()
|
||||
{
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kRadiusInput, NodeValue::kFloat, 200);
|
||||
SetInputProperty(kRadiusInput, QStringLiteral("min"), 0);
|
||||
|
||||
AddInput(kAngleInput, NodeValue::kFloat, 10);
|
||||
SetInputProperty(kAngleInput, QStringLiteral("base"), 0.1);
|
||||
|
||||
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0));
|
||||
|
||||
SetFlags(kVideoEffect);
|
||||
SetEffectInput(kTextureInput);
|
||||
|
||||
gizmo_ = AddDraggableGizmo<PointGizmo>({
|
||||
NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1),
|
||||
});
|
||||
gizmo_->SetShape(PointGizmo::kAnchorPoint);
|
||||
}
|
||||
|
||||
QString SwirlDistortNode::Name() const
|
||||
{
|
||||
return tr("Swirl");
|
||||
}
|
||||
|
||||
QString SwirlDistortNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.oliveeditor.Olive.swirl");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> SwirlDistortNode::Category() const
|
||||
{
|
||||
return {kCategoryDistort};
|
||||
}
|
||||
|
||||
QString SwirlDistortNode::Description() const
|
||||
{
|
||||
return tr("Distorts an image along a sine wave.");
|
||||
}
|
||||
|
||||
void SwirlDistortNode::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
SetInputName(kRadiusInput, tr("Radius"));
|
||||
SetInputName(kAngleInput, tr("Angle"));
|
||||
SetInputName(kPositionInput, tr("Position"));
|
||||
}
|
||||
|
||||
ShaderCode SwirlDistortNode::GetShaderCode(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/swirl.frag"));
|
||||
}
|
||||
|
||||
void SwirlDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (!qIsNull(value[kAngleInput].toDouble()) && !qIsNull(value[kRadiusInput].toDouble())) {
|
||||
ShaderJob job(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this));
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->Push(value[kTextureInput]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SwirlDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
QPointF half_res(globals.square_resolution().x()/2, globals.square_resolution().y()/2);
|
||||
|
||||
gizmo_->SetPoint(half_res + row[kPositionInput].toVec2().toPointF());
|
||||
}
|
||||
|
||||
void SwirlDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
NodeInputDragger &x_drag = gizmo_->GetDraggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo_->GetDraggers()[1];
|
||||
|
||||
x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
|
||||
y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/***
|
||||
|
||||
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 SWIRLDISTORTNODE_H
|
||||
#define SWIRLDISTORTNODE_H
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class SwirlDistortNode : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SwirlDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(SwirlDistortNode)
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kRadiusInput;
|
||||
static const QString kAngleInput;
|
||||
static const QString kPositionInput;
|
||||
|
||||
protected slots:
|
||||
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
PointGizmo *gizmo_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SWIRLDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/tile/tiledistortnode.cpp
|
||||
node/distort/tile/tiledistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,164 @@
|
||||
/***
|
||||
|
||||
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 "tiledistortnode.h"
|
||||
|
||||
#include "widget/slider/floatslider.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString TileDistortNode::kTextureInput = QStringLiteral("tex_in");
|
||||
const QString TileDistortNode::kScaleInput = QStringLiteral("scale_in");
|
||||
const QString TileDistortNode::kPositionInput = QStringLiteral("position_in");
|
||||
const QString TileDistortNode::kAnchorInput = QStringLiteral("anchor_in");
|
||||
const QString TileDistortNode::kMirrorXInput = QStringLiteral("mirrorx_in");
|
||||
const QString TileDistortNode::kMirrorYInput = QStringLiteral("mirrory_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
TileDistortNode::TileDistortNode()
|
||||
{
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kScaleInput, NodeValue::kFloat, 0.5);
|
||||
SetInputProperty(kScaleInput, QStringLiteral("min"), 0);
|
||||
SetInputProperty(kScaleInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||
|
||||
AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0));
|
||||
|
||||
AddInput(kAnchorInput, NodeValue::kCombo, kMiddleCenter);
|
||||
|
||||
AddInput(kMirrorXInput, NodeValue::kBoolean, false);
|
||||
AddInput(kMirrorYInput, NodeValue::kBoolean, false);
|
||||
|
||||
SetFlags(kVideoEffect);
|
||||
SetEffectInput(kTextureInput);
|
||||
|
||||
gizmo_ = AddDraggableGizmo<PointGizmo>({
|
||||
NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1),
|
||||
});
|
||||
gizmo_->SetShape(PointGizmo::kAnchorPoint);
|
||||
}
|
||||
|
||||
QString TileDistortNode::Name() const
|
||||
{
|
||||
return tr("Tile");
|
||||
}
|
||||
|
||||
QString TileDistortNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.oliveeditor.Olive.tile");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> TileDistortNode::Category() const
|
||||
{
|
||||
return {kCategoryDistort};
|
||||
}
|
||||
|
||||
QString TileDistortNode::Description() const
|
||||
{
|
||||
return tr("Infinitely tile an image horizontally and vertically.");
|
||||
}
|
||||
|
||||
void TileDistortNode::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
SetInputName(kScaleInput, tr("Scale"));
|
||||
SetInputName(kPositionInput, tr("Position"));
|
||||
SetInputName(kMirrorXInput, tr("Mirror Horizontally"));
|
||||
SetInputName(kMirrorYInput, tr("Mirror Vertically"));
|
||||
|
||||
SetInputName(kAnchorInput, tr("Anchor"));
|
||||
SetComboBoxStrings(kAnchorInput, {
|
||||
tr("Top-Left"),
|
||||
tr("Top-Center"),
|
||||
tr("Top-Right"),
|
||||
tr("Middle-Left"),
|
||||
tr("Middle-Center"),
|
||||
tr("Middle-Right"),
|
||||
tr("Bottom-Left"),
|
||||
tr("Bottom-Center"),
|
||||
tr("Bottom-Right"),
|
||||
});
|
||||
}
|
||||
|
||||
ShaderCode TileDistortNode::GetShaderCode(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/tile.frag"));
|
||||
}
|
||||
|
||||
void TileDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (!qFuzzyCompare(value[kScaleInput].toDouble(), 1.0)) {
|
||||
ShaderJob job(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this));
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->Push(value[kTextureInput]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
if (TexturePtr tex = row[kTextureInput].toTexture()) {
|
||||
QPointF res = tex->virtual_resolution().toPointF();
|
||||
QPointF pos = row[kPositionInput].toVec2().toPointF();
|
||||
qreal x = pos.x();
|
||||
qreal y = pos.y();
|
||||
|
||||
Anchor a = static_cast<Anchor>(row[kAnchorInput].toInt());
|
||||
if (a == kTopLeft || a == kTopCenter || a == kTopRight) {
|
||||
// Do nothing
|
||||
} else if (a == kMiddleLeft || a == kMiddleCenter || a == kMiddleRight) {
|
||||
y += res.y()/2;
|
||||
} else if (a == kBottomLeft || a == kBottomCenter || a == kBottomRight) {
|
||||
y += res.y();
|
||||
}
|
||||
if (a == kTopLeft || a == kMiddleLeft || a == kBottomLeft) {
|
||||
// Do nothing
|
||||
} else if (a == kTopCenter || a == kMiddleCenter || a == kBottomCenter) {
|
||||
x += res.x()/2;
|
||||
} else if (a == kTopRight || a == kMiddleRight || a == kBottomRight) {
|
||||
x += res.x();
|
||||
}
|
||||
|
||||
gizmo_->SetPoint(QPointF(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
void TileDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
NodeInputDragger &x_drag = gizmo_->GetDraggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo_->GetDraggers()[1];
|
||||
|
||||
x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
|
||||
y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/***
|
||||
|
||||
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 TILEDISTORTNODE_H
|
||||
#define TILEDISTORTNODE_H
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class TileDistortNode : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TileDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(TileDistortNode)
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kScaleInput;
|
||||
static const QString kPositionInput;
|
||||
static const QString kAnchorInput;
|
||||
static const QString kMirrorXInput;
|
||||
static const QString kMirrorYInput;
|
||||
|
||||
protected slots:
|
||||
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
enum Anchor {
|
||||
kTopLeft,
|
||||
kTopCenter,
|
||||
kTopRight,
|
||||
kMiddleLeft,
|
||||
kMiddleCenter,
|
||||
kMiddleRight,
|
||||
kBottomLeft,
|
||||
kBottomCenter,
|
||||
kBottomRight
|
||||
};
|
||||
|
||||
PointGizmo *gizmo_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TILEDISTORTNODE_H
|
||||
@@ -89,7 +89,7 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
|
||||
// Pop texture
|
||||
NodeValue texture_meta = value[kTextureInput];
|
||||
|
||||
QVariant job_to_push;
|
||||
TexturePtr job_to_push = nullptr;
|
||||
|
||||
// If we have a texture, generate a matrix and make it happen
|
||||
if (TexturePtr texture = texture_meta.toTexture()) {
|
||||
@@ -99,17 +99,18 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
|
||||
if (!real_matrix.isIdentity()) {
|
||||
// The matrix will transform things
|
||||
ShaderJob job;
|
||||
job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture), this));
|
||||
job.Insert(QStringLiteral("ove_maintex"), texture_meta);
|
||||
job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this));
|
||||
job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast<Texture::Interpolation>(value[kInterpolationInput].toInt()));
|
||||
|
||||
job_to_push = QVariant::fromValue(job);
|
||||
// Use global resolution rather than texture resolution because this may result in a size change
|
||||
job_to_push = Texture::Job(globals.vparams(), job);
|
||||
}
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kMatrix, QVariant::fromValue(generated_matrix), this);
|
||||
|
||||
if (job_to_push.isNull()) {
|
||||
if (!job_to_push) {
|
||||
// Re-push whatever value we received
|
||||
table->Push(texture_meta);
|
||||
} else {
|
||||
@@ -142,7 +143,7 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou
|
||||
}
|
||||
|
||||
gizmo_scale_uniform_ = row[kUniformScaleInput].toBool();
|
||||
gizmo_anchor_pt_ = (row[kAnchorInput].toVec2() + gizmo->GetGlobals().resolution()/2).toPointF();
|
||||
gizmo_anchor_pt_ = (row[kAnchorInput].toVec2() + gizmo->GetGlobals().nonsquare_resolution()/2).toPointF();
|
||||
|
||||
if (gizmo == point_gizmo_[kGizmoScaleTopLeft] || gizmo == point_gizmo_[kGizmoScaleTopRight]
|
||||
|| gizmo == point_gizmo_[kGizmoScaleBottomLeft] || gizmo == point_gizmo_[kGizmoScaleBottomRight]) {
|
||||
@@ -177,7 +178,7 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou
|
||||
|
||||
} else if (gizmo == rotation_gizmo_) {
|
||||
|
||||
gizmo_anchor_pt_ = (row[kAnchorInput].toVec2() + gizmo->GetGlobals().resolution()/2).toPointF();
|
||||
gizmo_anchor_pt_ = (row[kAnchorInput].toVec2() + gizmo->GetGlobals().nonsquare_resolution()/2).toPointF();
|
||||
gizmo_start_angle_ = qAtan2(y - gizmo_anchor_pt_.y(), x - gizmo_anchor_pt_.x());
|
||||
gizmo_last_angle_ = gizmo_start_angle_;
|
||||
gizmo_last_alt_angle_ = qAtan2(x - gizmo_anchor_pt_.x(), y - gizmo_anchor_pt_.y());
|
||||
@@ -343,7 +344,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N
|
||||
}
|
||||
|
||||
// Get the sequence resolution
|
||||
const QVector2D &sequence_res = globals.resolution();
|
||||
const QVector2D &sequence_res = globals.nonsquare_resolution();
|
||||
QVector2D sequence_half_res = sequence_res * 0.5;
|
||||
QPointF sequence_half_res_pt = sequence_half_res.toPointF();
|
||||
|
||||
@@ -418,7 +419,7 @@ QPointF TransformDistortNode::CreateScalePoint(double x, double y, const QPointF
|
||||
|
||||
QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& generated_matrix, const NodeValueRow& value, const NodeGlobals &globals, const VideoParams& texture_params) const
|
||||
{
|
||||
const QVector2D &sequence_res = globals.resolution();
|
||||
const QVector2D &sequence_res = globals.nonsquare_resolution();
|
||||
QVector2D texture_res(texture_params.square_pixel_width(), texture_params.height());
|
||||
AutoScaleType autoscale = static_cast<AutoScaleType>(value[kAutoscaleInput].toInt());
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/wave/wavedistortnode.cpp
|
||||
node/distort/wave/wavedistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,100 @@
|
||||
/***
|
||||
|
||||
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 "wavedistortnode.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString WaveDistortNode::kTextureInput = QStringLiteral("tex_in");
|
||||
const QString WaveDistortNode::kFrequencyInput = QStringLiteral("frequency_in");
|
||||
const QString WaveDistortNode::kIntensityInput = QStringLiteral("intensity_in");
|
||||
const QString WaveDistortNode::kEvolutionInput = QStringLiteral("evolution_in");
|
||||
const QString WaveDistortNode::kVerticalInput = QStringLiteral("vertical_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
WaveDistortNode::WaveDistortNode()
|
||||
{
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kFrequencyInput, NodeValue::kFloat, 10);
|
||||
AddInput(kIntensityInput, NodeValue::kFloat, 10);
|
||||
AddInput(kEvolutionInput, NodeValue::kFloat, 0);
|
||||
|
||||
AddInput(kVerticalInput, NodeValue::kCombo, false);
|
||||
|
||||
SetFlags(kVideoEffect);
|
||||
SetEffectInput(kTextureInput);
|
||||
}
|
||||
|
||||
QString WaveDistortNode::Name() const
|
||||
{
|
||||
return tr("Wave");
|
||||
}
|
||||
|
||||
QString WaveDistortNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.oliveeditor.Olive.wave");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> WaveDistortNode::Category() const
|
||||
{
|
||||
return {kCategoryDistort};
|
||||
}
|
||||
|
||||
QString WaveDistortNode::Description() const
|
||||
{
|
||||
return tr("Distorts an image along a sine wave.");
|
||||
}
|
||||
|
||||
void WaveDistortNode::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
SetInputName(kFrequencyInput, tr("Frequency"));
|
||||
SetInputName(kIntensityInput, tr("Intensity"));
|
||||
SetInputName(kEvolutionInput, tr("Evolution"));
|
||||
SetInputName(kVerticalInput, tr("Direction"));
|
||||
SetComboBoxStrings(kVerticalInput, {tr("Horizontal"), tr("Vertical")});
|
||||
}
|
||||
|
||||
ShaderCode WaveDistortNode::GetShaderCode(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/wave.frag"));
|
||||
}
|
||||
|
||||
void WaveDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr texture = value[kTextureInput].toTexture()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (!qIsNull(value[kIntensityInput].toDouble())) {
|
||||
table->Push(NodeValue::kTexture, Texture::Job(texture->params(), ShaderJob(value)), this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->Push(value[kTextureInput]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/***
|
||||
|
||||
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 WAVEDISTORTNODE_H
|
||||
#define WAVEDISTORTNODE_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class WaveDistortNode : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaveDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(WaveDistortNode)
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kFrequencyInput;
|
||||
static const QString kIntensityInput;
|
||||
static const QString kEvolutionInput;
|
||||
static const QString kVerticalInput;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WAVEDISTORTNODE_H
|
||||
@@ -45,17 +45,13 @@ ShaderCode OpacityEffect::GetShaderCode(const ShaderRequest &request) const
|
||||
|
||||
void OpacityEffect::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
|
||||
job.Insert(value);
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (job.Get(kTextureInput).toTexture()) {
|
||||
if (!qFuzzyCompare(job.Get(kValueInput).toDouble(), 1.0)) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
if (!qFuzzyCompare(value[kValueInput].toDouble(), 1.0)) {
|
||||
table->Push(NodeValue::kTexture, tex->toJob(ShaderJob(value)), this);
|
||||
} else {
|
||||
// 1.0 float is a no-op, so just push the texture
|
||||
table->Push(job.Get(kTextureInput));
|
||||
table->Push(value[kTextureInput]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,11 @@
|
||||
#include "distort/crop/cropdistortnode.h"
|
||||
#include "distort/flip/flipdistortnode.h"
|
||||
#include "distort/mask/mask.h"
|
||||
#include "distort/ripple/rippledistortnode.h"
|
||||
#include "distort/swirl/swirldistortnode.h"
|
||||
#include "distort/tile/tiledistortnode.h"
|
||||
#include "distort/transform/transformdistortnode.h"
|
||||
#include "distort/wave/wavedistortnode.h"
|
||||
#include "effect/opacity/opacityeffect.h"
|
||||
#include "filter/blur/blur.h"
|
||||
#include "filter/dropshadow/dropshadowfilter.h"
|
||||
@@ -295,6 +299,14 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
|
||||
return new DropShadowFilter();
|
||||
case kTimeFormat:
|
||||
return new TimeFormatNode();
|
||||
case kWaveDistort:
|
||||
return new WaveDistortNode();
|
||||
case kTileDistort:
|
||||
return new TileDistortNode();
|
||||
case kSwirlDistort:
|
||||
return new SwirlDistortNode();
|
||||
case kRippleDistort:
|
||||
return new RippleDistortNode();
|
||||
case kMulticamNode:
|
||||
return new MultiCamNode();
|
||||
|
||||
|
||||
@@ -76,6 +76,10 @@ public:
|
||||
kMaskDistort,
|
||||
kDropShadowFilter,
|
||||
kTimeFormat,
|
||||
kWaveDistort,
|
||||
kRippleDistort,
|
||||
kTileDistort,
|
||||
kSwirlDistort,
|
||||
kMulticamNode,
|
||||
|
||||
// Count value
|
||||
|
||||
@@ -120,33 +120,28 @@ ShaderCode BlurFilterNode::GetShaderCode(const ShaderRequest &request) const
|
||||
void BlurFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (value[kTextureInput].toTexture()) {
|
||||
|
||||
ShaderJob job;
|
||||
|
||||
job.Insert(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||
|
||||
Method method = static_cast<Method>(job.Get(kMethodInput).toInt());
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
Method method = static_cast<Method>(value[kMethodInput].toInt());
|
||||
|
||||
bool can_push_job = true;
|
||||
int iterations = 1;
|
||||
|
||||
// Check if radius is > 0
|
||||
if (job.Get(kRadiusInput).toDouble() > 0.0) {
|
||||
if (value[kRadiusInput].toDouble() > 0.0) {
|
||||
// Method-specific considerations
|
||||
switch (method) {
|
||||
case kBox:
|
||||
case kGaussian:
|
||||
{
|
||||
bool horiz = job.Get(kHorizInput).toBool();
|
||||
bool vert = job.Get(kVertInput).toBool();
|
||||
bool horiz = value[kHorizInput].toBool();
|
||||
bool vert = value[kVertInput].toBool();
|
||||
|
||||
if (!horiz && !vert) {
|
||||
// Disable job if horiz and vert are unchecked
|
||||
can_push_job = false;
|
||||
} else if (horiz && vert) {
|
||||
// Set iteration count to 2 if we're blurring both horizontally and vertically
|
||||
job.SetIterations(2, kTextureInput);
|
||||
iterations = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -159,10 +154,13 @@ void BlurFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals
|
||||
}
|
||||
|
||||
if (can_push_job) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
ShaderJob job(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this));
|
||||
job.SetIterations(iterations, kTextureInput);
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
} else {
|
||||
// If we're not performing the blur job, just push the texture
|
||||
table->Push(job.Get(kTextureInput));
|
||||
table->Push(value[kTextureInput]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -170,16 +168,18 @@ void BlurFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals
|
||||
|
||||
void BlurFilterNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
if (row[kMethodInput].toInt() == kRadial) {
|
||||
const QVector2D &sequence_res = globals.resolution();
|
||||
QVector2D sequence_half_res = sequence_res * 0.5;
|
||||
if (TexturePtr tex = row[kTextureInput].toTexture()) {
|
||||
if (row[kMethodInput].toInt() == kRadial) {
|
||||
const QVector2D &sequence_res = tex->virtual_resolution();
|
||||
QVector2D sequence_half_res = sequence_res * 0.5;
|
||||
|
||||
radial_center_gizmo_->SetVisible(true);
|
||||
radial_center_gizmo_->SetPoint(sequence_half_res.toPointF() + row[kRadialCenterInput].toVec2().toPointF());
|
||||
radial_center_gizmo_->SetVisible(true);
|
||||
radial_center_gizmo_->SetPoint(sequence_half_res.toPointF() + row[kRadialCenterInput].toVec2().toPointF());
|
||||
|
||||
SetInputProperty(kRadialCenterInput, QStringLiteral("offset"), sequence_half_res);
|
||||
} else{
|
||||
radial_center_gizmo_->SetVisible(false);
|
||||
SetInputProperty(kRadialCenterInput, QStringLiteral("offset"), sequence_half_res);
|
||||
} else{
|
||||
radial_center_gizmo_->SetVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,20 +78,19 @@ ShaderCode DropShadowFilter::GetShaderCode(const ShaderRequest &request) const
|
||||
|
||||
void DropShadowFilter::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
if (value[kTextureInput].toTexture()) {
|
||||
ShaderJob job;
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
ShaderJob job(value);
|
||||
|
||||
QString iterative = QStringLiteral("previous_iteration_in");
|
||||
|
||||
job.Insert(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this));
|
||||
job.Insert(iterative, value[kTextureInput]);
|
||||
|
||||
if (!qIsNull(value[kSoftnessInput].toDouble())) {
|
||||
job.SetIterations(3, iterative);
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,22 +53,18 @@ void MosaicFilterNode::Retranslate()
|
||||
|
||||
void MosaicFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
|
||||
job.Insert(value);
|
||||
|
||||
// Mipmapping makes this look weird, so we just use bilinear for finding the color of each block
|
||||
job.SetInterpolation(kTextureInput, Texture::kLinear);
|
||||
|
||||
if (job.Get(kTextureInput).toTexture()) {
|
||||
TexturePtr texture = job.Get(kTextureInput).toTexture();
|
||||
|
||||
if (TexturePtr texture = value[kTextureInput].toTexture()) {
|
||||
if (texture
|
||||
&& job.Get(kHorizInput).toInt() != texture->width()
|
||||
&& job.Get(kVertInput).toInt() != texture->height()) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
&& value[kHorizInput].toInt() != texture->width()
|
||||
&& value[kVertInput].toInt() != texture->height()) {
|
||||
ShaderJob job(value);
|
||||
|
||||
// Mipmapping makes this look weird, so we just use bilinear for finding the color of each block
|
||||
job.SetInterpolation(kTextureInput, Texture::kLinear);
|
||||
|
||||
table->Push(NodeValue::kTexture, texture->toJob(job), this);
|
||||
} else {
|
||||
table->Push(job.Get(kTextureInput));
|
||||
table->Push(value[kTextureInput]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,17 +86,14 @@ void StrokeFilterNode::Retranslate()
|
||||
|
||||
void StrokeFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
|
||||
job.Insert(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||
|
||||
if (job.Get(kTextureInput).toTexture()) {
|
||||
if (job.Get(kRadiusInput).toDouble() > 0.0
|
||||
&& job.Get(kOpacityInput).toDouble() > 0.0) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
if (value[kRadiusInput].toDouble() > 0.0
|
||||
&& value[kOpacityInput].toDouble() > 0.0) {
|
||||
ShaderJob job(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this));
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
} else {
|
||||
table->Push(job.Get(kTextureInput));
|
||||
table->Push(value[kTextureInput]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +80,13 @@ ShaderCode NoiseGeneratorNode::GetShaderCode(const ShaderRequest &request) const
|
||||
|
||||
void NoiseGeneratorNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
ShaderJob job(value);
|
||||
|
||||
job.Insert(value);
|
||||
job.Insert(QStringLiteral("time_in"), NodeValue(NodeValue::kFloat, globals.time().in().toDouble(), this));
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
TexturePtr base = value[kBaseIn].toTexture();
|
||||
|
||||
table->Push(NodeValue::kTexture, Texture::Job(base ? base->params() : globals.vparams(), job), this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,12 +87,11 @@ void PolygonGenerator::Retranslate()
|
||||
SetInputName(kColorInput, tr("Color"));
|
||||
}
|
||||
|
||||
ShaderJob PolygonGenerator::GetGenerateJob(const NodeValueRow &value) const
|
||||
ShaderJob PolygonGenerator::GetGenerateJob(const NodeValueRow &value, const VideoParams ¶ms) const
|
||||
{
|
||||
GenerateJob job;
|
||||
|
||||
job.Insert(value);
|
||||
job.SetRequestedFormat(VideoParams::kFormatUnsigned8);
|
||||
VideoParams p = params;
|
||||
p.set_format(VideoParams::kFormatUnsigned8);
|
||||
auto job = Texture::Job(p, GenerateJob(value));
|
||||
|
||||
// Conversion to RGB
|
||||
ShaderJob rgb;
|
||||
@@ -105,9 +104,7 @@ ShaderJob PolygonGenerator::GetGenerateJob(const NodeValueRow &value) const
|
||||
|
||||
void PolygonGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job = GetGenerateJob(value);
|
||||
|
||||
PushMergableJob(value, QVariant::fromValue(job), table);
|
||||
PushMergableJob(value, Texture::Job(globals.vparams(), GetGenerateJob(value, globals.vparams())), table);
|
||||
}
|
||||
|
||||
void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) const
|
||||
@@ -169,7 +166,14 @@ void PolygonGenerator::ValidateGizmoVectorSize(QVector<T*> &vec, int new_sz)
|
||||
|
||||
void PolygonGenerator::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
|
||||
{
|
||||
QPointF half_res(globals.resolution_by_par().x()/2, globals.resolution_by_par().y()/2);
|
||||
QVector2D res;
|
||||
if (TexturePtr tex = row[kBaseInput].toTexture()) {
|
||||
res = tex->virtual_resolution();
|
||||
} else {
|
||||
res = globals.square_resolution();
|
||||
}
|
||||
|
||||
QPointF half_res = res.toPointF()/2;
|
||||
|
||||
auto points = row[kPointsInput].toArray();
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
static const QString kColorInput;
|
||||
|
||||
protected:
|
||||
ShaderJob GetGenerateJob(const NodeValueRow &value) const;
|
||||
ShaderJob GetGenerateJob(const NodeValueRow &value, const VideoParams ¶ms) const;
|
||||
|
||||
protected slots:
|
||||
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
@@ -51,17 +51,17 @@ ShaderCode GeneratorWithMerge::GetShaderCode(const ShaderRequest &request) const
|
||||
return ShaderCode();
|
||||
}
|
||||
|
||||
void GeneratorWithMerge::PushMergableJob(const NodeValueRow &value, const QVariant &job, NodeValueTable *table) const
|
||||
void GeneratorWithMerge::PushMergableJob(const NodeValueRow &value, TexturePtr job, NodeValueTable *table) const
|
||||
{
|
||||
if (value[kBaseInput].toTexture()) {
|
||||
if (TexturePtr base = value[kBaseInput].toTexture()) {
|
||||
// Push as merge node
|
||||
ShaderJob merge;
|
||||
|
||||
merge.SetShaderID(QStringLiteral("mrg"));
|
||||
merge.Insert(MergeNode::kBaseIn, value[kBaseInput]);
|
||||
merge.Insert(MergeNode::kBlendIn, NodeValue(NodeValue::kTexture, job, this));
|
||||
merge.Insert(MergeNode::kBlendIn, NodeValue(NodeValue::kTexture, base->toJob(*job->job()), this));
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(merge), this);
|
||||
table->Push(NodeValue::kTexture, base->toJob(merge), this);
|
||||
} else {
|
||||
// Just push generate job
|
||||
table->Push(NodeValue::kTexture, job, this);
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
static const QString kBaseInput;
|
||||
|
||||
protected:
|
||||
void PushMergableJob(const NodeValueRow &value, const QVariant &job, NodeValueTable *table) const;
|
||||
void PushMergableJob(const NodeValueRow &value, TexturePtr job, NodeValueTable *table) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -77,13 +77,14 @@ ShaderCode ShapeNode::GetShaderCode(const ShaderRequest &request) const
|
||||
|
||||
void ShapeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
TexturePtr base = value[kBaseInput].toTexture();
|
||||
|
||||
job.Insert(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||
ShaderJob job(value);
|
||||
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, base ? base->virtual_resolution() : globals.square_resolution(), this));
|
||||
job.SetShaderID(QStringLiteral("shape"));
|
||||
|
||||
PushMergableJob(value, QVariant::fromValue(job), table);
|
||||
PushMergableJob(value, Texture::Job(base ? base->params() : globals.vparams(), job), table);
|
||||
}
|
||||
|
||||
void ShapeNode::InputValueChangedEvent(const QString &input, int element)
|
||||
|
||||
@@ -77,7 +77,7 @@ void ShapeNodeBase::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlob
|
||||
{
|
||||
// Use offsets to make the appearance of values that start in the top left, even though we
|
||||
// really anchor around the center
|
||||
QVector2D center_pt = globals.resolution() * 0.5;
|
||||
QVector2D center_pt = globals.square_resolution() * 0.5;
|
||||
SetInputProperty(kPositionInput, QStringLiteral("offset"), center_pt);
|
||||
|
||||
QVector2D pos = row[kPositionInput].toVec2();
|
||||
@@ -137,7 +137,7 @@ void ShapeNodeBase::GizmoDragMove(double x, double y, const Qt::KeyboardModifier
|
||||
|
||||
QVector2D gizmo_sz_start(w_drag.GetStartValue().toDouble(), h_drag.GetStartValue().toDouble());
|
||||
QVector2D gizmo_pos_start(x_drag.GetStartValue().toDouble(), y_drag.GetStartValue().toDouble());
|
||||
QVector2D gizmo_half_res = gizmo->GetGlobals().resolution()/2;
|
||||
QVector2D gizmo_half_res = gizmo->GetGlobals().square_resolution()/2;
|
||||
QVector2D adjusted_pt(x, y);
|
||||
QVector2D new_size;
|
||||
QVector2D new_pos;
|
||||
|
||||
@@ -63,9 +63,7 @@ void SolidGenerator::Retranslate()
|
||||
|
||||
void SolidGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
job.Insert(value);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, Texture::Job(globals.vparams(), ShaderJob(value)), this);
|
||||
}
|
||||
|
||||
ShaderCode SolidGenerator::GetShaderCode(const ShaderRequest &request) const
|
||||
|
||||
@@ -92,11 +92,8 @@ void TextGeneratorV1::Retranslate()
|
||||
|
||||
void TextGeneratorV1::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
GenerateJob job;
|
||||
job.Insert(value);
|
||||
|
||||
if (!job.Get(kTextInput).toString().isEmpty()) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
if (!value[kTextInput].toString().isEmpty()) {
|
||||
table->Push(NodeValue::kTexture, Texture::Job(globals.vparams(), GenerateJob(value)), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,12 +94,11 @@ void TextGeneratorV2::Retranslate()
|
||||
|
||||
void TextGeneratorV2::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
GenerateJob job;
|
||||
job.Insert(value);
|
||||
job.SetRequestedFormat(VideoParams::kFormatFloat32);
|
||||
|
||||
if (!job.Get(kTextInput).toString().isEmpty()) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
if (!value[kTextInput].toString().isEmpty()) {
|
||||
GenerateJob job(value);
|
||||
auto text_params = globals.vparams();
|
||||
text_params.set_format(VideoParams::kFormatFloat32);
|
||||
table->Push(NodeValue::kTexture, Texture::Job(text_params, job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,9 +98,7 @@ void TextGeneratorV3::Retranslate()
|
||||
|
||||
void TextGeneratorV3::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
GenerateJob job;
|
||||
job.Insert(value);
|
||||
job.SetRequestedFormat(VideoParams::kFormatUnsigned8);
|
||||
QString text = value[kTextInput].toString();
|
||||
|
||||
if (value[kUseArgsInput].toBool()) {
|
||||
auto args = value[kArgsInput].toArray();
|
||||
@@ -111,17 +109,21 @@ void TextGeneratorV3::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
list.append(args[i].toString());
|
||||
}
|
||||
|
||||
NodeValue v = job.Get(kTextInput);
|
||||
v.set_value(FormatString(v.toString(), list));
|
||||
job.Insert(kTextInput, v);
|
||||
text = FormatString(text, list);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Provide user override for this
|
||||
job.SetColorspace(project()->color_manager()->GetDefaultInputColorSpace());
|
||||
if (!text.isEmpty()) {
|
||||
TexturePtr base = value[kTextInput].toTexture();
|
||||
|
||||
if (!job.Get(kTextInput).toString().isEmpty()) {
|
||||
PushMergableJob(value, QVariant::fromValue(job), table);
|
||||
VideoParams text_params = base ? base->params() : globals.vparams();
|
||||
text_params.set_format(VideoParams::kFormatUnsigned8);
|
||||
text_params.set_colorspace(project()->color_manager()->GetDefaultInputColorSpace());
|
||||
|
||||
GenerateJob job(value);
|
||||
job.Insert(kTextInput, NodeValue(NodeValue::kText, text));
|
||||
|
||||
PushMergableJob(value, Texture::Job(text_params, job), table);
|
||||
} else if (value[kBaseInput].toTexture()) {
|
||||
table->Push(value[kBaseInput]);
|
||||
}
|
||||
|
||||
+8
-34
@@ -24,6 +24,7 @@
|
||||
#include <QVector2D>
|
||||
|
||||
#include "common/timerange.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -32,46 +33,19 @@ class NodeGlobals
|
||||
public:
|
||||
NodeGlobals(){}
|
||||
|
||||
NodeGlobals(const QVector2D &resolution, const rational &pixel_aspect, const TimeRange &time) :
|
||||
resolution_(resolution),
|
||||
pixel_aspect_(pixel_aspect),
|
||||
NodeGlobals(const VideoParams &vparam, const TimeRange &time) :
|
||||
video_params_(vparam),
|
||||
time_(time)
|
||||
{
|
||||
resolution_by_par_ = QVector2D(resolution_.x() * pixel_aspect_.toDouble(), resolution_.y());
|
||||
}
|
||||
|
||||
const QVector2D &resolution() const
|
||||
{
|
||||
return resolution_;
|
||||
}
|
||||
|
||||
const QVector2D &resolution_by_par() const
|
||||
{
|
||||
return resolution_by_par_;
|
||||
}
|
||||
|
||||
const rational &pixel_aspect() const
|
||||
{
|
||||
return pixel_aspect_;
|
||||
}
|
||||
|
||||
const TimeRange &time() const
|
||||
{
|
||||
return time_;
|
||||
}
|
||||
|
||||
void set_time(const TimeRange &time)
|
||||
{
|
||||
time_ = time;
|
||||
}
|
||||
QVector2D square_resolution() const { return video_params_.square_resolution(); }
|
||||
QVector2D nonsquare_resolution() const { return video_params_.resolution(); }
|
||||
const VideoParams &vparams() const { return video_params_; }
|
||||
const TimeRange &time() const { return time_; }
|
||||
|
||||
private:
|
||||
QVector2D resolution_;
|
||||
|
||||
rational pixel_aspect_;
|
||||
|
||||
QVector2D resolution_by_par_;
|
||||
|
||||
VideoParams video_params_;
|
||||
TimeRange time_;
|
||||
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace olive {
|
||||
|
||||
const QString ChromaKeyNode::kColorInput = QStringLiteral("color_key");
|
||||
const QString ChromaKeyNode::kMaskOnlyInput = QStringLiteral("mask_only_in");
|
||||
const QString ChromaKeyNode::kInvertInput = QStringLiteral("invert_in");
|
||||
const QString ChromaKeyNode::kUpperToleranceInput = QStringLiteral("upper_tolerence_in");
|
||||
const QString ChromaKeyNode::kLowerToleranceInput = QStringLiteral("lower_tolerence_in");
|
||||
const QString ChromaKeyNode::kGarbageMatteInput = QStringLiteral("garbage_in");
|
||||
@@ -59,6 +60,8 @@ ChromaKeyNode::ChromaKeyNode()
|
||||
SetInputProperty(kShadowsInput, QStringLiteral("min"), 0.0);
|
||||
SetInputProperty(kShadowsInput, QStringLiteral("base"), 0.1);
|
||||
|
||||
AddInput(kInvertInput, NodeValue::kBoolean, false);
|
||||
|
||||
AddInput(kMaskOnlyInput, NodeValue::kBoolean, false);
|
||||
}
|
||||
|
||||
@@ -93,6 +96,7 @@ void ChromaKeyNode::Retranslate()
|
||||
SetInputName(kHighlightsInput, tr("Highlights"));
|
||||
SetInputName(kUpperToleranceInput, tr("Upper Tolerance"));
|
||||
SetInputName(kLowerToleranceInput, tr("Lower Tolerance"));
|
||||
SetInputName(kInvertInput, tr("Invert Mask"));
|
||||
SetInputName(kMaskOnlyInput, tr("Show Mask Only"));
|
||||
}
|
||||
|
||||
@@ -128,16 +132,17 @@ void ChromaKeyNode::GenerateProcessor()
|
||||
|
||||
void ChromaKeyNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
if (value[kTextureInput].toTexture() && processor()) {
|
||||
ColorTransformJob job;
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
if (processor()) {
|
||||
ColorTransformJob job(value);
|
||||
|
||||
job.Insert(value);
|
||||
job.SetColorProcessor(processor());
|
||||
job.SetInputTexture(value[kTextureInput].toTexture());
|
||||
job.SetNeedsCustomShader(this);
|
||||
job.SetFunctionName(QStringLiteral("SceneLinearToCIEXYZ_d65"));
|
||||
job.SetColorProcessor(processor());
|
||||
job.SetInputTexture(value[kTextureInput]);
|
||||
job.SetNeedsCustomShader(this);
|
||||
job.SetFunctionName(QStringLiteral("SceneLinearToCIEXYZ_d65"));
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ class ChromaKeyNode : public OCIOBaseNode {
|
||||
virtual void ConfigChanged() override;
|
||||
|
||||
static const QString kColorInput;
|
||||
static const QString kInvertInput;
|
||||
static const QString kMaskOnlyInput;
|
||||
static const QString kUpperToleranceInput;
|
||||
static const QString kLowerToleranceInput;
|
||||
|
||||
@@ -93,12 +93,11 @@ ShaderCode ColorDifferenceKeyNode::GetShaderCode(const ShaderRequest &request) c
|
||||
|
||||
void ColorDifferenceKeyNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
job.Insert(value);
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (job.Get(kTextureInput).toTexture()) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
if (TexturePtr tex = value[kTextureInput].toTexture()) {
|
||||
ShaderJob job;
|
||||
job.Insert(value);
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,8 +91,8 @@ void DespillNode::Value(const NodeValueRow &value, const NodeGlobals &globals, N
|
||||
NodeValue(NodeValue::kVec3, QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2])));
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (job.Get(kTextureInput).toTexture()) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
if (TexturePtr tex = job.Get(kTextureInput).toTexture()) {
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
||||
}
|
||||
} else if (pairing == kPairTextureMatrix) {
|
||||
// Only allow matrix multiplication
|
||||
const QVector2D &sequence_res = globals.resolution();
|
||||
const QVector2D &sequence_res = globals.nonsquare_resolution();
|
||||
QVector2D texture_res(texture->params().width() * texture->pixel_aspect_ratio().toDouble(), texture->params().height());
|
||||
|
||||
QMatrix4x4 adjusted_matrix = TransformDistortNode::AdjustMatrixByResolutions(number_val.toMatrix(),
|
||||
@@ -380,7 +380,7 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
||||
output->Push(texture_val);
|
||||
} else {
|
||||
// Push shader job
|
||||
output->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
output->Push(NodeValue::kTexture, Texture::Job(globals.vparams(), job), this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -410,7 +410,7 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
||||
|
||||
output->Push(NodeValue::kSamples, QVariant::fromValue(buffer), this);
|
||||
} else {
|
||||
SampleJob job(val_a.type() == NodeValue::kSamples ? val_a : val_b);
|
||||
SampleJob job(globals.time(), val_a.type() == NodeValue::kSamples ? val_a : val_b);
|
||||
job.Insert(number_param, NodeValue(NodeValue::kFloat, number, this));
|
||||
output->Push(NodeValue::kSamples, QVariant::fromValue(job), this);
|
||||
}
|
||||
|
||||
@@ -76,21 +76,19 @@ ShaderCode MergeNode::GetShaderCode(const ShaderRequest &request) const
|
||||
|
||||
void MergeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
job.Insert(value);
|
||||
|
||||
TexturePtr base_tex = job.Get(kBaseIn).toTexture();
|
||||
TexturePtr blend_tex = job.Get(kBlendIn).toTexture();
|
||||
TexturePtr base_tex = value[kBaseIn].toTexture();
|
||||
TexturePtr blend_tex = value[kBlendIn].toTexture();
|
||||
|
||||
if (base_tex || blend_tex) {
|
||||
if (!base_tex || (blend_tex && blend_tex->channel_count() < VideoParams::kRGBAChannelCount)) {
|
||||
// We only have a blend texture or the blend texture is RGB only, no need to alpha over
|
||||
table->Push(job.Get(kBlendIn));
|
||||
table->Push(value[kBlendIn]);
|
||||
} else if (!blend_tex) {
|
||||
// We only have a base texture, no need to alpha over
|
||||
table->Push(job.Get(kBaseIn));
|
||||
table->Push(value[kBaseIn]);
|
||||
} else {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
table->Push(NodeValue::kTexture, base_tex->toJob(ShaderJob(value)), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,9 +265,7 @@ void Footage::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
|
||||
// Push each stream as a footage job
|
||||
for (int i=0; i<GetTotalStreamCount(); i++) {
|
||||
Track::Reference ref = GetReferenceFromRealIndex(i);
|
||||
FootageJob job(decoder_, filename(), ref.type(), GetLength());
|
||||
|
||||
NodeValue::Type type;
|
||||
FootageJob job(globals.time(), decoder_, filename(), ref.type(), GetLength());
|
||||
|
||||
if (ref.type() == Track::kVideo) {
|
||||
VideoParams vp = GetVideoParams(ref.index());
|
||||
@@ -275,18 +273,25 @@ void Footage::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
|
||||
// Ensure the colorspace is valid and not empty
|
||||
vp.set_colorspace(GetColorspaceToUse(vp));
|
||||
|
||||
// Adjust footage job's divider
|
||||
if (globals.vparams().divider() > 1) {
|
||||
// Use a divider appropriate for this target resolution
|
||||
vp.set_divider(VideoParams::GetDividerForTargetResolution(vp.width(), vp.height(), globals.vparams().effective_width(), globals.vparams().effective_height()));
|
||||
} else {
|
||||
// Render everything at full res
|
||||
vp.set_divider(1);
|
||||
}
|
||||
|
||||
job.set_video_params(vp);
|
||||
|
||||
type = NodeValue::kTexture;
|
||||
table->Push(NodeValue::kTexture, Texture::Job(vp, job), this, ref.ToString());
|
||||
} else {
|
||||
AudioParams ap = GetAudioParams(ref.index());
|
||||
job.set_audio_params(ap);
|
||||
job.set_cache_path(project()->cache_path());
|
||||
|
||||
type = NodeValue::kSamples;
|
||||
table->Push(NodeValue::kSamples, QVariant::fromValue(job), this, ref.ToString());
|
||||
}
|
||||
|
||||
table->Push(type, QVariant::fromValue(job), this, ref.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+142
-166
@@ -61,7 +61,15 @@ NodeValueRow NodeTraverser::GenerateRow(NodeValueDatabase *database, const Node
|
||||
row.insert(it.key(), value);
|
||||
}
|
||||
|
||||
PreProcessRow(range, row);
|
||||
// TEMP: Audio needs to be refactored to work with new job system. But refactoring hasn't been
|
||||
// done yet, so we emulate old behavior here JUST FOR AUDIO.
|
||||
for (auto it=row.begin(); it!=row.end(); it++) {
|
||||
NodeValue &val = it.value();
|
||||
if (val.type() == NodeValue::kSamples) {
|
||||
ResolveJobs(val);
|
||||
}
|
||||
}
|
||||
// END TEMP
|
||||
|
||||
return row;
|
||||
}
|
||||
@@ -109,14 +117,16 @@ NodeValue NodeTraverser::GenerateRowValueElement(const Node *node, const QString
|
||||
|
||||
NodeValue value = table->TakeAt(value_index);
|
||||
|
||||
if (value.type() == NodeValue::kTexture) {
|
||||
QMutexLocker locker(node->video_frame_cache()->mutex());
|
||||
if (value.type() == NodeValue::kTexture && UseCache()) {
|
||||
if (TexturePtr tex = value.toTexture()) {
|
||||
QMutexLocker locker(node->video_frame_cache()->mutex());
|
||||
|
||||
node->video_frame_cache()->LoadState();
|
||||
node->video_frame_cache()->LoadState();
|
||||
|
||||
QString cache = node->video_frame_cache()->GetValidCacheFilename(time.in());
|
||||
if (!cache.isEmpty()) {
|
||||
value.set_value(CacheJob(cache, value.data()));
|
||||
QString cache = node->video_frame_cache()->GetValidCacheFilename(time.in());
|
||||
if (!cache.isEmpty()) {
|
||||
value.set_value(tex->toJob(CacheJob(cache, value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,26 +181,7 @@ void NodeTraverser::Transform(QTransform *transform, const Node *start, const No
|
||||
|
||||
NodeGlobals NodeTraverser::GenerateGlobals(const VideoParams ¶ms, const TimeRange &time)
|
||||
{
|
||||
return NodeGlobals(QVector2D(params.width(), params.height()), params.pixel_aspect_ratio(), time);
|
||||
}
|
||||
|
||||
int NodeTraverser::GetChannelCountFromJob(const GenerateJob &job)
|
||||
{
|
||||
return VideoParams::kRGBAChannelCount;
|
||||
}
|
||||
|
||||
TexturePtr NodeTraverser::GetMainTextureFromJob(const GenerateJob &job)
|
||||
{
|
||||
// FIXME: Should probably take Node::GetEffectInput into account here
|
||||
for (auto it=job.GetValues().cbegin(); it!=job.GetValues().cend(); it++) {
|
||||
if (it.value().type() == NodeValue::kTexture) {
|
||||
if (TexturePtr t = it.value().toTexture()) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return NodeGlobals(params, time);
|
||||
}
|
||||
|
||||
NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& input, const TimeRange& range)
|
||||
@@ -284,7 +275,13 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang
|
||||
// NOTE: Times how long a node takes to process, useful for profiling.
|
||||
//GTTTime gtt(n);Q_UNUSED(gtt);
|
||||
|
||||
// FIXME: Cache certain values here if we've already processed them before
|
||||
// Use table cache to skip processing where available
|
||||
if (value_cache_.contains(n)) {
|
||||
QHash<TimeRange, NodeValueTable> &node_value_map = value_cache_[n];
|
||||
if (node_value_map.contains(range)) {
|
||||
return node_value_map.value(range);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate row for node
|
||||
NodeValueDatabase database = GenerateDatabase(n, range);
|
||||
@@ -298,11 +295,13 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang
|
||||
is_enabled = database[Node::kEnabledInput].Get(NodeValue::kBoolean).toBool();
|
||||
}
|
||||
|
||||
NodeValueTable table;
|
||||
|
||||
if (is_enabled) {
|
||||
NodeValueRow row = GenerateRow(&database, n, range);
|
||||
|
||||
// Generate output table
|
||||
NodeValueTable table = database.Merge();
|
||||
table = database.Merge();
|
||||
|
||||
// By this point, the node should have all the inputs it needs to render correctly
|
||||
NodeGlobals globals = GenerateGlobals(video_params_, range);
|
||||
@@ -323,8 +322,6 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang
|
||||
transform_now_ = next_node;
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
} else {
|
||||
// If this node has an effect input, ensure that is pushed last
|
||||
NodeValueTable primary;
|
||||
@@ -332,13 +329,16 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang
|
||||
primary = database.Take(n->GetEffectInputID());
|
||||
}
|
||||
|
||||
NodeValueTable m = database.Merge();
|
||||
m.Push(primary);
|
||||
return m;
|
||||
table = database.Merge();
|
||||
table.Push(primary);
|
||||
}
|
||||
|
||||
value_cache_[n][range] = table;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
TexturePtr NodeTraverser::ProcessVideoCacheJob(const CacheJob &val)
|
||||
TexturePtr NodeTraverser::ProcessVideoCacheJob(const CacheJob *val)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -348,153 +348,129 @@ QVector2D NodeTraverser::GenerateResolution() const
|
||||
return QVector2D(video_params_.square_pixel_width(), video_params_.height());
|
||||
}
|
||||
|
||||
void NodeTraverser::ResolveJobs(NodeValue &val, const TimeRange &range)
|
||||
void NodeTraverser::ResolveJobs(NodeValue &val)
|
||||
{
|
||||
if (val.type() == NodeValue::kTexture || val.type() == NodeValue::kSamples) {
|
||||
if (val.canConvert<CacheJob>()) {
|
||||
CacheJob job = val.value<CacheJob>();
|
||||
TexturePtr tex = ProcessVideoCacheJob(job);
|
||||
if (tex) {
|
||||
val.set_value(tex);
|
||||
} else {
|
||||
val.set_value(job.GetFallback());
|
||||
if (val.type() == NodeValue::kTexture) {
|
||||
|
||||
if (TexturePtr job_tex = val.toTexture()) {
|
||||
if (AcceleratedJob *base_job = job_tex->job()) {
|
||||
|
||||
if (resolved_texture_cache_.contains(job_tex.get())) {
|
||||
val.set_value(resolved_texture_cache_.value(job_tex.get()));
|
||||
} else {
|
||||
// Resolve any sub-jobs
|
||||
for (auto it=base_job->GetValues().begin(); it!=base_job->GetValues().end(); it++) {
|
||||
// Jobs will almost always be submitted with one of these types
|
||||
NodeValue &subval = it.value();
|
||||
ResolveJobs(subval);
|
||||
}
|
||||
|
||||
if (CacheJob *cj = dynamic_cast<CacheJob*>(base_job)) {
|
||||
TexturePtr tex = ProcessVideoCacheJob(cj);
|
||||
if (tex) {
|
||||
val.set_value(tex);
|
||||
} else {
|
||||
val.set_value(cj->GetFallback());
|
||||
}
|
||||
|
||||
} else if (ColorTransformJob *ctj = dynamic_cast<ColorTransformJob*>(base_job)) {
|
||||
|
||||
VideoParams ctj_params = job_tex->params();
|
||||
|
||||
ctj_params.set_format(GetCacheVideoParams().format());
|
||||
|
||||
TexturePtr dest = CreateTexture(ctj_params);
|
||||
|
||||
// Resolve input texture
|
||||
NodeValue v = ctj->GetInputTexture();
|
||||
ResolveJobs(v);
|
||||
ctj->SetInputTexture(v);
|
||||
|
||||
ProcessColorTransform(dest, val.source(), ctj);
|
||||
|
||||
val.set_value(dest);
|
||||
|
||||
} else if (ShaderJob *sj = dynamic_cast<ShaderJob*>(base_job)) {
|
||||
|
||||
VideoParams tex_params = job_tex->params();
|
||||
|
||||
TexturePtr tex = CreateTexture(tex_params);
|
||||
|
||||
ProcessShader(tex, val.source(), sj);
|
||||
|
||||
val.set_value(tex);
|
||||
|
||||
} else if (GenerateJob *gj = dynamic_cast<GenerateJob*>(base_job)) {
|
||||
|
||||
VideoParams tex_params = job_tex->params();
|
||||
|
||||
TexturePtr tex = CreateTexture(tex_params);
|
||||
|
||||
ProcessFrameGeneration(tex, val.source(), gj);
|
||||
|
||||
// Convert to reference space
|
||||
const QString &colorspace = tex_params.colorspace();
|
||||
if (!colorspace.isEmpty()) {
|
||||
// Set format to primary format
|
||||
tex_params.set_format(GetCacheVideoParams().format());
|
||||
|
||||
TexturePtr dest = CreateTexture(tex_params);
|
||||
|
||||
ConvertToReferenceSpace(dest, tex, colorspace);
|
||||
|
||||
tex = dest;
|
||||
}
|
||||
|
||||
val.set_value(tex);
|
||||
|
||||
} else if (FootageJob *fj = dynamic_cast<FootageJob*>(base_job)) {
|
||||
|
||||
rational footage_time = Footage::AdjustTimeByLoopMode(fj->time().in(), loop_mode_, fj->length(), fj->video_params().video_type(), fj->video_params().frame_rate_as_time_base());
|
||||
|
||||
TexturePtr tex;
|
||||
|
||||
if (footage_time.isNaN()) {
|
||||
// Push dummy texture
|
||||
tex = CreateDummyTexture(fj->video_params());
|
||||
} else {
|
||||
VideoParams managed_params = fj->video_params();
|
||||
managed_params.set_format(GetCacheVideoParams().format());
|
||||
|
||||
tex = CreateTexture(managed_params);
|
||||
ProcessVideoFootage(tex, fj, footage_time);
|
||||
}
|
||||
|
||||
val.set_value(tex);
|
||||
|
||||
}
|
||||
|
||||
// Cache resolved value
|
||||
resolved_texture_cache_.insert(job_tex.get(), val.toTexture());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (val.canConvert<ShaderJob>()) {
|
||||
} else if (val.type() == NodeValue::kSamples) {
|
||||
|
||||
ShaderJob job = val.value<ShaderJob>();
|
||||
if (val.canConvert<SampleJob>()) {
|
||||
|
||||
PreProcessRow(range, job.GetValues());
|
||||
|
||||
VideoParams tex_params = GetCacheVideoParams();
|
||||
tex_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
|
||||
if (!job.GetWillChangeImageSize()) {
|
||||
if (TexturePtr texture = GetMainTextureFromJob(job)) {
|
||||
tex_params.set_width(texture->params().width());
|
||||
tex_params.set_height(texture->params().height());
|
||||
tex_params.set_divider(texture->params().divider());
|
||||
}
|
||||
}
|
||||
|
||||
TexturePtr tex = CreateTexture(tex_params);
|
||||
|
||||
ProcessShader(tex, val.source(), range, job);
|
||||
|
||||
val.set_value(tex);
|
||||
|
||||
} else if (val.canConvert<GenerateJob>()) {
|
||||
|
||||
GenerateJob job = val.value<GenerateJob>();
|
||||
|
||||
VideoParams tex_params = GetCacheVideoParams();
|
||||
tex_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
|
||||
VideoParams upload_params = tex_params;
|
||||
if (job.GetRequestedFormat() != VideoParams::kFormatInvalid) {
|
||||
upload_params.set_format(job.GetRequestedFormat());
|
||||
}
|
||||
|
||||
TexturePtr tex = CreateTexture(upload_params);
|
||||
|
||||
PreProcessRow(range, job.GetValues());
|
||||
ProcessFrameGeneration(tex, val.source(), job);
|
||||
|
||||
if (!job.GetColorspace().isEmpty()) {
|
||||
// Convert to reference space
|
||||
TexturePtr dest = CreateTexture(tex_params);
|
||||
|
||||
ConvertToReferenceSpace(dest, tex, job.GetColorspace());
|
||||
|
||||
tex = dest;
|
||||
}
|
||||
|
||||
val.set_value(tex);
|
||||
|
||||
} else if (val.canConvert<ColorTransformJob>()) {
|
||||
|
||||
ColorTransformJob job = val.value<ColorTransformJob>();
|
||||
|
||||
VideoParams src_params = job.GetInputTexture()->params();
|
||||
src_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
|
||||
TexturePtr dest = CreateTexture(src_params);
|
||||
|
||||
ProcessColorTransform(dest, val.source(), job);
|
||||
|
||||
val.set_value(dest);
|
||||
SampleJob job = val.value<SampleJob>();
|
||||
SampleBuffer output_buffer = CreateSampleBuffer(job.samples().audio_params(), job.samples().sample_count());
|
||||
ProcessSamples(output_buffer, val.source(), job.time(), job);
|
||||
val.set_value(QVariant::fromValue(output_buffer));
|
||||
|
||||
} else if (val.canConvert<FootageJob>()) {
|
||||
|
||||
FootageJob job = val.value<FootageJob>();
|
||||
|
||||
if (job.type() == Track::kVideo) {
|
||||
|
||||
rational footage_time = Footage::AdjustTimeByLoopMode(range.in(), loop_mode_, job.length(), job.video_params().video_type(), job.video_params().frame_rate_as_time_base());
|
||||
|
||||
TexturePtr tex;
|
||||
|
||||
// Adjust footage job's divider
|
||||
VideoParams render_params = GetCacheVideoParams();
|
||||
VideoParams job_params = job.video_params();
|
||||
|
||||
if (render_params.divider() > 1) {
|
||||
// Use a divider appropriate for this target resolution
|
||||
job_params.set_divider(VideoParams::GetDividerForTargetResolution(job_params.width(), job_params.height(), render_params.effective_width(), render_params.effective_height()));
|
||||
} else {
|
||||
// Render everything at full res
|
||||
job_params.set_divider(1);
|
||||
}
|
||||
|
||||
job.set_video_params(job_params);
|
||||
|
||||
if (footage_time.isNaN()) {
|
||||
// Push dummy texture
|
||||
tex = CreateDummyTexture(job.video_params());
|
||||
} else {
|
||||
VideoParams managed_params = job.video_params();
|
||||
managed_params.set_format(GetCacheVideoParams().format());
|
||||
|
||||
tex = CreateTexture(managed_params);
|
||||
ProcessVideoFootage(tex, job, footage_time);
|
||||
}
|
||||
|
||||
val.set_value(tex);
|
||||
|
||||
} else if (job.type() == Track::kAudio) {
|
||||
|
||||
SampleBuffer buffer = CreateSampleBuffer(GetCacheAudioParams(), range.length());
|
||||
ProcessAudioFootage(buffer, job, range);
|
||||
val.set_value(buffer);
|
||||
|
||||
}
|
||||
|
||||
} else if (val.canConvert<SampleJob>()) {
|
||||
|
||||
SampleJob job = val.value<SampleJob>();
|
||||
SampleBuffer output_buffer = CreateSampleBuffer(job.samples().audio_params(), job.samples().sample_count());
|
||||
ProcessSamples(output_buffer, val.source(), range, job);
|
||||
val.set_value(QVariant::fromValue(output_buffer));
|
||||
SampleBuffer buffer = CreateSampleBuffer(GetCacheAudioParams(), job.time().length());
|
||||
ProcessAudioFootage(buffer, &job, job.time());
|
||||
val.set_value(buffer);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NodeTraverser::PreProcessRow(const TimeRange &range, NodeValueRow &row)
|
||||
{
|
||||
QByteArray cached_node_hash;
|
||||
|
||||
// Resolve any jobs
|
||||
for (auto it=row.begin(); it!=row.end(); it++) {
|
||||
// Jobs will almost always be submitted with one of these types
|
||||
NodeValue &val = it.value();
|
||||
|
||||
ResolveJobs(val, range);
|
||||
}
|
||||
}
|
||||
|
||||
TexturePtr NodeTraverser::CreateDummyTexture(const VideoParams &p)
|
||||
{
|
||||
return std::make_shared<Texture>(p);
|
||||
|
||||
+13
-13
@@ -80,30 +80,26 @@ public:
|
||||
audio_params_ = params;
|
||||
}
|
||||
|
||||
static int GetChannelCountFromJob(const GenerateJob& job);
|
||||
|
||||
static TexturePtr GetMainTextureFromJob(const GenerateJob& job);
|
||||
|
||||
protected:
|
||||
NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range);
|
||||
|
||||
void ProcessInputElement(NodeValueTableArray &array_tbl, const Node *node, const QString &input, int element, const TimeRange &range);
|
||||
|
||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time){}
|
||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob *stream, const rational &input_time){}
|
||||
|
||||
virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time){}
|
||||
virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob *stream, const TimeRange &input_time){}
|
||||
|
||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job){}
|
||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const ShaderJob *job){}
|
||||
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job){}
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job){}
|
||||
|
||||
virtual void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job){}
|
||||
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job){}
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob *job){}
|
||||
|
||||
virtual void ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs){}
|
||||
|
||||
virtual TexturePtr ProcessVideoCacheJob(const CacheJob &val);
|
||||
virtual TexturePtr ProcessVideoCacheJob(const CacheJob *val);
|
||||
|
||||
virtual TexturePtr CreateTexture(const VideoParams &p)
|
||||
{
|
||||
@@ -140,7 +136,8 @@ protected:
|
||||
CancelAtom *GetCancelPointer() const { return cancel_; }
|
||||
void SetCancelPointer(CancelAtom *cancel) { cancel_ = cancel; }
|
||||
|
||||
void ResolveJobs(NodeValue &value, const TimeRange &range);
|
||||
void ResolveJobs(NodeValue &value);
|
||||
void ResolveAudioJobs(NodeValue &value);
|
||||
|
||||
Block *GetCurrentBlock() const
|
||||
{
|
||||
@@ -149,9 +146,9 @@ protected:
|
||||
|
||||
Decoder::LoopMode loop_mode() const { return loop_mode_; }
|
||||
|
||||
private:
|
||||
void PreProcessRow(const TimeRange &range, NodeValueRow &row);
|
||||
virtual bool UseCache() const { return false; }
|
||||
|
||||
private:
|
||||
TexturePtr CreateDummyTexture(const VideoParams &p);
|
||||
|
||||
VideoParams video_params_;
|
||||
@@ -168,6 +165,9 @@ private:
|
||||
|
||||
Decoder::LoopMode loop_mode_;
|
||||
|
||||
QHash<const Node*, QHash<TimeRange, NodeValueTable> > value_cache_;
|
||||
QHash<Texture*, TexturePtr> resolved_texture_cache_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -268,6 +268,11 @@ public:
|
||||
return type_ == rhs.type_ && tag_ == rhs.tag_ && data_ == rhs.data_;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return !data_.isNull();
|
||||
}
|
||||
|
||||
static QString GetPrettyDataTypeName(Type type);
|
||||
|
||||
static QString GetDataTypeName(Type type);
|
||||
|
||||
@@ -26,10 +26,13 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class AcceleratedJob {
|
||||
class AcceleratedJob
|
||||
{
|
||||
public:
|
||||
AcceleratedJob() = default;
|
||||
|
||||
virtual ~AcceleratedJob(){}
|
||||
|
||||
NodeValue Get(const QString& input) const
|
||||
{
|
||||
return value_map_.value(input);
|
||||
|
||||
@@ -24,13 +24,16 @@
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
#include "node/value.h"
|
||||
#include "render/job/acceleratedjob.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class CacheJob
|
||||
class CacheJob : public AcceleratedJob
|
||||
{
|
||||
public:
|
||||
CacheJob() = default;
|
||||
CacheJob(const QString &filename, const QVariant &fallback = QVariant())
|
||||
CacheJob(const QString &filename, const NodeValue &fallback = NodeValue())
|
||||
{
|
||||
filename_ = filename;
|
||||
}
|
||||
@@ -38,18 +41,16 @@ public:
|
||||
const QString &GetFilename() const { return filename_; }
|
||||
void SetFilename(const QString &s) { filename_ = s; }
|
||||
|
||||
const QVariant &GetFallback() const { return fallback_; }
|
||||
void SetFallback(const QVariant &val) { fallback_ = val; }
|
||||
const NodeValue &GetFallback() const { return fallback_; }
|
||||
void SetFallback(const NodeValue &val) { fallback_ = val; }
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
|
||||
QVariant fallback_;
|
||||
NodeValue fallback_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::CacheJob)
|
||||
|
||||
#endif // CACHEJOB_H
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <QMatrix4x4>
|
||||
#include <QString>
|
||||
|
||||
#include "render/job/generatejob.h"
|
||||
#include "acceleratedjob.h"
|
||||
#include "render/alphaassoc.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "render/texture.h"
|
||||
@@ -33,18 +33,23 @@ namespace olive {
|
||||
|
||||
class Node;
|
||||
|
||||
class ColorTransformJob : public GenerateJob
|
||||
class ColorTransformJob : public AcceleratedJob
|
||||
{
|
||||
public:
|
||||
ColorTransformJob()
|
||||
{
|
||||
processor_ = nullptr;
|
||||
input_texture_ = nullptr;
|
||||
custom_shader_src_ = nullptr;
|
||||
input_alpha_association_ = kAlphaNone;
|
||||
clear_destination_ = true;
|
||||
}
|
||||
|
||||
ColorTransformJob(const NodeValueRow &row) :
|
||||
ColorTransformJob()
|
||||
{
|
||||
Insert(row);
|
||||
}
|
||||
|
||||
QString id() const
|
||||
{
|
||||
if (id_.isEmpty()) {
|
||||
@@ -56,8 +61,13 @@ public:
|
||||
|
||||
void SetOverrideID(const QString &id) { id_ = id; }
|
||||
|
||||
TexturePtr GetInputTexture() const { return input_texture_; }
|
||||
void SetInputTexture(TexturePtr tex) { input_texture_ = tex; }
|
||||
const NodeValue &GetInputTexture() const { return input_texture_; }
|
||||
void SetInputTexture(const NodeValue &tex) { input_texture_ = tex; }
|
||||
void SetInputTexture(TexturePtr tex)
|
||||
{
|
||||
Q_ASSERT(!tex->IsDummy());
|
||||
input_texture_ = NodeValue(NodeValue::kTexture, tex);
|
||||
}
|
||||
|
||||
ColorProcessorPtr GetColorProcessor() const { return processor_; }
|
||||
void SetColorProcessor(ColorProcessorPtr p) { processor_ = p; }
|
||||
@@ -89,7 +99,7 @@ private:
|
||||
ColorProcessorPtr processor_;
|
||||
QString id_;
|
||||
|
||||
TexturePtr input_texture_;
|
||||
NodeValue input_texture_;
|
||||
|
||||
const Node *custom_shader_src_;
|
||||
QString custom_shader_id_;
|
||||
@@ -108,6 +118,4 @@ private:
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::ColorTransformJob)
|
||||
|
||||
#endif // COLORTRANSFORMJOB_H
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class FootageJob
|
||||
class FootageJob : public AcceleratedJob
|
||||
{
|
||||
public:
|
||||
FootageJob() :
|
||||
@@ -33,7 +33,8 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
FootageJob(const QString& decoder, const QString& filename, Track::Type type, const rational& length) :
|
||||
FootageJob(const TimeRange &time, const QString& decoder, const QString& filename, Track::Type type, const rational& length) :
|
||||
time_(time),
|
||||
decoder_(decoder),
|
||||
filename_(filename),
|
||||
type_(type),
|
||||
@@ -96,7 +97,11 @@ public:
|
||||
length_ = length;
|
||||
}
|
||||
|
||||
const TimeRange &time() const { return time_; }
|
||||
|
||||
private:
|
||||
TimeRange time_;
|
||||
|
||||
QString decoder_;
|
||||
|
||||
QString filename_;
|
||||
|
||||
@@ -22,33 +22,22 @@
|
||||
#define GENERATEJOB_H
|
||||
|
||||
#include "acceleratedjob.h"
|
||||
#include "render/videoparams.h"
|
||||
#include "codec/frame.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class GenerateJob : public AcceleratedJob {
|
||||
class GenerateJob : public AcceleratedJob
|
||||
{
|
||||
public:
|
||||
GenerateJob()
|
||||
GenerateJob() = default;
|
||||
GenerateJob(const NodeValueRow &row) :
|
||||
GenerateJob()
|
||||
{
|
||||
requested_format_ = VideoParams::kFormatInvalid;
|
||||
Insert(row);
|
||||
}
|
||||
|
||||
VideoParams::Format GetRequestedFormat() const { return requested_format_; }
|
||||
|
||||
void SetRequestedFormat(VideoParams::Format f) { requested_format_ = f; }
|
||||
|
||||
const QString &GetColorspace() const { return colorspace_; }
|
||||
void SetColorspace(const QString &s) { colorspace_ = s; }
|
||||
|
||||
private:
|
||||
VideoParams::Format requested_format_;
|
||||
|
||||
QString colorspace_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::GenerateJob)
|
||||
|
||||
#endif // GENERATEJOB_H
|
||||
|
||||
@@ -23,23 +23,27 @@
|
||||
|
||||
#include "acceleratedjob.h"
|
||||
#include "codec/samplebuffer.h"
|
||||
#include "common/timerange.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class SampleJob : public AcceleratedJob {
|
||||
class SampleJob : public AcceleratedJob
|
||||
{
|
||||
public:
|
||||
SampleJob()
|
||||
{
|
||||
}
|
||||
|
||||
SampleJob(const NodeValue& value)
|
||||
SampleJob(const TimeRange &time, const NodeValue& value)
|
||||
{
|
||||
samples_ = value.toSamples();
|
||||
time_ = time;
|
||||
}
|
||||
|
||||
SampleJob(const QString& from, const NodeValueRow& row)
|
||||
SampleJob(const TimeRange &time, const QString& from, const NodeValueRow& row)
|
||||
{
|
||||
samples_ = row[from].toSamples();
|
||||
time_ = time;
|
||||
}
|
||||
|
||||
const SampleBuffer &samples() const
|
||||
@@ -52,9 +56,13 @@ public:
|
||||
return samples_.is_allocated();
|
||||
}
|
||||
|
||||
const TimeRange &time() const { return time_; }
|
||||
|
||||
private:
|
||||
SampleBuffer samples_;
|
||||
|
||||
TimeRange time_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -24,19 +24,24 @@
|
||||
#include <QMatrix4x4>
|
||||
#include <QVector>
|
||||
|
||||
#include "generatejob.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "acceleratedjob.h"
|
||||
#include "render/texture.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ShaderJob : public GenerateJob {
|
||||
class ShaderJob : public AcceleratedJob
|
||||
{
|
||||
public:
|
||||
ShaderJob()
|
||||
{
|
||||
iterations_ = 1;
|
||||
iterative_input_ = nullptr;
|
||||
will_change_image_size_ = true;
|
||||
}
|
||||
|
||||
ShaderJob(const NodeValueRow &row) :
|
||||
ShaderJob()
|
||||
{
|
||||
Insert(row);
|
||||
}
|
||||
|
||||
const QString& GetShaderID() const
|
||||
@@ -100,9 +105,6 @@ public:
|
||||
return vertex_overrides_;
|
||||
}
|
||||
|
||||
bool GetWillChangeImageSize() const { return will_change_image_size_; }
|
||||
void SetWillChangeImageSize(bool e) { will_change_image_size_ = e; }
|
||||
|
||||
private:
|
||||
QString shader_id_;
|
||||
|
||||
@@ -114,12 +116,8 @@ private:
|
||||
|
||||
QVector<float> vertex_overrides_;
|
||||
|
||||
bool will_change_image_size_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::ShaderJob)
|
||||
|
||||
#endif // SHADERJOB_H
|
||||
|
||||
@@ -364,7 +364,7 @@ void OpenGLRenderer::Flush()
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
functions_->glFlush();
|
||||
functions_->glFinish();
|
||||
}
|
||||
|
||||
Color OpenGLRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt)
|
||||
@@ -415,6 +415,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
// This variable is used in the shader, let's set it
|
||||
const NodeValue& value = it.value();
|
||||
|
||||
// Arrays are not currently supported in this system
|
||||
if (value.array()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,16 @@ void PlaybackCache::LoadState()
|
||||
{
|
||||
QDir cache_dir = GetThisCacheDirectory();
|
||||
QFile f(cache_dir.filePath(QStringLiteral("state")));
|
||||
if (f.open(QFile::ReadOnly)) {
|
||||
|
||||
if (!f.exists()) {
|
||||
// No state exists, assume nothing valid
|
||||
validated_.clear();
|
||||
passthroughs_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
qint64 file_time = f.fileTime(QFileDevice::FileModificationTime).toMSecsSinceEpoch();
|
||||
if (file_time > last_loaded_state_ && f.open(QFile::ReadOnly)) {
|
||||
QDataStream s(&f);
|
||||
|
||||
uint32_t version;
|
||||
@@ -81,7 +90,6 @@ void PlaybackCache::LoadState()
|
||||
{
|
||||
int valid_count, pass_count;
|
||||
|
||||
validated_.clear();
|
||||
s >> valid_count;
|
||||
for (int i=0; i<valid_count; i++) {
|
||||
int in_num, in_den, out_num, out_den;
|
||||
@@ -94,7 +102,6 @@ void PlaybackCache::LoadState()
|
||||
validated_.insert(TimeRange(rational(in_num, in_den), rational(out_num, out_den)));
|
||||
}
|
||||
|
||||
passthroughs_.clear();
|
||||
s >> pass_count;
|
||||
for (int i=0; i<pass_count; i++) {
|
||||
QUuid id;
|
||||
@@ -116,6 +123,8 @@ void PlaybackCache::LoadState()
|
||||
}
|
||||
|
||||
f.close();
|
||||
|
||||
last_loaded_state_ = file_time;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +170,8 @@ void PlaybackCache::SaveState()
|
||||
}
|
||||
|
||||
f.close();
|
||||
|
||||
last_loaded_state_ = f.fileTime(QFileDevice::FileModificationTime).toMSecsSinceEpoch();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,7 +247,8 @@ Project *PlaybackCache::GetProject() const
|
||||
|
||||
PlaybackCache::PlaybackCache(QObject *parent) :
|
||||
QObject(parent),
|
||||
saving_enabled_(true)
|
||||
saving_enabled_(true),
|
||||
last_loaded_state_(0)
|
||||
{
|
||||
uuid_ = QUuid::createUuid();
|
||||
}
|
||||
|
||||
@@ -132,6 +132,8 @@ private:
|
||||
|
||||
QVector<Passthrough> passthroughs_;
|
||||
|
||||
qint64 last_loaded_state_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ void Renderer::BlitColorManaged(const ColorTransformJob &color_job, Texture *des
|
||||
}
|
||||
|
||||
ShaderJob job;
|
||||
job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(color_job.GetInputTexture())));
|
||||
job.Insert(QStringLiteral("ove_maintex"), color_job.GetInputTexture());
|
||||
job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
|
||||
job.Insert(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, color_job.GetCropMatrix().inverted()));
|
||||
job.Insert(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, int(color_job.GetInputAlphaAssociation())));
|
||||
|
||||
@@ -54,7 +54,7 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational
|
||||
|
||||
NodeValue tex_val = table.Get(NodeValue::kTexture);
|
||||
|
||||
ResolveJobs(tex_val, range);
|
||||
ResolveJobs(tex_val);
|
||||
|
||||
return tex_val.toTexture();
|
||||
}
|
||||
@@ -226,7 +226,7 @@ void RenderProcessor::Run()
|
||||
|
||||
NodeValue sample_val = table.Get(NodeValue::kSamples);
|
||||
|
||||
ResolveJobs(sample_val, time);
|
||||
ResolveJobs(sample_val);
|
||||
|
||||
SampleBuffer samples = sample_val.toSamples();
|
||||
if (samples.is_allocated()) {
|
||||
@@ -300,7 +300,7 @@ void RenderProcessor::Process(RenderTicketPtr ticket, Renderer *render_ctx, Deco
|
||||
p.Run();
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time)
|
||||
void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJob *stream, const rational &input_time)
|
||||
{
|
||||
if (ticket_->property("type").value<RenderManager::TicketType>() != RenderManager::kTypeVideo) {
|
||||
// Video cannot contribute to audio, so we do nothing here
|
||||
@@ -310,7 +310,7 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
// Check the still frame cache. On large frames such as high resolution still images, uploading
|
||||
// and color managing them for every frame is a waste of time, so we implement a small cache here
|
||||
// to optimize such a situation
|
||||
VideoParams stream_data = stream.video_params();
|
||||
VideoParams stream_data = stream->video_params();
|
||||
|
||||
ColorManager* color_manager = Node::ValueToPtr<ColorManager>(ticket_->property("colormanager"));
|
||||
|
||||
@@ -321,9 +321,9 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
qWarning() << "HAVEN'T GOTTEN DEFAULT INPUT COLORSPACE";
|
||||
}
|
||||
|
||||
Decoder::CodecStream default_codec_stream(stream.filename(), stream_data.stream_index(), GetCurrentBlock());
|
||||
Decoder::CodecStream default_codec_stream(stream->filename(), stream_data.stream_index(), GetCurrentBlock());
|
||||
|
||||
QString decoder_id = stream.decoder();
|
||||
QString decoder_id = stream->decoder();
|
||||
|
||||
DecoderPtr decoder = nullptr;
|
||||
|
||||
@@ -341,7 +341,7 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
QString frame_filename;
|
||||
|
||||
int64_t frame_number = stream_data.get_time_in_timebase_units(input_time);
|
||||
frame_filename = Decoder::TransformImageSequenceFileName(stream.filename(), frame_number);
|
||||
frame_filename = Decoder::TransformImageSequenceFileName(stream->filename(), frame_number);
|
||||
|
||||
// Decoder will close automatically since it's a stream_ptr
|
||||
decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index(), GetCurrentBlock()));
|
||||
@@ -352,11 +352,11 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
|
||||
if (decoder && render_ctx_) {
|
||||
Decoder::RetrieveVideoParams p;
|
||||
p.divider = stream.video_params().divider();
|
||||
p.divider = stream->video_params().divider();
|
||||
p.maximum_format = destination->format();
|
||||
|
||||
if (!IsCancelled()) {
|
||||
VideoParams tex_params = stream.video_params();
|
||||
VideoParams tex_params = stream->video_params();
|
||||
|
||||
if (tex_params.is_valid()) {
|
||||
TexturePtr unmanaged_texture;
|
||||
@@ -397,16 +397,16 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
}
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time)
|
||||
void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const FootageJob *stream, const TimeRange &input_time)
|
||||
{
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream.decoder(), Decoder::CodecStream(stream.filename(), stream.audio_params().stream_index(), nullptr));
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream->decoder(), Decoder::CodecStream(stream->filename(), stream->audio_params().stream_index(), nullptr));
|
||||
|
||||
if (decoder) {
|
||||
const AudioParams& audio_params = GetCacheAudioParams();
|
||||
|
||||
Decoder::RetrieveAudioStatus status = decoder->RetrieveAudio(destination,
|
||||
input_time, audio_params,
|
||||
stream.cache_path(),
|
||||
stream->cache_path(),
|
||||
loop_mode(),
|
||||
static_cast<RenderMode::Mode>(ticket_->property("mode").toInt()));
|
||||
|
||||
@@ -416,13 +416,13 @@ void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const Foota
|
||||
}
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob &job)
|
||||
void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, const ShaderJob *job)
|
||||
{
|
||||
if (!render_ctx_) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString full_shader_id = QStringLiteral("%1:%2").arg(node->id(), job.GetShaderID());
|
||||
QString full_shader_id = QStringLiteral("%1:%2").arg(node->id(), job->GetShaderID());
|
||||
|
||||
QMutexLocker locker(shader_cache_->mutex());
|
||||
|
||||
@@ -430,16 +430,20 @@ void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, co
|
||||
|
||||
if (shader.isNull()) {
|
||||
// Since we have shader code, compile it now
|
||||
shader = render_ctx_->CreateNativeShader(node->GetShaderCode(job.GetShaderID()));
|
||||
shader = render_ctx_->CreateNativeShader(node->GetShaderCode(job->GetShaderID()));
|
||||
|
||||
if (shader.isNull()) {
|
||||
// Couldn't find or build the shader required
|
||||
return;
|
||||
}
|
||||
|
||||
shader_cache_->insert(full_shader_id, shader);
|
||||
}
|
||||
|
||||
locker.unlock();
|
||||
|
||||
// Run shader
|
||||
render_ctx_->BlitToTexture(shader, job, destination.get());
|
||||
render_ctx_->BlitToTexture(shader, *job, destination.get());
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job)
|
||||
@@ -473,16 +477,16 @@ void RenderProcessor::ProcessSamples(SampleBuffer &destination, const Node *node
|
||||
}
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob &job)
|
||||
void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job)
|
||||
{
|
||||
if (!render_ctx_) {
|
||||
return;
|
||||
}
|
||||
|
||||
render_ctx_->BlitColorManaged(job, destination.get());
|
||||
render_ctx_->BlitColorManaged(*job, destination.get());
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob &job)
|
||||
void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob *job)
|
||||
{
|
||||
if (!render_ctx_) {
|
||||
return;
|
||||
@@ -493,14 +497,14 @@ void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const Node
|
||||
frame->set_video_params(destination->params());
|
||||
frame->allocate();
|
||||
|
||||
node->GenerateFrame(frame, job);
|
||||
node->GenerateFrame(frame, *job);
|
||||
|
||||
destination->Upload(frame->data(), frame->linesize_pixels());
|
||||
}
|
||||
|
||||
TexturePtr RenderProcessor::ProcessVideoCacheJob(const CacheJob &val)
|
||||
TexturePtr RenderProcessor::ProcessVideoCacheJob(const CacheJob *val)
|
||||
{
|
||||
FramePtr frame = FrameHashCache::LoadCacheFrame(val.GetFilename());
|
||||
FramePtr frame = FrameHashCache::LoadCacheFrame(val->GetFilename());
|
||||
if (frame) {
|
||||
TexturePtr tex = CreateTexture(frame->video_params());
|
||||
if (tex) {
|
||||
@@ -509,7 +513,7 @@ TexturePtr RenderProcessor::ProcessVideoCacheJob(const CacheJob &val)
|
||||
}
|
||||
} else {
|
||||
QStringList s = ticket_->property("badcache").toStringList();
|
||||
s.append(val.GetFilename());
|
||||
s.append(val->GetFilename());
|
||||
ticket_->setProperty("badcache", s);
|
||||
}
|
||||
|
||||
@@ -543,4 +547,9 @@ void RenderProcessor::ConvertToReferenceSpace(TexturePtr destination, TexturePtr
|
||||
render_ctx_->BlitColorManaged(ctj, destination.get());
|
||||
}
|
||||
|
||||
bool RenderProcessor::UseCache() const
|
||||
{
|
||||
return static_cast<RenderMode::Mode>(ticket_->property("mode").toInt()) == RenderMode::kOffline;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,19 +42,19 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) override;
|
||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob *stream, const rational &input_time) override;
|
||||
|
||||
virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time) override;
|
||||
virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob *stream, const TimeRange &input_time) override;
|
||||
|
||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const ShaderJob *job) override;
|
||||
|
||||
virtual void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job) override;
|
||||
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job) override;
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob *job) override;
|
||||
|
||||
virtual TexturePtr ProcessVideoCacheJob(const CacheJob &val) override;
|
||||
virtual TexturePtr ProcessVideoCacheJob(const CacheJob *val) override;
|
||||
|
||||
virtual TexturePtr CreateTexture(const VideoParams &p) override;
|
||||
|
||||
@@ -65,6 +65,8 @@ protected:
|
||||
|
||||
virtual void ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs) override;
|
||||
|
||||
virtual bool UseCache() const override;
|
||||
|
||||
private:
|
||||
RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache);
|
||||
|
||||
|
||||
@@ -31,6 +31,10 @@ Texture::~Texture()
|
||||
if (renderer_) {
|
||||
renderer_->DestroyTexture(this);
|
||||
}
|
||||
|
||||
if (job_) {
|
||||
delete job_;
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::Upload(void *data, int linesize)
|
||||
|
||||
+37
-4
@@ -27,8 +27,12 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class AcceleratedJob;
|
||||
class Renderer;
|
||||
|
||||
class Texture;
|
||||
using TexturePtr = std::shared_ptr<Texture>;
|
||||
|
||||
class Texture
|
||||
{
|
||||
public:
|
||||
@@ -45,17 +49,26 @@ public:
|
||||
*/
|
||||
Texture(const VideoParams& param) :
|
||||
renderer_(nullptr),
|
||||
params_(param)
|
||||
params_(param),
|
||||
job_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Texture(const VideoParams &p, const T &j) :
|
||||
Texture(p)
|
||||
{
|
||||
job_ = new T(j);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a real texture linked to a renderer backend
|
||||
*/
|
||||
Texture(Renderer* renderer, const QVariant& native, const VideoParams& param) :
|
||||
renderer_(renderer),
|
||||
params_(param),
|
||||
id_(native)
|
||||
id_(native),
|
||||
job_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -71,6 +84,18 @@ public:
|
||||
return params_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static TexturePtr Job(const VideoParams &p, const T &j)
|
||||
{
|
||||
return std::make_shared<Texture>(p, j);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TexturePtr toJob(const T &job)
|
||||
{
|
||||
return Texture::Job(params_, job);
|
||||
}
|
||||
|
||||
void Upload(void* data, int linesize);
|
||||
|
||||
void Download(void* data, int linesize);
|
||||
@@ -90,6 +115,11 @@ public:
|
||||
return params_.effective_height();
|
||||
}
|
||||
|
||||
QVector2D virtual_resolution() const
|
||||
{
|
||||
return QVector2D(params_.square_pixel_width(), params_.height());
|
||||
}
|
||||
|
||||
VideoParams::Format format() const
|
||||
{
|
||||
return params_.format();
|
||||
@@ -115,6 +145,9 @@ public:
|
||||
return renderer_;
|
||||
}
|
||||
|
||||
bool IsJob() const { return job_; }
|
||||
AcceleratedJob *job() const { return job_; }
|
||||
|
||||
private:
|
||||
Renderer* renderer_;
|
||||
|
||||
@@ -122,9 +155,9 @@ private:
|
||||
|
||||
QVariant id_;
|
||||
|
||||
};
|
||||
AcceleratedJob *job_;
|
||||
|
||||
using TexturePtr = std::shared_ptr<Texture>;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,16 @@ public:
|
||||
return par_width_;
|
||||
}
|
||||
|
||||
QVector2D resolution() const
|
||||
{
|
||||
return QVector2D(width_, height_);
|
||||
}
|
||||
|
||||
QVector2D square_resolution() const
|
||||
{
|
||||
return QVector2D(par_width_, height_);
|
||||
}
|
||||
|
||||
int height() const
|
||||
{
|
||||
return height_;
|
||||
|
||||
@@ -9,6 +9,7 @@ uniform sampler2D garbage_in;
|
||||
uniform sampler2D core_in;
|
||||
uniform bool garbage_in_enabled;
|
||||
uniform bool core_in_enabled;
|
||||
uniform bool invert_in;
|
||||
|
||||
uniform float highlights_in;
|
||||
uniform float shadows_in;
|
||||
@@ -94,6 +95,11 @@ void main() {
|
||||
mask = shadows_in * 0.01 * (highlights_in * 0.01 * mask - 1.0) + 1.0;
|
||||
mask = clamp(mask, 0.0, 1.0);
|
||||
|
||||
// Invert
|
||||
if (invert_in) {
|
||||
mask = 1.0 - mask;
|
||||
}
|
||||
|
||||
col.rgb *= mask;
|
||||
col.w = mask;
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Input texture
|
||||
uniform sampler2D tex_in;
|
||||
|
||||
// Input texture coordinate
|
||||
in vec2 ove_texcoord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(tex_in, ove_texcoord);
|
||||
color.rgb = 1.0 - color.rgb;
|
||||
frag_color = color;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
uniform float evolution_in;
|
||||
uniform float intensity_in;
|
||||
uniform float frequency_in;
|
||||
uniform vec2 position_in;
|
||||
uniform bool stretch_in;
|
||||
|
||||
uniform vec2 resolution_in;
|
||||
uniform sampler2D tex_in;
|
||||
|
||||
in vec2 ove_texcoord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main(void) {
|
||||
vec2 center = position_in/resolution_in;
|
||||
|
||||
vec2 adj_texcoord = ove_texcoord;
|
||||
|
||||
adj_texcoord -= 0.5;
|
||||
if (!stretch_in) {
|
||||
// Adjust by aspect ratio
|
||||
float ar = (resolution_in.x/resolution_in.y);
|
||||
if (resolution_in.x > resolution_in.y) {
|
||||
adj_texcoord.y /= ar;
|
||||
center.y /= ar;
|
||||
} else {
|
||||
adj_texcoord.x *= ar;
|
||||
center.x *= ar;
|
||||
}
|
||||
}
|
||||
adj_texcoord += 0.5;
|
||||
center += 0.5;
|
||||
|
||||
adj_texcoord -= center;
|
||||
|
||||
float len = length(adj_texcoord);
|
||||
vec2 uv = ove_texcoord + (adj_texcoord/len)*cos((frequency_in)*(len*12.0-evolution_in))*(intensity_in*0.0005);
|
||||
frag_color = texture(tex_in, uv);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Swirl effect parameters
|
||||
uniform float radius_in;
|
||||
uniform float angle_in;
|
||||
uniform vec2 pos_in;
|
||||
uniform vec2 resolution_in;
|
||||
|
||||
uniform sampler2D tex_in;
|
||||
|
||||
in vec2 ove_texcoord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main(void) {
|
||||
vec2 center = resolution_in*0.5 + pos_in;
|
||||
|
||||
vec2 uv = ove_texcoord;
|
||||
|
||||
vec2 tc = uv * resolution_in;
|
||||
tc -= center;
|
||||
float dist = length(tc);
|
||||
if (dist < radius_in) {
|
||||
float percent = (radius_in - dist) / radius_in;
|
||||
float theta = percent * percent * -angle_in;
|
||||
float s = sin(theta);
|
||||
float c = cos(theta);
|
||||
tc = vec2(dot(tc, vec2(c, -s)), dot(tc, vec2(s, c)));
|
||||
}
|
||||
tc += center;
|
||||
frag_color = texture(tex_in, tc / resolution_in);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
uniform float scale_in;
|
||||
uniform vec2 position_in;
|
||||
uniform vec2 resolution_in;
|
||||
uniform bool mirrorx_in;
|
||||
uniform bool mirrory_in;
|
||||
uniform int anchor_in;
|
||||
|
||||
uniform sampler2D tex_in;
|
||||
|
||||
in vec2 ove_texcoord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define TOP_LEFT 0
|
||||
#define TOP_CENTER 1
|
||||
#define TOP_RIGHT 2
|
||||
#define MIDDLE_LEFT 3
|
||||
#define MIDDLE_CENTER 4
|
||||
#define MIDDLE_RIGHT 5
|
||||
#define BOTTOM_LEFT 6
|
||||
#define BOTTOM_CENTER 7
|
||||
#define BOTTOM_RIGHT 8
|
||||
|
||||
void main(void) {
|
||||
vec2 coord = ove_texcoord;
|
||||
|
||||
vec2 offset;
|
||||
|
||||
if (anchor_in == TOP_LEFT || anchor_in == TOP_CENTER || anchor_in == TOP_RIGHT) {
|
||||
offset.y = 0.0;
|
||||
} else if (anchor_in == MIDDLE_LEFT || anchor_in == MIDDLE_CENTER || anchor_in == MIDDLE_RIGHT) {
|
||||
offset.y = 0.5;
|
||||
} else if (anchor_in == BOTTOM_LEFT || anchor_in == BOTTOM_CENTER || anchor_in == BOTTOM_RIGHT) {
|
||||
offset.y = 1.0;
|
||||
}
|
||||
|
||||
if (anchor_in == TOP_LEFT || anchor_in == MIDDLE_LEFT || anchor_in == BOTTOM_LEFT) {
|
||||
offset.x = 0.0;
|
||||
} else if (anchor_in == TOP_CENTER || anchor_in == MIDDLE_CENTER || anchor_in == BOTTOM_CENTER) {
|
||||
offset.x = 0.5;
|
||||
} else if (anchor_in == TOP_RIGHT || anchor_in == MIDDLE_RIGHT || anchor_in == BOTTOM_RIGHT) {
|
||||
offset.x = 1.0;
|
||||
}
|
||||
|
||||
coord -= position_in/resolution_in;
|
||||
|
||||
coord -= offset;
|
||||
coord /= scale_in;
|
||||
coord += offset;
|
||||
|
||||
vec2 modcoord = mod(coord, 1.0);
|
||||
|
||||
if (mirrorx_in && mod(coord.x, 2.0) > 1.0) {
|
||||
modcoord.x = 1.0 - modcoord.x;
|
||||
}
|
||||
|
||||
if (mirrory_in && mod(coord.y, 2.0) > 1.0) {
|
||||
modcoord.y = 1.0 - modcoord.y;
|
||||
}
|
||||
|
||||
frag_color = vec4(texture(tex_in, modcoord));
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
uniform float frequency_in;
|
||||
uniform float intensity_in;
|
||||
uniform float evolution_in;
|
||||
uniform bool vertical_in;
|
||||
|
||||
uniform sampler2D tex_in;
|
||||
|
||||
in vec2 ove_texcoord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main(void) {
|
||||
vec2 pos = ove_texcoord;
|
||||
|
||||
if (vertical_in) {
|
||||
pos.x -= sin((ove_texcoord.y-(evolution_in*0.01))*frequency_in)*intensity_in*0.01;
|
||||
} else {
|
||||
pos.y -= sin((ove_texcoord.x-(evolution_in*0.01))*frequency_in)*intensity_in*0.01;
|
||||
}
|
||||
|
||||
if (pos.x < 0.0 || pos.x >= 1.0 || pos.y < 0.0 || pos.y >= 1.0) {
|
||||
discard;
|
||||
} else {
|
||||
frag_color = texture(tex_in, pos);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user