diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index b22b69ced..162256626 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -94,6 +94,7 @@ if(MSVC) /external:anglebrackets /external:W0 "$<$:/O2>" + "$<$:/MP>" ) else() target_compile_options( @@ -155,6 +156,12 @@ if (WIN32) PRIVATE DbgHelp ) +elseif (APPLE) + target_link_libraries( + ${OLIVE_TARGET} + PRIVATE + "-framework ApplicationServices" + ) endif() set(OLIVE_TS_FILES diff --git a/app/codec/oiio/oiiodecoder.cpp b/app/codec/oiio/oiiodecoder.cpp index 27e02991f..1f2b136b3 100644 --- a/app/codec/oiio/oiiodecoder.cpp +++ b/app/codec/oiio/oiiodecoder.cpp @@ -174,9 +174,10 @@ FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider FramePtr frame = Frame::Create(); - frame->set_video_params(VideoRenderingParams(buffer_->spec().width / divider, - buffer_->spec().height / divider, - pix_fmt_)); + frame->set_video_params(VideoRenderingParams(buffer_->spec().width, + buffer_->spec().height, + pix_fmt_, + divider)); frame->allocate(); if (divider == 1) { diff --git a/app/config/config.cpp b/app/config/config.cpp index a5d031c47..c6e26a454 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -83,15 +83,15 @@ void Config::SetDefaults() config_map_["DropWithoutSequenceBehavior"] = TimelineWidget::kDWSAsk; config_map_["Loop"] = false; - config_map_["NodeCatColor0"] = QVariant::fromValue(Color(0.25, 0.25, 0.65)); - config_map_["NodeCatColor1"] = QVariant::fromValue(Color(0.6, 0.6, 0.85)); - config_map_["NodeCatColor2"] = QVariant::fromValue(Color(0.75, 0.75, 0.45)); - config_map_["NodeCatColor3"] = QVariant::fromValue(Color(0.25, 0.5, 0.25)); - config_map_["NodeCatColor4"] = QVariant::fromValue(Color(0.25, 0.65, 0.25)); - config_map_["NodeCatColor5"] = QVariant::fromValue(Color(0.35, 0.35, 0.35)); - config_map_["NodeCatColor6"] = QVariant::fromValue(Color(0.45, 0.45, 0.45)); - config_map_["NodeCatColor7"] = QVariant::fromValue(Color(0.7, 0.3, 0.7)); - config_map_["NodeCatColor8"] = QVariant::fromValue(Color(0.85, 0.65, 0.4)); + config_map_["NodeCatColor0"] = QVariant::fromValue(Color(0.25f, 0.25f, 0.65f)); + config_map_["NodeCatColor1"] = QVariant::fromValue(Color(0.6f, 0.6f, 0.85f)); + config_map_["NodeCatColor2"] = QVariant::fromValue(Color(0.75f, 0.75f, 0.45f)); + config_map_["NodeCatColor3"] = QVariant::fromValue(Color(0.25f, 0.5f, 0.25f)); + config_map_["NodeCatColor4"] = QVariant::fromValue(Color(0.25f, 0.65f, 0.25f)); + config_map_["NodeCatColor5"] = QVariant::fromValue(Color(0.35f, 0.35f, 0.35f)); + config_map_["NodeCatColor6"] = QVariant::fromValue(Color(0.45f, 0.45f, 0.45f)); + config_map_["NodeCatColor7"] = QVariant::fromValue(Color(0.7f, 0.3f, 0.7f)); + config_map_["NodeCatColor8"] = QVariant::fromValue(Color(0.85f, 0.65f, 0.4f)); config_map_["AudioOutput"] = QString(); config_map_["AudioInput"] = QString(); diff --git a/app/dialog/footageproperties/footageproperties.cpp b/app/dialog/footageproperties/footageproperties.cpp index 3fe507906..484127021 100644 --- a/app/dialog/footageproperties/footageproperties.cpp +++ b/app/dialog/footageproperties/footageproperties.cpp @@ -109,7 +109,7 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent, Footage *foota // Auto-select first item that actually has properties if (first_usable_stream >= 0) { - track_list->item(first_usable_stream)->setSelected(true); + track_list->setCurrentRow(first_usable_stream); } track_list->setFocus(); } diff --git a/app/dialog/projectproperties/projectproperties.cpp b/app/dialog/projectproperties/projectproperties.cpp index 6e79a1aae..7f90d2bd2 100644 --- a/app/dialog/projectproperties/projectproperties.cpp +++ b/app/dialog/projectproperties/projectproperties.cpp @@ -44,10 +44,12 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project* p, QWidget *parent) : setWindowTitle(tr("Project Properties for '%1'").arg(working_project_->name())); + QTabWidget* tabs = new QTabWidget; + layout->addWidget(tabs); + { // Color management group - QGroupBox* color_group = new QGroupBox(); - color_group->setTitle(tr("Color Management")); + QWidget* color_group = new QWidget(); QGridLayout* color_layout = new QGridLayout(color_group); @@ -72,18 +74,19 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project* p, QWidget *parent) : color_layout->addWidget(browse_btn, 0, 2); connect(browse_btn, &QPushButton::clicked, this, &ProjectPropertiesDialog::BrowseForOCIOConfig); - layout->addWidget(color_group); - ocio_filename_->setText(working_project_->color_manager()->GetConfigFilename()); connect(ocio_filename_, &QLineEdit::textChanged, this, &ProjectPropertiesDialog::OCIOFilenameUpdated); OCIOFilenameUpdated(); + + tabs->addTab(color_group, tr("Color Management")); } + + { // Paths group - QGroupBox* paths_group = new QGroupBox(); - paths_group->setTitle(tr("Paths")); + QWidget* paths_group = new QWidget(); QGridLayout* paths_layout = new QGridLayout(paths_group); @@ -104,7 +107,7 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project* p, QWidget *parent) : paths_layout->addWidget(proxy_path_->browse_btn(), row, 2); paths_layout->addWidget(proxy_path_->default_box(), row, 3); - layout->addWidget(paths_group); + tabs->addTab(paths_group, tr("Paths")); } QDialogButtonBox* dialog_btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, @@ -177,7 +180,7 @@ void ProjectPropertiesDialog::OCIOFilenameUpdated() if (ocio_filename_->text().isEmpty()) { c = ColorManager::GetDefaultConfig(); } else { - c = OCIO::Config::CreateFromFile(ocio_filename_->text().toUtf8()); + c = ColorManager::CreateConfigFromFile(ocio_filename_->text()); } ocio_filename_->setStyleSheet(QString()); diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 965982241..8930b3979 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(audio) add_subdirectory(block) +add_subdirectory(filter) add_subdirectory(generator) add_subdirectory(input) add_subdirectory(math) @@ -27,8 +28,6 @@ set(OLIVE_SOURCES node/dependency.cpp node/edge.h node/edge.cpp - node/external.h - node/external.cpp node/factory.h node/factory.cpp node/graph.h @@ -39,8 +38,6 @@ set(OLIVE_SOURCES node/inputarray.cpp node/keyframe.h node/keyframe.cpp - node/metareader.h - node/metareader.cpp node/node.h node/node.cpp node/output.h diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 98c716678..1597d737d 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -365,4 +365,9 @@ void Block::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput * Node::InvalidateCache(range, from, source); } +void Block::Hash(QCryptographicHash &, const rational &) const +{ + // A block does nothing by default +} + OLIVE_NAMESPACE_EXIT diff --git a/app/node/block/block.h b/app/node/block/block.h index ca3c6b613..f69b568b8 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -99,6 +99,8 @@ public: virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput* source) override; + virtual void Hash(QCryptographicHash &hash, const rational &time) const override; + public slots: signals: diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 8db5efdd5..413050d98 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -119,4 +119,13 @@ void ClipBlock::Retranslate() texture_input_->set_name(tr("Buffer")); } +void ClipBlock::Hash(QCryptographicHash &hash, const rational &time) const +{ + if (texture_input_->IsConnected()) { + rational t = InputTimeAdjustment(texture_input_, TimeRange(time, time)).in(); + + texture_input_->get_connected_node()->Hash(hash, t); + } +} + OLIVE_NAMESPACE_EXIT diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 56f81c927..375f319ce 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -54,6 +54,8 @@ public: virtual void Retranslate() override; + virtual void Hash(QCryptographicHash &hash, const rational &time) const override; + signals: void PreviewUpdated(); diff --git a/app/node/block/transition/CMakeLists.txt b/app/node/block/transition/CMakeLists.txt index 0ca1160b9..65f672588 100644 --- a/app/node/block/transition/CMakeLists.txt +++ b/app/node/block/transition/CMakeLists.txt @@ -16,8 +16,6 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - node/block/transition/externaltransition.h - node/block/transition/externaltransition.cpp node/block/transition/transition.h node/block/transition/transition.cpp PARENT_SCOPE diff --git a/app/node/block/transition/externaltransition.cpp b/app/node/block/transition/externaltransition.cpp deleted file mode 100644 index 798ae5b74..000000000 --- a/app/node/block/transition/externaltransition.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "externaltransition.h" - -OLIVE_NAMESPACE_ENTER - -ExternalTransition::ExternalTransition(const QString &xml_meta_filename) : - meta_(xml_meta_filename) -{ - foreach (NodeInput* input, meta_.inputs()) { - AddInput(input); - } -} - -Node *ExternalTransition::copy() const -{ - return new ExternalTransition(meta_.filename()); -} - -QString ExternalTransition::Name() const -{ - return meta_.Name(); -} - -QString ExternalTransition::ShortName() const -{ - return meta_.ShortName(); -} - -QString ExternalTransition::id() const -{ - return meta_.id(); -} - -QList ExternalTransition::Category() const -{ - return meta_.Category(); -} - -QString ExternalTransition::Description() const -{ - return meta_.Description(); -} - -void ExternalTransition::Retranslate() -{ - meta_.Retranslate(); -} - -Node::Capabilities ExternalTransition::GetCapabilities(const NodeValueDatabase &) const -{ - return kShader; -} - -QString ExternalTransition::ShaderVertexCode(const NodeValueDatabase &) const -{ - return meta_.vert_code(); -} - -QString ExternalTransition::ShaderFragmentCode(const NodeValueDatabase&) const -{ - return meta_.frag_code(); -} - -int ExternalTransition::ShaderIterations() const -{ - return meta_.iterations(); -} - -NodeInput *ExternalTransition::ShaderIterativeInput() const -{ - return meta_.iteration_input(); -} - -OLIVE_NAMESPACE_EXIT diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index cdb0b8489..e0d66e69e 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -131,8 +131,6 @@ double TransitionBlock::GetInProgress(const rational &time) const void TransitionBlock::Hash(QCryptographicHash &hash, const rational &time) const { - Block::Hash(hash, time); - double all_prog = GetTotalProgress(time); double in_prog = GetInProgress(time); double out_prog = GetOutProgress(time); @@ -140,6 +138,14 @@ void TransitionBlock::Hash(QCryptographicHash &hash, const rational &time) const hash.addData(reinterpret_cast(&all_prog), sizeof(double)); hash.addData(reinterpret_cast(&in_prog), sizeof(double)); hash.addData(reinterpret_cast(&out_prog), sizeof(double)); + + if (out_block_input_->IsConnected()) { + out_block_input_->get_connected_node()->Hash(hash, time); + } + + if (in_block_input_->IsConnected()) { + in_block_input_->get_connected_node()->Hash(hash, time); + } } double TransitionBlock::GetInternalTransitionTime(const rational &time) const diff --git a/app/node/external.cpp b/app/node/external.cpp deleted file mode 100644 index eddcf43be..000000000 --- a/app/node/external.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "external.h" - -#include - -OLIVE_NAMESPACE_ENTER - -ExternalNode::ExternalNode(const QString &xml_meta_filename) : - meta_(xml_meta_filename) -{ - foreach (NodeInput* input, meta_.inputs()) { - AddInput(input); - } -} - -Node *ExternalNode::copy() const -{ - return new ExternalNode(meta_.filename()); -} - -QString ExternalNode::Name() const -{ - return meta_.Name(); -} - -QString ExternalNode::ShortName() const -{ - return meta_.ShortName(); -} - -QString ExternalNode::id() const -{ - return meta_.id(); -} - -QList ExternalNode::Category() const -{ - return meta_.Category(); -} - -QString ExternalNode::Description() const -{ - return meta_.Description(); -} - -void ExternalNode::Retranslate() -{ - meta_.Retranslate(); -} - -Node::Capabilities ExternalNode::GetCapabilities(const NodeValueDatabase &) const -{ - return kShader; -} - -QString ExternalNode::ShaderVertexCode(const NodeValueDatabase&) const -{ - return meta_.vert_code(); -} - -QString ExternalNode::ShaderFragmentCode(const NodeValueDatabase&) const -{ - return meta_.frag_code(); -} - -int ExternalNode::ShaderIterations() const -{ - return meta_.iterations(); -} - -NodeInput *ExternalNode::ShaderIterativeInput() const -{ - return meta_.iteration_input(); -} - -OLIVE_NAMESPACE_EXIT diff --git a/app/node/factory.cpp b/app/node/factory.cpp index fe7361389..8111d8ad9 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -24,16 +24,19 @@ #include "audio/volume/volume.h" #include "block/clip/clip.h" #include "block/gap/gap.h" -#include "block/transition/externaltransition.h" #include "generator/matrix/matrix.h" +#include "generator/polygon/polygon.h" +#include "generator/solid/solid.h" +#include "filter/blur/blur.h" +#include "filter/stroke/stroke.h" #include "input/media/video/video.h" #include "input/media/audio/audio.h" #include "input/time/timeinput.h" #include "math/math/math.h" +#include "math/merge/merge.h" #include "math/trigonometry/trigonometry.h" #include "output/track/track.h" #include "output/viewer/viewer.h" -#include "external.h" OLIVE_NAMESPACE_ENTER QList NodeFactory::library_; @@ -47,13 +50,10 @@ void NodeFactory::Initialize() library_.append(CreateInternal(static_cast(i))); } - library_.append(new ExternalNode(":/shaders/blur.xml")); - library_.append(new ExternalNode(":/shaders/solid.xml")); - library_.append(new ExternalNode(":/shaders/stroke.xml")); - library_.append(new ExternalNode(":/shaders/alphaover.xml")); - library_.append(new ExternalNode(":/shaders/dropshadow.xml")); + /* library_.append(new ExternalTransition(":/shaders/crossdissolve.xml")); library_.append(new ExternalTransition(":/shaders/diptoblack.xml")); + */ } void NodeFactory::Destroy() @@ -132,6 +132,8 @@ Node *NodeFactory::CreateInternal(const NodeFactory::InternalID &id) return new ClipBlock(); case kGapBlock: return new GapBlock(); + case kPolygonGenerator: + return new PolygonGenerator(); case kMatrixGenerator: return new MatrixGenerator(); case kVideoInput: @@ -152,6 +154,14 @@ Node *NodeFactory::CreateInternal(const NodeFactory::InternalID &id) return new TrigonometryNode(); case kTime: return new TimeInput(); + case kBlurFilter: + return new BlurFilterNode(); + case kSolidGenerator: + return new SolidGenerator(); + case kMerge: + return new MergeNode(); + case kStrokeFilter: + return new StrokeFilterNode(); case kInternalNodeCount: break; diff --git a/app/node/factory.h b/app/node/factory.h index c6329de7e..6b17bd792 100644 --- a/app/node/factory.h +++ b/app/node/factory.h @@ -36,6 +36,7 @@ public: kClipBlock, kGapBlock, kAudioInput, + kPolygonGenerator, kMatrixGenerator, kVideoInput, kTrackOutput, @@ -44,6 +45,10 @@ public: kMath, kTime, kTrigonometry, + kBlurFilter, + kSolidGenerator, + kMerge, + kStrokeFilter, // Count value kInternalNodeCount diff --git a/app/node/filter/CMakeLists.txt b/app/node/filter/CMakeLists.txt new file mode 100644 index 000000000..4d7db901d --- /dev/null +++ b/app/node/filter/CMakeLists.txt @@ -0,0 +1,23 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +add_subdirectory(blur) +add_subdirectory(stroke) + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + PARENT_SCOPE +) diff --git a/app/node/filter/blur/CMakeLists.txt b/app/node/filter/blur/CMakeLists.txt new file mode 100644 index 000000000..50bd90ad5 --- /dev/null +++ b/app/node/filter/blur/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/filter/blur/blur.h + node/filter/blur/blur.cpp + PARENT_SCOPE +) diff --git a/app/node/filter/blur/blur.cpp b/app/node/filter/blur/blur.cpp new file mode 100644 index 000000000..64d634d0c --- /dev/null +++ b/app/node/filter/blur/blur.cpp @@ -0,0 +1,104 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "blur.h" + +OLIVE_NAMESPACE_ENTER + +BlurFilterNode::BlurFilterNode() +{ + texture_input_ = new NodeInput("tex_in", NodeParam::kTexture); + AddInput(texture_input_); + + method_input_ = new NodeInput("method_in", NodeParam::kCombo, 0); + AddInput(method_input_); + + radius_input_ = new NodeInput("radius_in", NodeParam::kFloat, 10.0f); + radius_input_->set_property(QStringLiteral("min"), 0.0f); + AddInput(radius_input_); + + horiz_input_ = new NodeInput("horiz_in", NodeParam::kBoolean, true); + AddInput(horiz_input_); + + vert_input_ = new NodeInput("vert_in", NodeParam::kBoolean, true); + AddInput(vert_input_); + + repeat_edge_pixels_input_ = new NodeInput("repeat_edge_pixels_in", NodeParam::kBoolean, false); + AddInput(repeat_edge_pixels_input_); +} + +Node *BlurFilterNode::copy() const +{ + return new BlurFilterNode(); +} + +QString BlurFilterNode::Name() const +{ + return tr("Blur"); +} + +QString BlurFilterNode::id() const +{ + return QStringLiteral("org.olivevideoeditor.Olive.blur"); +} + +QList BlurFilterNode::Category() const +{ + return {kCategoryFilter}; +} + +QString BlurFilterNode::Description() const +{ + return tr("Blurs an image."); +} + +void BlurFilterNode::Retranslate() +{ + texture_input_->set_name(tr("Input")); + method_input_->set_name(tr("Method")); + method_input_->set_combobox_strings({ tr("Box"), tr("Gaussian") }); + radius_input_->set_name(tr("Radius")); + horiz_input_->set_name(tr("Horizontal")); + vert_input_->set_name(tr("Vertical")); + repeat_edge_pixels_input_->set_name(tr("Repeat Edge Pixels")); +} + +Node::Capabilities BlurFilterNode::GetCapabilities(const NodeValueDatabase &) const +{ + return kShader; +} + +QString BlurFilterNode::ShaderFragmentCode(const NodeValueDatabase &) const +{ + return ReadFileAsString(":/shaders/blur.frag"); +} + +int BlurFilterNode::ShaderIterations() const +{ + // FIXME: Optimize if horiz_in or vert_in is disabled + return 2; +} + +NodeInput *BlurFilterNode::ShaderIterativeInput() const +{ + return texture_input_; +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/node/block/transition/externaltransition.h b/app/node/filter/blur/blur.h similarity index 76% rename from app/node/block/transition/externaltransition.h rename to app/node/filter/blur/blur.h index 5962ecf9d..142f03985 100644 --- a/app/node/block/transition/externaltransition.h +++ b/app/node/filter/blur/blur.h @@ -18,24 +18,21 @@ ***/ -#ifndef EXTERNALTRANSITION_H -#define EXTERNALTRANSITION_H +#ifndef BLURFILTERNODE_H +#define BLURFILTERNODE_H -#include "transition.h" - -#include "node/metareader.h" +#include "node/node.h" OLIVE_NAMESPACE_ENTER -class ExternalTransition : public TransitionBlock +class BlurFilterNode : public Node { public: - ExternalTransition(const QString& xml_meta_filename); + BlurFilterNode(); virtual Node* copy() const override; virtual QString Name() const override; - virtual QString ShortName() const override; virtual QString id() const override; virtual QList Category() const override; virtual QString Description() const override; @@ -43,15 +40,26 @@ public: virtual void Retranslate() override; virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; - virtual QString ShaderVertexCode(const NodeValueDatabase&) const override; virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; + virtual int ShaderIterations() const override; virtual NodeInput* ShaderIterativeInput() const override; private: - NodeMetaReader meta_; + NodeInput* texture_input_; + + NodeInput* method_input_; + + NodeInput* radius_input_; + + NodeInput* horiz_input_; + + NodeInput* vert_input_; + + NodeInput* repeat_edge_pixels_input_; + }; OLIVE_NAMESPACE_EXIT -#endif // EXTERNALTRANSITION_H +#endif // BLURFILTERNODE_H diff --git a/app/node/filter/stroke/CMakeLists.txt b/app/node/filter/stroke/CMakeLists.txt new file mode 100644 index 000000000..212f3bfe2 --- /dev/null +++ b/app/node/filter/stroke/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/filter/stroke/stroke.h + node/filter/stroke/stroke.cpp + PARENT_SCOPE +) diff --git a/app/node/filter/stroke/stroke.cpp b/app/node/filter/stroke/stroke.cpp new file mode 100644 index 000000000..e87449db2 --- /dev/null +++ b/app/node/filter/stroke/stroke.cpp @@ -0,0 +1,95 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "stroke.h" + +#include "render/color.h" + +OLIVE_NAMESPACE_ENTER + +StrokeFilterNode::StrokeFilterNode() +{ + tex_input_ = new NodeInput("tex_in", NodeParam::kTexture); + AddInput(tex_input_); + + color_input_ = new NodeInput("color_in", + NodeParam::kColor, + QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f))); + AddInput(color_input_); + + radius_input_ = new NodeInput("radius_in", NodeParam::kFloat, 10.0f); + radius_input_->set_property("min", 0.0f); + AddInput(radius_input_); + + opacity_input_ = new NodeInput("opacity_in", NodeParam::kFloat, 1.0f); + opacity_input_->set_property("view", "percent"); + opacity_input_->set_property("min", 0.0f); + opacity_input_->set_property("max", 1.0f); + AddInput(opacity_input_); + + inner_input_ = new NodeInput("inner_in", NodeParam::kBoolean, false); + AddInput(inner_input_); +} + +Node *StrokeFilterNode::copy() const +{ + return new StrokeFilterNode(); +} + +QString StrokeFilterNode::Name() const +{ + return tr("Stroke"); +} + +QString StrokeFilterNode::id() const +{ + return QStringLiteral("org.olivevideoeditor.Olive.stroke"); +} + +QList StrokeFilterNode::Category() const +{ + return {kCategoryFilter}; +} + +QString StrokeFilterNode::Description() const +{ + return tr("Creates a stroke outline around an image."); +} + +void StrokeFilterNode::Retranslate() +{ + tex_input_->set_name(tr("Input")); + color_input_->set_name(tr("Color")); + radius_input_->set_name(tr("Radius")); + opacity_input_->set_name(tr("Opacity")); + inner_input_->set_name(tr("Inner")); +} + +Node::Capabilities StrokeFilterNode::GetCapabilities(const NodeValueDatabase &) const +{ + return kShader; +} + +QString StrokeFilterNode::ShaderFragmentCode(const NodeValueDatabase &) const +{ + return ReadFileAsString(":/shaders/stroke.frag"); +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/node/external.h b/app/node/filter/stroke/stroke.h similarity index 68% rename from app/node/external.h rename to app/node/filter/stroke/stroke.h index 046539534..06eb97f64 100644 --- a/app/node/external.h +++ b/app/node/filter/stroke/stroke.h @@ -18,28 +18,21 @@ ***/ -#ifndef EXTERNALNODE_H -#define EXTERNALNODE_H +#ifndef STROKEFILTERNODE_H +#define STROKEFILTERNODE_H -#include - -#include "node.h" -#include "metareader.h" +#include "node/node.h" OLIVE_NAMESPACE_ENTER -/** - * @brief A node generated from an external XML metadata file - */ -class ExternalNode : public Node +class StrokeFilterNode : public Node { public: - ExternalNode(const QString& xml_meta_filename); + StrokeFilterNode(); virtual Node* copy() const override; virtual QString Name() const override; - virtual QString ShortName() const override; virtual QString id() const override; virtual QList Category() const override; virtual QString Description() const override; @@ -47,15 +40,21 @@ public: virtual void Retranslate() override; virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; - virtual QString ShaderVertexCode(const NodeValueDatabase&) const override; virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; - virtual int ShaderIterations() const override; - virtual NodeInput* ShaderIterativeInput() const override; private: - NodeMetaReader meta_; + NodeInput* tex_input_; + + NodeInput* color_input_; + + NodeInput* radius_input_; + + NodeInput* opacity_input_; + + NodeInput* inner_input_; + }; OLIVE_NAMESPACE_EXIT -#endif // EXTERNALNODE_H +#endif // STROKEFILTERNODE_H diff --git a/app/node/generator/CMakeLists.txt b/app/node/generator/CMakeLists.txt index 9e8d25261..52d74e469 100644 --- a/app/node/generator/CMakeLists.txt +++ b/app/node/generator/CMakeLists.txt @@ -15,6 +15,8 @@ # along with this program. If not, see . add_subdirectory(matrix) +add_subdirectory(polygon) +add_subdirectory(solid) set(OLIVE_SOURCES ${OLIVE_SOURCES} diff --git a/app/node/generator/matrix/matrix.cpp b/app/node/generator/matrix/matrix.cpp index 29871b6f8..66e7f57af 100644 --- a/app/node/generator/matrix/matrix.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -89,30 +89,58 @@ void MatrixGenerator::Retranslate() } NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const +{ + // Push matrix output + QMatrix4x4 mat = GenerateMatrix(value); + NodeValueTable output = value.Merge(); + output.Push(NodeParam::kMatrix, mat); + return output; +} + +bool MatrixGenerator::HasGizmos() const +{ + return true; +} + +void MatrixGenerator::DrawGizmos(NodeValueDatabase &db, QPainter *p, const QVector2D &scale) const +{ + // FIXME: Implement this properly + /* + p->setPen(Qt::white); + + // Fold values into a matrix + QMatrix4x4 matrix = GenerateMatrix(db); + + // Set QPainter transform to our matrix + p->setTransform(matrix.toTransform()); + + // Draw ellipse + p->drawEllipse(QRect(0, 0, 100, 100)); + */ +} + +QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value) const { QMatrix4x4 mat; // Position translate - QVector2D pos = value[position_input_].Get(NodeParam::kVec2).value(); + QVector2D pos = value[position_input_].Take(NodeParam::kVec2).value(); mat.translate(pos); // Rotation - mat.rotate(value[rotation_input_].Get(NodeParam::kFloat).toFloat(), 0, 0, 1); + mat.rotate(value[rotation_input_].Take(NodeParam::kFloat).toFloat(), 0, 0, 1); // Scale and Uniform Scale - QVector2D scale = value[scale_input_].Get(NodeParam::kVec2).value(); - if (value[uniform_scale_input_].Get(NodeParam::kBoolean).toBool()) { + QVector2D scale = value[scale_input_].Take(NodeParam::kVec2).value(); + if (value[uniform_scale_input_].Take(NodeParam::kBoolean).toBool()) { scale.setY(scale.x()); } mat.scale(scale); // Anchor Point - mat.translate(-value[anchor_input_].Get(NodeParam::kVec2).value()); + mat.translate(-value[anchor_input_].Take(NodeParam::kVec2).value()); - // Push matrix output - NodeValueTable output; - output.Push(NodeParam::kMatrix, mat); - return output; + return mat; } void MatrixGenerator::UniformScaleChanged() diff --git a/app/node/generator/matrix/matrix.h b/app/node/generator/matrix/matrix.h index 88e8caeb4..901c3dc29 100644 --- a/app/node/generator/matrix/matrix.h +++ b/app/node/generator/matrix/matrix.h @@ -43,7 +43,12 @@ public: virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual bool HasGizmos() const override; + virtual void DrawGizmos(NodeValueDatabase& db, QPainter *p, const QVector2D &scale) const override; + private: + QMatrix4x4 GenerateMatrix(NodeValueDatabase& value) const; + NodeInput* position_input_; NodeInput* rotation_input_; diff --git a/app/node/generator/polygon/CMakeLists.txt b/app/node/generator/polygon/CMakeLists.txt new file mode 100644 index 000000000..a1a4b2cc5 --- /dev/null +++ b/app/node/generator/polygon/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/generator/polygon/polygon.h + node/generator/polygon/polygon.cpp + PARENT_SCOPE +) diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp new file mode 100644 index 000000000..15ed05561 --- /dev/null +++ b/app/node/generator/polygon/polygon.cpp @@ -0,0 +1,133 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "polygon.h" + +#include + +OLIVE_NAMESPACE_ENTER + +PolygonGenerator::PolygonGenerator() +{ + points_input_ = new NodeInputArray("points_in", NodeParam::kVec2); + AddInput(points_input_); + + color_input_ = new NodeInput("color_in", NodeParam::kColor); + AddInput(color_input_); + + // Default to "a color" that isn't + color_input_->set_standard_value(1.0, 0); + color_input_->set_standard_value(1.0, 1); + color_input_->set_standard_value(1.0, 2); + color_input_->set_standard_value(1.0, 3); + + // FIXME: Test code + points_input_->SetSize(5); + points_input_->At(0)->set_standard_value(960, 0); + points_input_->At(0)->set_standard_value(240, 1); + points_input_->At(1)->set_standard_value(640, 0); + points_input_->At(1)->set_standard_value(480, 1); + points_input_->At(2)->set_standard_value(760, 0); + points_input_->At(2)->set_standard_value(800, 1); + points_input_->At(3)->set_standard_value(1100, 0); + points_input_->At(3)->set_standard_value(800, 1); + points_input_->At(4)->set_standard_value(1280, 0); + points_input_->At(4)->set_standard_value(480, 1); + // End test +} + +Node *PolygonGenerator::copy() const +{ + return new PolygonGenerator(); +} + +QString PolygonGenerator::Name() const +{ + return tr("Polygon"); +} + +QString PolygonGenerator::id() const +{ + return QStringLiteral("org.olivevideoeditor.Olive.polygon"); +} + +QList PolygonGenerator::Category() const +{ + return {kCategoryGenerator}; +} + +QString PolygonGenerator::Description() const +{ + return tr("Generate a 2D polygon of any amount of points."); +} + +void PolygonGenerator::Retranslate() +{ + points_input_->set_name(tr("Points")); + color_input_->set_name(tr("Color")); +} + +Node::Capabilities PolygonGenerator::GetCapabilities(const NodeValueDatabase &) const +{ + return kShader; +} + +QString PolygonGenerator::ShaderFragmentCode(const NodeValueDatabase &) const +{ + return Node::ReadFileAsString(":/shaders/polygon.frag"); +} + +bool PolygonGenerator::HasGizmos() const +{ + return true; +} + +void PolygonGenerator::DrawGizmos(NodeValueDatabase &db, QPainter *p, const QVector2D &scale) const +{ + if (!points_input_->GetSize()) { + return; + } + + QVector points(points_input_->GetSize()); + + p->setPen(Qt::white); + p->setBrush(Qt::white); + + int rect_sz = p->fontMetrics().height() / 8; + + for (int i=0;iGetSize();i++) { + QVector2D v = db[points_input_->At(i)].Take(NodeParam::kVec2).value(); + + v *= scale; + + QPointF pt = v.toPointF(); + points[i] = pt; + + QRectF rect(pt - QPointF(rect_sz, rect_sz), + pt + QPointF(rect_sz, rect_sz)); + p->drawRect(rect); + } + + points.append(points.first()); + + p->drawPolyline(points.constData(), points.size()); +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/node/generator/polygon/polygon.h b/app/node/generator/polygon/polygon.h new file mode 100644 index 000000000..53b0d96b5 --- /dev/null +++ b/app/node/generator/polygon/polygon.h @@ -0,0 +1,63 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef POLYGONGENERATOR_H +#define POLYGONGENERATOR_H + +#include "node/node.h" + +OLIVE_NAMESPACE_ENTER + +class PolygonGenerator : public Node +{ +public: + PolygonGenerator(); + + virtual Node* copy() const override; + + virtual QString Name() const override; + virtual QString id() const override; + virtual QList Category() const override; + virtual QString Description() const override; + + virtual void Retranslate() override; + + virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; + virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; + + virtual bool HasGizmos() const override; + virtual void DrawGizmos(NodeValueDatabase& db, QPainter *p, const QVector2D &scale) const override; + + /* + virtual bool GizmoPress(const QPointF &p) override; + virtual void GizmoMove(const QPointF &p) override; + virtual void GizmoRelease(const QPointF &p) override; + */ + +private: + NodeInputArray* points_input_; + + NodeInput* color_input_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // POLYGONGENERATOR_H diff --git a/app/node/generator/solid/CMakeLists.txt b/app/node/generator/solid/CMakeLists.txt new file mode 100644 index 000000000..df388a97d --- /dev/null +++ b/app/node/generator/solid/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/generator/solid/solid.h + node/generator/solid/solid.cpp + PARENT_SCOPE +) diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp new file mode 100644 index 000000000..d2ef7fd74 --- /dev/null +++ b/app/node/generator/solid/solid.cpp @@ -0,0 +1,76 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "solid.h" + +#include "render/color.h" + +OLIVE_NAMESPACE_ENTER + +SolidGenerator::SolidGenerator() +{ + // Default to a color that isn't black + color_input_ = new NodeInput("color_in", + NodeInput::kColor, + QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f))); + AddInput(color_input_); +} + +Node *SolidGenerator::copy() const +{ + return new SolidGenerator(); +} + +QString SolidGenerator::Name() const +{ + return tr("Solid"); +} + +QString SolidGenerator::id() const +{ + return QStringLiteral("org.olivevideoeditor.Olive.solidgenerator"); +} + +QList SolidGenerator::Category() const +{ + return {kCategoryGenerator}; +} + +QString SolidGenerator::Description() const +{ + return tr("Generate a solid color."); +} + +void SolidGenerator::Retranslate() +{ + color_input_->set_name(tr("Color")); +} + +Node::Capabilities SolidGenerator::GetCapabilities(const NodeValueDatabase &) const +{ + return kShader; +} + +QString SolidGenerator::ShaderFragmentCode(const NodeValueDatabase &) const +{ + return ReadFileAsString(":/shaders/solid.frag"); +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/node/generator/solid/solid.h b/app/node/generator/solid/solid.h new file mode 100644 index 000000000..8e5433e69 --- /dev/null +++ b/app/node/generator/solid/solid.h @@ -0,0 +1,52 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef SOLIDGENERATOR_H +#define SOLIDGENERATOR_H + +#include "node/node.h" + +OLIVE_NAMESPACE_ENTER + +class SolidGenerator : public Node +{ +public: + SolidGenerator(); + + virtual Node* copy() const override; + + virtual QString Name() const override; + virtual QString id() const override; + virtual QList Category() const override; + virtual QString Description() const override; + + virtual void Retranslate() override; + + virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; + virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; + +private: + NodeInput* color_input_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // SOLIDGENERATOR_H diff --git a/app/node/math/CMakeLists.txt b/app/node/math/CMakeLists.txt index 1ae06847e..32ad7e923 100644 --- a/app/node/math/CMakeLists.txt +++ b/app/node/math/CMakeLists.txt @@ -15,6 +15,7 @@ # along with this program. If not, see . add_subdirectory(math) +add_subdirectory(merge) add_subdirectory(trigonometry) set(OLIVE_SOURCES diff --git a/app/node/math/merge/CMakeLists.txt b/app/node/math/merge/CMakeLists.txt new file mode 100644 index 000000000..a7472ded0 --- /dev/null +++ b/app/node/math/merge/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/math/merge/merge.h + node/math/merge/merge.cpp + PARENT_SCOPE +) diff --git a/app/node/math/merge/merge.cpp b/app/node/math/merge/merge.cpp new file mode 100644 index 000000000..e5c039a6e --- /dev/null +++ b/app/node/math/merge/merge.cpp @@ -0,0 +1,96 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "merge.h" + +OLIVE_NAMESPACE_ENTER + +MergeNode::MergeNode() +{ + base_in_ = new NodeInput("base_in", NodeParam::kTexture); + AddInput(base_in_); + + blend_in_ = new NodeInput("blend_in", NodeParam::kTexture); + AddInput(blend_in_); +} + +Node *MergeNode::copy() const +{ + return new MergeNode(); +} + +QString MergeNode::Name() const +{ + return tr("Merge"); +} + +QString MergeNode::id() const +{ + return QStringLiteral("org.olivevideoeditor.Olive.merge"); +} + +QList MergeNode::Category() const +{ + return {kCategoryMath}; +} + +QString MergeNode::Description() const +{ + return tr("Merge two textures together."); +} + +void MergeNode::Retranslate() +{ + base_in_->set_name(tr("Base")); + blend_in_->set_name(tr("Blend")); +} + +Node::Capabilities MergeNode::GetCapabilities(const NodeValueDatabase &) const +{ + return kShader; +} + +QString MergeNode::ShaderFragmentCode(const NodeValueDatabase &) const +{ + return ReadFileAsString(":/shaders/alphaover.frag"); +} + +NodeInput *MergeNode::base_in() const +{ + return base_in_; +} + +NodeInput *MergeNode::blend_in() const +{ + return blend_in_; +} + +void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const +{ + if (base_in_->IsConnected()) { + base_in_->get_connected_node()->Hash(hash, time); + } + + if (blend_in_->IsConnected()) { + blend_in_->get_connected_node()->Hash(hash, time); + } +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/node/math/merge/merge.h b/app/node/math/merge/merge.h new file mode 100644 index 000000000..63a17cb2d --- /dev/null +++ b/app/node/math/merge/merge.h @@ -0,0 +1,59 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef MERGENODE_H +#define MERGENODE_H + +#include "node/node.h" + +OLIVE_NAMESPACE_ENTER + +class MergeNode : public Node +{ +public: + MergeNode(); + + virtual Node* copy() const override; + + virtual QString Name() const override; + virtual QString id() const override; + virtual QList Category() const override; + virtual QString Description() const override; + + virtual void Retranslate() override; + + virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override; + virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override; + + NodeInput* base_in() const; + NodeInput* blend_in() const; + + virtual void Hash(QCryptographicHash &hash, const rational &time) const override; + +private: + NodeInput* base_in_; + + NodeInput* blend_in_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // MERGENODE_H diff --git a/app/node/metareader.cpp b/app/node/metareader.cpp deleted file mode 100644 index e09386df4..000000000 --- a/app/node/metareader.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "metareader.h" - -#include - -#include "common/xmlutils.h" -#include "config/config.h" -#include "node.h" - -OLIVE_NAMESPACE_ENTER - -NodeMetaReader::NodeMetaReader(const QString &xml_meta_filename) : - xml_filename_(xml_meta_filename), - iterations_(1), - iteration_input_(nullptr) -{ - QFile metadata_file(xml_filename_); - - if (metadata_file.open(QFile::ReadOnly)) { - QXmlStreamReader reader(&metadata_file); - - while (XMLReadNextStartElement(&reader)) { - if (reader.name() == QStringLiteral("effect")) { - XMLReadEffect(&reader); - } else { - reader.skipCurrentElement(); - } - } - - metadata_file.close(); - } else { - qWarning() << "Failed to load node metadata file" << xml_filename_; - } -} - -QString NodeMetaReader::Name() const -{ - return GetStringForCurrentLanguage(&names_); -} - -QString NodeMetaReader::ShortName() const -{ - if (short_names_.isEmpty()) { - return Name(); - } else { - return GetStringForCurrentLanguage(&short_names_); - } -} - -const QString &NodeMetaReader::id() const -{ - return id_; -} - -QList NodeMetaReader::Category() const -{ - return categories_; -} - -QString NodeMetaReader::Description() const -{ - return GetStringForCurrentLanguage(&descriptions_); -} - -const QString &NodeMetaReader::filename() const -{ - return xml_filename_; -} - -const QString &NodeMetaReader::frag_code() const -{ - return frag_code_; -} - -const QString &NodeMetaReader::vert_code() const -{ - return vert_code_; -} - -const int &NodeMetaReader::iterations() const -{ - return iterations_; -} - -NodeInput *NodeMetaReader::iteration_input() const -{ - return iteration_input_; -} - -const QList &NodeMetaReader::inputs() const -{ - return inputs_; -} - -void NodeMetaReader::Retranslate() -{ - { - // Re-translate every parameter name - QMap::const_iterator iterator; - - // Iterate through parameter language tables that we have - for (iterator=param_names_.begin();iterator!=param_names_.end();iterator++) { - NodeInput* this_input = GetInputWithID(iterator.key()); - this_input->set_name(GetStringForCurrentLanguage(&iterator.value())); - } - } - - { - // Re-translate any combobox items - QMap >::const_iterator param_it; - - for (param_it=combo_names_.begin(); param_it!=combo_names_.end(); param_it++) { - NodeInput* input = GetInputWithID(param_it.key()); - - QStringList combo_items; - - foreach (const LanguageMap& lang_map, param_it.value()) { - combo_items.append(GetStringForCurrentLanguage(&lang_map)); - } - - input->set_combobox_strings(combo_items); - } - } -} - -void NodeMetaReader::XMLReadLanguageString(QXmlStreamReader* reader, LanguageMap* map) -{ - QString lang; - - // Traverse through name attributes for its language - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("lang")) { - lang = attr.value().toString(); - - // We don't recognize any other "name" attributes at this time - break; - } - } - - // Insert name with language into map - map->insert(lang, reader->readElementText().trimmed()); -} - -void NodeMetaReader::XMLReadEffect(QXmlStreamReader* reader) -{ - // Traverse through effect attributes for an ID - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("id")) { - id_ = attr.value().toString(); - - // We don't recognize any other "effect" attributes at this time - break; - } - } - - if (id_.isEmpty()) { - qWarning() << "Effect metadata" << xml_filename_ << "has no ID"; - return; - } - - // Continue reading for other metadata - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("name")) { - // Pick up name - XMLReadLanguageString(reader, &names_); - } else if (reader->name() == QStringLiteral("shortnames")) { - // Pick up short name - XMLReadLanguageString(reader, &short_names_); - } else if (reader->name() == QStringLiteral("category")) { - // Pick up category - QStringList category_ids = reader->readElementText().split(':'); - - foreach (const QString& id, category_ids) { - bool ok; - - int try_parse = id.toInt(&ok); - - if (!ok || try_parse < 0 || try_parse >= Node::kCategoryCount) { - continue; - } - - categories_.append(static_cast(try_parse)); - } - } else if (reader->name() == QStringLiteral("description")) { - // Pick up description - XMLReadLanguageString(reader, &descriptions_); - } else if (reader->name() == QStringLiteral("iterations")) { - // Pick up iterations - XMLReadIterations(reader); - } else if (reader->name() == QStringLiteral("fragment")) { - // Pick up fragment shader code - XMLReadShader(reader, frag_code_); - } else if (reader->name() == QStringLiteral("vertex")) { - // Pick up vertex shader code - XMLReadShader(reader, vert_code_); - } else if (reader->name() == QStringLiteral("param")) { - // Pick up a parameter - XMLReadParam(reader); - } else { - reader->skipCurrentElement(); - } - } -} - -void NodeMetaReader::XMLReadIterations(QXmlStreamReader* reader) -{ - int iteration_pickup = reader->readElementText().toInt(); - - if (iterations_ > 0) { - iterations_ = iteration_pickup; - } else { - // If the iteration value is invalid, don't set it, print an error instead - qWarning() << "Invalid iteration number in" << xml_filename_ << "- setting to default (1)"; - } -} - -void NodeMetaReader::XMLReadParam(QXmlStreamReader *reader) -{ - QString param_id; - NodeParam::DataType param_type = NodeParam::kAny; - bool is_iterative = false; - - // Traverse through parameter attributes for an ID - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("id")) { - param_id = attr.value().toString(); - } else if (attr.name() == QStringLiteral("type")) { - param_type = NodeParam::StringToDataType(attr.value().toString()); - } else if (attr.name() == QStringLiteral("iterative_input")) { - is_iterative = true; - } - } - - if (param_id.isEmpty()) { - qWarning() << "Effect metadata" << xml_filename_ << "contains a parameter with no ID - parameter was not added"; - return; - } - - QVector default_val; - QHash properties; - LanguageMap param_names; - QList combo_names; - QList combo_descriptions; - - // Traverse through param contents for more data - while (XMLReadNextStartElement(reader)) { - // NOTE: readElementText() returns a string, but for number types (which min and max apply to), QVariant will - // convert them automatically - if (reader->name() == QStringLiteral("name")) { - - // Insert language into map - XMLReadLanguageString(reader, ¶m_names); - - } else if (reader->name() == QStringLiteral("default")) { - - // Reads the default value - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("value")) { - default_val.append(NodeInput::StringToValue(param_type, reader->readElementText())); - } else { - reader->skipCurrentElement(); - } - } - - } else if (reader->name() == QStringLiteral("option")) { - - // Read names and descriptions - LanguageMap names; - LanguageMap descriptions; - - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("name")) { - XMLReadLanguageString(reader, &names); - } else if (reader->name() == QStringLiteral("description")) { - XMLReadLanguageString(reader, &descriptions); - } else { - reader->skipCurrentElement(); - } - } - - combo_names.append(names); - combo_descriptions.append(descriptions); - - } else { - properties.insert(reader->name().toString(), reader->readElementText()); - } - } - - param_names_.insert(param_id, param_names); - - // Insert combo options if they exist - if (!combo_names.isEmpty()) { - combo_names_.insert(param_id, combo_names); - combo_descriptions_.insert(param_id, combo_descriptions); - } - - NodeInput* input = new NodeInput(param_id, param_type, default_val); - - QHash::const_iterator iterator; - - for (iterator=properties.begin();iterator!=properties.end();iterator++) { - input->set_property(iterator.key(), iterator.value()); - } - - if (is_iterative) { - iteration_input_ = input; - } - - inputs_.append(input); -} - -void NodeMetaReader::XMLReadShader(QXmlStreamReader *reader, QString &destination) -{ - QString code_url; - - // Traverse through parameter attributes for an ID - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("url")) { - code_url = attr.value().toString(); - - // We don't recognize any other "shader" attributes at this time - break; - } - } - - // Add code in file from URL - if (!code_url.isEmpty()) { - destination.append(Node::ReadFileAsString(code_url)); - } - - // Add any code that's inline in the XML - QString element_text = reader->readElementText().trimmed(); - if (!element_text.isEmpty()) { - destination.append(element_text); - } -} - -QString NodeMetaReader::GetStringForCurrentLanguage(const LanguageMap *language_map) -{ - if (language_map->isEmpty()) { - // There are no entries for this map, this must be an empty string - return QString(); - } - - // Get current language config - QString language = Config::Current()[QStringLiteral("Language")].toString(); - - // See if our map has an exact language match - QString str_for_lang = language_map->value(language); - if (!str_for_lang.isEmpty()) { - return str_for_lang; - } - - // If not, try to find a match with the same language but not the same derivation - QString base_lang = language.split('_').first(); - QList available_languages = language_map->keys(); - foreach (const QString& l, available_languages) { - if (l.startsWith(base_lang)) { - // This is the same language, so we can return this - return language_map->value(l); - } - } - - // We couldn't find an exact or close match, just return the first in the list - // (assume a string in the wrong language is better than no string at all) - return language_map->first(); -} - -NodeInput *NodeMetaReader::GetInputWithID(const QString &id) const -{ - foreach (NodeInput* input, inputs_) { - if (input->id() == id) { - return input; - } - } - return nullptr; -} - -OLIVE_NAMESPACE_EXIT diff --git a/app/node/metareader.h b/app/node/metareader.h deleted file mode 100644 index 9efb6441d..000000000 --- a/app/node/metareader.h +++ /dev/null @@ -1,92 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef NODEMETAREADER_H -#define NODEMETAREADER_H - -#include -#include -#include - -#include "input.h" -#include "node/node.h" - -OLIVE_NAMESPACE_ENTER - -class NodeMetaReader -{ -public: - NodeMetaReader(const QString& xml_meta_filename); - - QString Name() const; - QString ShortName() const; - const QString& id() const; - QList Category() const; - QString Description() const; - - const QString& filename() const; - - const QString& frag_code() const; - const QString& vert_code() const; - - const int& iterations() const; - NodeInput* iteration_input() const; - - const QList& inputs() const; - - void Retranslate(); - -private: - using LanguageMap = QMap; - - void XMLReadLanguageString(QXmlStreamReader* reader, LanguageMap *map); - void XMLReadEffect(QXmlStreamReader *reader); - void XMLReadIterations(QXmlStreamReader* reader); - void XMLReadParam(QXmlStreamReader* reader); - void XMLReadShader(QXmlStreamReader* reader, QString& destination); - - static QString GetStringForCurrentLanguage(const LanguageMap *language_map); - - NodeInput* GetInputWithID(const QString& id) const; - - QString xml_filename_; - - LanguageMap names_; - LanguageMap short_names_; - LanguageMap descriptions_; - QList categories_; - QMap param_names_; - QMap > combo_names_; - QMap > combo_descriptions_; - - QString id_; - - QString frag_code_; - QString vert_code_; - - int iterations_; - NodeInput* iteration_input_; - - QList inputs_; -}; - -OLIVE_NAMESPACE_EXIT - -#endif // NODEMETAREADER_H diff --git a/app/node/node.cpp b/app/node/node.cpp index 7a149d3ad..724b77fe2 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -282,6 +282,28 @@ QList Node::GetOutputs() const return {output_}; } +bool Node::HasGizmos() const +{ + return false; +} + +void Node::DrawGizmos(NodeValueDatabase &, QPainter *, const QVector2D &) const +{ +} + +bool Node::GizmoPress(const QPointF &) +{ + return false; +} + +void Node::GizmoMove(const QPointF &) +{ +} + +void Node::GizmoRelease(const QPointF &) +{ +} + const QString &Node::GetLabel() const { return label_; diff --git a/app/node/node.h b/app/node/node.h index f9f64898d..acfe96d65 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -380,6 +381,14 @@ public: QList GetOutputs() const; + virtual bool HasGizmos() const; + + virtual void DrawGizmos(NodeValueDatabase& db, QPainter* p, const QVector2D &scale) const; + + virtual bool GizmoPress(const QPointF& p); + virtual void GizmoMove(const QPointF& p); + virtual void GizmoRelease(const QPointF& p); + const QString& GetLabel() const; void SetLabel(const QString& s); diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 9d6f75b53..240dc514e 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -420,11 +420,11 @@ NodeInputArray *TrackOutput::block_input() const void TrackOutput::Hash(QCryptographicHash &hash, const rational &time) const { - // Resolve block list Block* b = BlockAtTime(time); + // Defer to block at this time, don't add any of our own information to the hash if (b) { - return b->Hash(hash, time); + b->Hash(hash, time); } } diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index 07b7678c0..6165c11fb 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -22,6 +22,7 @@ #include "node/factory.h" #include "node/math/math/math.h" +#include "node/math/merge/merge.h" #include "node/output/viewer/viewer.h" OLIVE_NAMESPACE_ENTER @@ -104,11 +105,11 @@ TrackOutput* TrackList::AddTrack() switch (type_) { case Timeline::kTrackTypeVideo: { - Node* blend = NodeFactory::CreateFromID(QStringLiteral("org.olivevideoeditor.Olive.alphaoverblend")); + MergeNode* blend = new MergeNode(); GetParentGraph()->AddNode(blend); - NodeParam::ConnectEdge(track->output(), static_cast(blend->GetInputWithID("blend_in"))); - NodeParam::ConnectEdge(last_track->output(), static_cast(blend->GetInputWithID("base_in"))); + NodeParam::ConnectEdge(track->output(), blend->blend_in()); + NodeParam::ConnectEdge(last_track->output(), blend->base_in()); NodeParam::ConnectEdge(blend->output(), edge->input()); break; } diff --git a/app/node/param.cpp b/app/node/param.cpp index 2e401b34a..b8f25249c 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -71,14 +71,13 @@ Node *NodeParam::parentNode() const { QObject* p = parent(); - while (p != nullptr) { - // Determine if this object is a Node or not + while (p) { Node* cast_test = dynamic_cast(p); - if (cast_test != nullptr) { + if (cast_test) { return cast_test; + } else { + p = p->parent(); } - - p = p->parent(); } return nullptr; diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index c4737cfd1..9f5a1f8f5 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -29,21 +29,29 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa NodeValueDatabase database; // We need to insert tables into the database for each input - foreach (NodeParam* param, node->parameters()) { + QList inputs = node->GetInputsIncludingArrays(); + + foreach (NodeInput* input, inputs) { if (IsCancelled()) { return NodeValueDatabase(); } - if (param->type() == NodeParam::kInput) { - NodeInput* input = static_cast(param); - TimeRange input_time = node->InputTimeAdjustment(input, range); + TimeRange input_time = node->InputTimeAdjustment(input, range); - NodeValueTable table = ProcessInput(input, input_time); + NodeValueTable table = ProcessInput(input, input_time); - InputProcessingEvent(input, input_time, &table); + // Exception for Footage types where we actually retrieve some Footage data from a decoder + if (input->data_type() == NodeParam::kFootage) { + StreamPtr stream = ResolveStreamFromInput(input); - database.Insert(input, table); + if (stream) { + + FootageProcessingEvent(stream, input_time, &table); + + } } + + database.Insert(input, table); } // Insert global variables @@ -79,8 +87,21 @@ NodeValueTable NodeTraverser::ProcessNode(const NodeDependency& dep) NodeValueTable NodeTraverser::RenderBlock(const TrackOutput *track, const TimeRange &range) { - // By default, don't bother traversing blocks - return NodeValueTable(); + // By default, just follow the in point + Block* active_block = track->BlockAtTime(range.in()); + + NodeValueTable table; + + if (active_block) { + table = ProcessNode(NodeDependency(active_block, range)); + } + + return table; +} + +StreamPtr NodeTraverser::ResolveStreamFromInput(NodeInput *input) +{ + return input->get_standard_value().value(); } NodeValueTable NodeTraverser::ProcessInput(const NodeInput *input, const TimeRange& range) diff --git a/app/node/traverser.h b/app/node/traverser.h index 7897cd7a1..4e80a2a68 100644 --- a/app/node/traverser.h +++ b/app/node/traverser.h @@ -37,17 +37,20 @@ public: NodeValueTable ProcessNode(const NodeDependency &dep); -protected: NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range); +protected: virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range); NodeValueTable ProcessInput(const NodeInput* input, const TimeRange &range); - virtual void InputProcessingEvent(NodeInput*, const TimeRange&, NodeValueTable*){} + virtual void FootageProcessingEvent(StreamPtr, const TimeRange&, NodeValueTable*){} virtual void ProcessNodeEvent(const Node*, const TimeRange&, NodeValueDatabase&, NodeValueTable&){} +private: + StreamPtr ResolveStreamFromInput(NodeInput* input); + }; OLIVE_NAMESPACE_EXIT diff --git a/app/panel/param/param.cpp b/app/panel/param/param.cpp index 3c3f64e10..649998cdf 100644 --- a/app/panel/param/param.cpp +++ b/app/panel/param/param.cpp @@ -33,6 +33,7 @@ ParamPanel::ParamPanel(QWidget* parent) : connect(view, &NodeParamView::RequestSelectNode, this, &ParamPanel::RequestSelectNode); connect(view, &NodeParamView::OpenedNode, this, &ParamPanel::OpeningNode); connect(view, &NodeParamView::ClosedNode, this, &ParamPanel::ClosingNode); + connect(view, &NodeParamView::FoundGizmos, this, &ParamPanel::FoundGizmos); SetTimeBasedWidget(view); Retranslate(); diff --git a/app/panel/param/param.h b/app/panel/param/param.h index 0f6d43d20..0227e45ba 100644 --- a/app/panel/param/param.h +++ b/app/panel/param/param.h @@ -45,6 +45,8 @@ signals: void RequestSelectNode(const QList& target); + void FoundGizmos(Node* node); + protected: virtual void Retranslate() override; diff --git a/app/panel/pixelsampler/pixelsamplerpanel.h b/app/panel/pixelsampler/pixelsamplerpanel.h index 089de194c..6e91cce2f 100644 --- a/app/panel/pixelsampler/pixelsamplerpanel.h +++ b/app/panel/pixelsampler/pixelsamplerpanel.h @@ -22,7 +22,7 @@ #define PIXELSAMPLERPANEL_H #include "widget/panel/panel.h" -#include "widget/viewer/pixelsamplerwidget.h" +#include "widget/pixelsampler/pixelsampler.h" OLIVE_NAMESPACE_ENTER diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp index 3cab17f98..9f9e4c84e 100644 --- a/app/panel/viewer/viewerbase.cpp +++ b/app/panel/viewer/viewerbase.cpp @@ -90,6 +90,11 @@ void ViewerPanelBase::SetFullScreen(QScreen *screen) static_cast(GetTimeBasedWidget())->SetFullScreen(screen); } +void ViewerPanelBase::SetGizmos(Node *node) +{ + static_cast(GetTimeBasedWidget())->SetGizmos(node); +} + void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type) { ViewerWidget* vw = static_cast(GetTimeBasedWidget()); diff --git a/app/panel/viewer/viewerbase.h b/app/panel/viewer/viewerbase.h index a0acd2d29..ed4c5651a 100644 --- a/app/panel/viewer/viewerbase.h +++ b/app/panel/viewer/viewerbase.h @@ -56,6 +56,9 @@ public: */ void SetFullScreen(QScreen* screen = nullptr); +public slots: + void SetGizmos(Node* node); + protected: void CreateScopePanel(ScopePanel::Type type); diff --git a/app/render/backend/audiorenderbackend.cpp b/app/render/backend/audiorenderbackend.cpp index 9e819c0c8..368d70195 100644 --- a/app/render/backend/audiorenderbackend.cpp +++ b/app/render/backend/audiorenderbackend.cpp @@ -124,7 +124,7 @@ TimeRange AudioRenderBackend::PopNextFrameFromQueue() return range; } -void AudioRenderBackend::InvalidateCacheInternal(const rational &start_range, const rational &end_range) +void AudioRenderBackend::InvalidateCacheInternal(const rational &start_range, const rational &end_range, bool only_visible) { if (!ic_from_conform_) { // Cancel any ranges waiting on a conform here since obviously the contents have changed @@ -153,7 +153,7 @@ void AudioRenderBackend::InvalidateCacheInternal(const rational &start_range, co } } - RenderBackend::InvalidateCacheInternal(start_range, end_range); + RenderBackend::InvalidateCacheInternal(start_range, end_range, only_visible); } void AudioRenderBackend::ListenForConformSignal(AudioStreamPtr s) diff --git a/app/render/backend/audiorenderbackend.h b/app/render/backend/audiorenderbackend.h index ef6e6a090..6c6dd04f2 100644 --- a/app/render/backend/audiorenderbackend.h +++ b/app/render/backend/audiorenderbackend.h @@ -67,7 +67,7 @@ protected: virtual TimeRange PopNextFrameFromQueue() override; - virtual void InvalidateCacheInternal(const rational &start_range, const rational &end_range) override; + virtual void InvalidateCacheInternal(const rational &start_range, const rational &end_range, bool only_visible) override; private: struct ConformWaitInfo { diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index 05b4e5609..649b2ba15 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -110,7 +110,10 @@ void OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, NodeValueTable* bool has_alpha = PixelFormat::FormatHasAlphaChannel(frame->format()); // Convert frame to float for OCIO - frame = PixelFormat::ConvertPixelFormat(frame, has_alpha ? PixelFormat::PIX_FMT_RGBA32F : PixelFormat::PIX_FMT_RGB32F); + frame = PixelFormat::ConvertPixelFormat(frame, + has_alpha + ? PixelFormat::PIX_FMT_RGBA32F + : PixelFormat::PIX_FMT_RGB32F); // If alpha is associated, disassociate for the color transform if (has_alpha && video_stream->premultiplied_alpha()) { @@ -281,7 +284,25 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, N shader->setUniformValue(variable_location, value.toFloat()); break; case NodeInput::kVec2: - shader->setUniformValue(variable_location, value.value()); + if (input->IsArray()) { + NodeInputArray* array = static_cast(input); + QVector a(array->GetSize()); + + for (int i=0;iAt(i)].Get(NodeParam::kVec2).value(); + } + + shader->setUniformValueArray(variable_location, a.constData(), a.size()); + + int count_location = shader->uniformLocation(QStringLiteral("%1_count").arg(input->id())); + if (count_location > -1) { + shader->setUniformValue(count_location, + array->GetSize()); + } + + } else { + shader->setUniformValue(variable_location, value.value()); + } break; case NodeInput::kVec3: shader->setUniformValue(variable_location, value.value()); diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 8a4acb21e..502f35916 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -259,6 +259,8 @@ void RenderBackend::CacheNext() SetWorkerBusyState(worker, true); cancel_dialog_->WorkerStarted(); + WorkerAboutToStartEvent(worker); + QMetaObject::invokeMethod(worker, "Render", Qt::QueuedConnection, @@ -290,24 +292,14 @@ void RenderBackend::CancelQueue() cancel_dialog_->RunIfWorkersAreBusy(); } +void RenderBackend::InvalidateVisible(const TimeRange &range, NodeInput *from) +{ + InvalidateCacheVeryInternal(range, from, true); +} + void RenderBackend::InvalidateCache(const TimeRange &range, NodeInput *from) { - // Adjust range to min/max values - rational start_range_adj = qMax(rational(0), range.in()); - rational end_range_adj = qMin(GetSequenceLength(), range.out()); - - qDebug() << "Cache invalidated between" - << start_range_adj.toDouble() - << "and" - << end_range_adj.toDouble(); - - if (from) { - // Queue value update - qDebug() << " from" << from->parentNode()->id() << "::" << from->id(); - QueueValueUpdate(from); - } - - InvalidateCacheInternal(start_range_adj, end_range_adj); + InvalidateCacheVeryInternal(range, from, false); } bool RenderBackend::ViewerIsConnected() const @@ -349,6 +341,26 @@ void RenderBackend::SetWorkerBusyState(RenderWorker *worker, bool busy) processor_busy_state_.replace(processors_.indexOf(worker), busy); } +void RenderBackend::InvalidateCacheVeryInternal(const TimeRange &range, NodeInput *from, bool only_visible) +{ + // Adjust range to min/max values + rational start_range_adj = qMax(rational(0), range.in()); + rational end_range_adj = qMin(GetSequenceLength(), range.out()); + + qDebug() << "Cache invalidated between" + << start_range_adj.toDouble() + << "and" + << end_range_adj.toDouble(); + + if (from) { + // Queue value update + qDebug() << " from" << from->parentNode()->id() << "::" << from->id(); + QueueValueUpdate(from); + } + + InvalidateCacheInternal(start_range_adj, end_range_adj, only_visible); +} + void RenderBackend::CopyNodeInputValue(NodeInput *input) { // Find our copy of this parameter @@ -446,8 +458,10 @@ const QVector &RenderBackend::threads() return threads_; } -void RenderBackend::InvalidateCacheInternal(const rational &start_range, const rational &end_range) +void RenderBackend::InvalidateCacheInternal(const rational &start_range, const rational &end_range, bool only_visible) { + Q_UNUSED(only_visible) + // Add the range to the list cache_queue_.InsertTimeRange(TimeRange(start_range, end_range)); @@ -459,6 +473,11 @@ void RenderBackend::CacheIDChangedEvent(const QString &id) Q_UNUSED(id) } +void RenderBackend::WorkerAboutToStartEvent(RenderWorker *worker) +{ + Q_UNUSED(worker) +} + void RenderBackend::InitWorkers() { for (int i=0;iget_standard_value().value(); -} - DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream) { // Access a map of Node inputs and decoder instances and retrieve a frame! @@ -108,21 +103,12 @@ bool RenderWorker::IsStarted() return started_; } -void RenderWorker::InputProcessingEvent(NodeInput* input, const TimeRange& input_time, NodeValueTable *table) +void RenderWorker::FootageProcessingEvent(StreamPtr stream, const TimeRange& input_time, NodeValueTable *table) { - // Exception for Footage types where we actually retrieve some Footage data from a decoder - if (input->data_type() == NodeParam::kFootage) { - StreamPtr stream = ResolveStreamFromInput(input); + DecoderPtr decoder = ResolveDecoderFromInput(stream); - if (stream) { - DecoderPtr decoder = ResolveDecoderFromInput(stream); - - if (decoder) { - - FrameToValue(decoder, stream, input_time, table); - - } - } + if (decoder) { + FrameToValue(decoder, stream, input_time, table); } } diff --git a/app/render/backend/renderworker.h b/app/render/backend/renderworker.h index 17d8e600f..aa03e7af1 100644 --- a/app/render/backend/renderworker.h +++ b/app/render/backend/renderworker.h @@ -59,12 +59,10 @@ protected: virtual void FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table) = 0; - virtual void InputProcessingEvent(NodeInput *input, const TimeRange &input_time, NodeValueTable* table) override; + virtual void FootageProcessingEvent(StreamPtr stream, const TimeRange &input_time, NodeValueTable* table) override; virtual void ProcessNodeEvent(const Node *node, const TimeRange &range, NodeValueDatabase &input_params, NodeValueTable &output_params) override; - StreamPtr ResolveStreamFromInput(NodeInput* input); - DecoderPtr ResolveDecoderFromInput(StreamPtr stream); const NodeDependency& CurrentPath() const; diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index db61fc0da..fee74005c 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -41,7 +41,8 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) : operating_mode_(VideoRenderWorker::kHashRenderCache), only_signal_last_frame_requested_(true), limit_caching_(true), - pop_toggle_(false) + pop_toggle_(false), + queue_is_visible_only_(false) { connect(DiskManager::instance(), &DiskManager::DeletedFrame, this, &VideoRenderBackend::FrameRemovedFromDiskCache); } @@ -152,7 +153,7 @@ void VideoRenderBackend::ConnectWorkerToThis(RenderWorker *processor) connect(video_processor, &VideoRenderWorker::GeneratedFrame, this, &VideoRenderBackend::ThreadGeneratedFrame, Qt::QueuedConnection); } -void VideoRenderBackend::InvalidateCacheInternal(const rational &start_range, const rational &end_range) +void VideoRenderBackend::InvalidateCacheInternal(const rational &start_range, const rational &end_range, bool only_visible) { TimeRange invalidated(start_range, end_range); @@ -160,7 +161,39 @@ void VideoRenderBackend::InvalidateCacheInternal(const rational &start_range, co emit RangeInvalidated(invalidated); - Requeue(); + queue_is_visible_only_ = only_visible; + + if (only_visible) { + + // We're only caching this frame, and for maximum responsiveness, should cancel the rest of the + // queue + cache_queue_.clear(); + cache_queue_.InsertTimeRange(TimeRange(start_range, end_range)); + + CacheNext(); + + } else { + + // Rework the queue + Requeue(); + + } +} + +void VideoRenderBackend::WorkerAboutToStartEvent(RenderWorker *worker) +{ + if (operating_mode_ & VideoRenderWorker::kDownloadOnly) { + int mode = operating_mode_; + + if (queue_is_visible_only_) { + mode &= ~VideoRenderWorker::kDownloadOnly; + } else { + mode |= VideoRenderWorker::kDownloadOnly; + } + + static_cast(worker)-> + SetOperatingMode(static_cast(mode)); + } } VideoRenderFrameCache *VideoRenderBackend::frame_cache() @@ -201,9 +234,11 @@ QString VideoRenderBackend::GetCachedFrame(const rational &time) void VideoRenderBackend::UpdateLastRequestedTime(const rational &time) { - last_time_requested_ = time; + if (last_time_requested_ != time) { + last_time_requested_ = time; - Requeue(); + Requeue(); + } } NodeInput *VideoRenderBackend::GetDependentInput() diff --git a/app/render/backend/videorenderbackend.h b/app/render/backend/videorenderbackend.h index fe7bc6f1b..97b1f09f9 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -99,10 +99,12 @@ protected: virtual void ConnectWorkerToThis(RenderWorker* processor) override; - virtual void InvalidateCacheInternal(const rational &start_range, const rational &end_range) override; + virtual void InvalidateCacheInternal(const rational &start_range, const rational &end_range, bool only_visible) override; virtual void ParamsChangedEvent(){} + virtual void WorkerAboutToStartEvent(RenderWorker* worker) override; + VideoRenderWorker::OperatingMode operating_mode_; signals: @@ -135,6 +137,8 @@ private: bool pop_toggle_; + bool queue_is_visible_only_; + private slots: void ThreadCompletedDownload(NodeDependency dep, qint64 job_time, QByteArray hash); void ThreadSkippedFrame(NodeDependency dep, qint64 job_time, QByteArray hash); diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index ffc568811..74c23ecd1 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -201,20 +201,6 @@ void VideoRenderWorker::ResizeDownloadBuffer() download_buffer_.resize(PixelFormat::GetBufferSize(video_params_.format(), video_params_.effective_width(), video_params_.effective_height())); } -NodeValueTable VideoRenderWorker::RenderBlock(const TrackOutput *track, const TimeRange &range) -{ - // A frame can only have one active block so we just validate the in point of the range - Block* active_block = track->BlockAtTime(range.in()); - - NodeValueTable table; - - if (active_block) { - table = ProcessNode(NodeDependency(active_block, range)); - } - - return table; -} - ColorProcessorCache *VideoRenderWorker::color_cache() { return &color_cache_; diff --git a/app/render/backend/videorenderworker.h b/app/render/backend/videorenderworker.h index a4737fcde..77ba6f2bb 100644 --- a/app/render/backend/videorenderworker.h +++ b/app/render/backend/videorenderworker.h @@ -92,14 +92,12 @@ protected: virtual void ParametersChangedEvent(){} - virtual void TextureToBuffer(const QVariant& texture, void *buffer, int linesize); + void TextureToBuffer(const QVariant& texture, void *buffer, int linesize); virtual void TextureToBuffer(const QVariant& texture, int width, int height, const QMatrix4x4& matrix, void *buffer, int linesize) = 0; virtual NodeValueTable RenderInternal(const NodeDependency& CurrentPath, const qint64& job_time) override; - virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range) override; - ColorProcessorCache* color_cache(); private: diff --git a/app/render/colormanager.cpp b/app/render/colormanager.cpp index 3a907b3ae..5b2cc1838 100644 --- a/app/render/colormanager.cpp +++ b/app/render/colormanager.cpp @@ -49,6 +49,13 @@ OCIO::ConstConfigRcPtr ColorManager::GetConfig() const return config_; } +OCIO::ConstConfigRcPtr ColorManager::CreateConfigFromFile(const QString &filename) +{ + OCIO_SET_C_LOCALE_FOR_SCOPE; + + return OCIO::Config::CreateFromFile(filename.toUtf8()); +} + const QString &ColorManager::GetConfigFilename() const { return config_filename_; @@ -61,7 +68,10 @@ OCIO::ConstConfigRcPtr ColorManager::GetDefaultConfig() void ColorManager::SetUpDefaultConfig() { + OCIO_SET_C_LOCALE_FOR_SCOPE; + if (!qgetenv("OCIO").isEmpty()) { + // Attempt to set config from "OCIO" environment variable try { default_config_ = OCIO::Config::CreateFromEnv(); @@ -71,7 +81,7 @@ void ColorManager::SetUpDefaultConfig() } } - // Kind of hacky, but it'll work + // Extract OCIO config - kind of hacky, but it'll work QString dir = QDir(FileFunctions::GetTempFilePath()).filePath(QStringLiteral("ocioconf")); FileFunctions::CopyDirectory(QStringLiteral(":/ocioconf"), @@ -80,7 +90,7 @@ void ColorManager::SetUpDefaultConfig() qDebug() << "Extracting default OCIO config to" << dir; - default_config_ = OCIO::Config::CreateFromFile(QDir(dir).filePath(QStringLiteral("config.ocio")).toUtf8()); + default_config_ = CreateConfigFromFile(QDir(dir).filePath(QStringLiteral("config.ocio"))); } void ColorManager::SetConfig(const QString &filename) @@ -372,4 +382,15 @@ void ColorManager::AssociateAlphaInternal(ColorManager::AlphaAction action, T *d } } +ColorManager::SetLocale::SetLocale(const char* new_locale) +{ + old_locale_ = setlocale(LC_NUMERIC, nullptr); + setlocale(LC_NUMERIC, new_locale); +} + +ColorManager::SetLocale::~SetLocale() +{ + setlocale(LC_NUMERIC, old_locale_.toUtf8()); +} + OLIVE_NAMESPACE_EXIT diff --git a/app/render/colormanager.h b/app/render/colormanager.h index af6146b86..e4e6ff1d8 100644 --- a/app/render/colormanager.h +++ b/app/render/colormanager.h @@ -26,6 +26,8 @@ #include "codec/frame.h" #include "colorprocessor.h" +#define OCIO_SET_C_LOCALE_FOR_SCOPE ColorManager::SetLocale d("C") + OLIVE_NAMESPACE_ENTER class ColorManager : public QObject @@ -36,6 +38,8 @@ public: OCIO::ConstConfigRcPtr GetConfig() const; + static OCIO::ConstConfigRcPtr CreateConfigFromFile(const QString& filename); + const QString& GetConfigFilename() const; static OCIO::ConstConfigRcPtr GetDefaultConfig(); @@ -90,6 +94,18 @@ public: static void SetOCIOMethodForMode(RenderMode::Mode mode, OCIOMethod method); + class SetLocale + { + public: + SetLocale(const char* new_locale); + + ~SetLocale(); + + private: + QString old_locale_; + + }; + signals: void ConfigChanged(); diff --git a/app/render/colorprocessor.cpp b/app/render/colorprocessor.cpp index 6f4a18e52..4b98a5133 100644 --- a/app/render/colorprocessor.cpp +++ b/app/render/colorprocessor.cpp @@ -44,10 +44,12 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const display_transform->setLooksOverrideEnabled(true); } + OCIO_SET_C_LOCALE_FOR_SCOPE; processor_ = config->GetConfig()->getProcessor(display_transform); } else { + OCIO_SET_C_LOCALE_FOR_SCOPE; processor_ = config->GetConfig()->getProcessor(input.toUtf8(), output.toUtf8()); diff --git a/app/render/ocioconf/CMakeLists.txt b/app/render/ocioconf/CMakeLists.txt index 72fff96aa..faa7b5e40 100644 --- a/app/render/ocioconf/CMakeLists.txt +++ b/app/render/ocioconf/CMakeLists.txt @@ -14,8 +14,16 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +file(GLOB_RECURSE OCIOCONF_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.ocio *.spi3d *.spi1d) +set(QRC_BODY "") +foreach(OCIOCONF_FILE ${OCIOCONF_RESOURCES}) + string(APPEND QRC_BODY "${OCIOCONF_FILE}\n") + configure_file(${OCIOCONF_FILE} ${OCIOCONF_FILE} COPYONLY) +endforeach() +configure_file(ocioconf.qrc.in ocioconf.qrc @ONLY) + set(OLIVE_RESOURCES ${OLIVE_RESOURCES} - render/ocioconf/ocioconf.qrc + ${CMAKE_CURRENT_BINARY_DIR}/ocioconf.qrc PARENT_SCOPE ) diff --git a/app/render/ocioconf/gen-qrc.sh b/app/render/ocioconf/gen-qrc.sh deleted file mode 100755 index 448f6b4b2..000000000 --- a/app/render/ocioconf/gen-qrc.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -ourbasename=$(basename "$0") - -rm ocioconf.qrc -echo "" >> ocioconf.qrc -echo " " >> ocioconf.qrc - -for f in $(find * -type f) -do - if [ "$f" != "CMakeLists.txt" ] && [ "$f" != "ocioconf.qrc" ] && [ "$f" != "$ourbasename" ] - then - echo " $f" >> ocioconf.qrc - fi -done - -echo " " >> ocioconf.qrc -echo "" >> ocioconf.qrc diff --git a/app/render/ocioconf/ocioconf.qrc b/app/render/ocioconf/ocioconf.qrc deleted file mode 100644 index badfdb9b2..000000000 --- a/app/render/ocioconf/ocioconf.qrc +++ /dev/null @@ -1,19 +0,0 @@ - - - config.ocio - looks/Filmic_False_Colour.spi3d - looks/Filmic_to_0-35_1-30.spi1d - looks/Filmic_to_0-48_1-09.spi1d - looks/Filmic_to_0-60_1-04.spi1d - looks/Filmic_to_0-70_1-03.spi1d - looks/Filmic_to_0-85_1-011.spi1d - looks/Filmic_to_0.99_1-0075.spi1d - looks/Filmic_to_1.20_1-00.spi1d - luts/F-Log_to_Linear.spi1d - luts/V-Log_to_linear.spi1d - luts/V3_LogC_400_to_linear.spi1d - luts/V3_LogC_800_to_linear.spi1d - luts/desat65cube.spi3d - luts/sRGB_OETF_to_Linear.spi1d - - diff --git a/app/render/ocioconf/ocioconf.qrc.in b/app/render/ocioconf/ocioconf.qrc.in new file mode 100644 index 000000000..7d9cdfce9 --- /dev/null +++ b/app/render/ocioconf/ocioconf.qrc.in @@ -0,0 +1,5 @@ + + + @QRC_BODY@ + + diff --git a/app/shaders/CMakeLists.txt b/app/shaders/CMakeLists.txt index 762945e1b..a9ad28c12 100644 --- a/app/shaders/CMakeLists.txt +++ b/app/shaders/CMakeLists.txt @@ -14,8 +14,16 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +file(GLOB_RECURSE SHADER_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.frag *.vert) +set(QRC_BODY "") +foreach(SHADER_FILE ${SHADER_RESOURCES}) + string(APPEND QRC_BODY "${SHADER_FILE}\n") + configure_file(${SHADER_FILE} ${SHADER_FILE} COPYONLY) +endforeach() +configure_file(shaders.qrc.in shaders.qrc @ONLY) + set(OLIVE_RESOURCES ${OLIVE_RESOURCES} - shaders/shaders.qrc + ${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc PARENT_SCOPE ) diff --git a/app/shaders/alphaover.xml b/app/shaders/alphaover.xml deleted file mode 100644 index 0f98ea7f0..000000000 --- a/app/shaders/alphaover.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - Alpha Over - - - 3 - - - - A blending node that composites one texture over another using its alpha channel. - - - - - Base - - - Blend - - - - - diff --git a/app/shaders/blur.xml b/app/shaders/blur.xml deleted file mode 100644 index 6942d8749..000000000 --- a/app/shaders/blur.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - Blur - - - 4 - - - - Blurs an image. - - - - - Input - - - - - Method - - - - - - - Radius - 0 - - 10 - - - - - - Horizontal - - 1 - - - - - - Vertical - - 1 - - - - - - Repeat Edge Pixels - - 0 - - - - - - - - 2 - diff --git a/app/shaders/crossdissolve.xml b/app/shaders/crossdissolve.xml deleted file mode 100644 index 3338f6e86..000000000 --- a/app/shaders/crossdissolve.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - Cross Dissolve - - - - - - - A smooth fade transition from one video clip to another. - - - - - diff --git a/app/shaders/diptoblack.xml b/app/shaders/diptoblack.xml deleted file mode 100644 index 1bca1af4e..000000000 --- a/app/shaders/diptoblack.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - Dip to Black - - - - - - - A smooth dip to transparency and back into another clip. - - - - - diff --git a/app/shaders/dropshadow.frag b/app/shaders/dropshadow.frag deleted file mode 100644 index 0a35c7517..000000000 --- a/app/shaders/dropshadow.frag +++ /dev/null @@ -1,62 +0,0 @@ -#version 150 - -#define M_PI 3.1415926535897932384626433832795 - -uniform sampler2D tex_in; -uniform vec3 color_in; -uniform float softness_in; -uniform float opacity_in; -uniform float distance_in; -uniform float direction_in; - -uniform vec2 ove_resolution; - -in vec2 ove_texcoord; - -out vec4 fragColor; - -void main(void) { - // Use pythagoras with the distance (hypotenuse) to find the shadow offset - float direction_radians = direction_in * (M_PI / 180.0); - - float opposite = sin(direction_radians) * distance_in; - float adjacent = cos(direction_radians) * distance_in; - - vec2 angle = vec2(adjacent, opposite); - - // Convert distance from pixels to 0.0 - 1.0 texture coordinates - angle /= ove_resolution; - - float shadow_alpha; - - // For a soft shadow, we use a box blur-like formula - if (softness_in > 0.0) { - float radius = ceil(softness_in); - float divider = 1.0 / pow(softness_in, 2.0); - shadow_alpha = 0.0; - - for (float x = -radius + 0.5; x <= radius; x += 2.0) { - for (float y = -radius + 0.5; y <= radius; y += 2.0) { - vec2 pixel_coord = ove_texcoord - angle; - pixel_coord.x += x / ove_resolution.x; - pixel_coord.y += y / ove_resolution.y; - vec4 pixel_color = texture(tex_in, pixel_coord); - - shadow_alpha += pixel_color.a * divider; - } - } - } else { - // Perfectly hard shadow - vec4 src_color = texture(tex_in, ove_texcoord - angle); - shadow_alpha = src_color.a; - } - - vec4 shadow_px = vec4(color_in, shadow_alpha * opacity_in * 0.01); - - // Get current pixel and perform an alpha over for it over the shadow we've made - vec4 dst_color = texture(tex_in, ove_texcoord); - shadow_px *= (1.0 - dst_color.a); - shadow_px += dst_color; - - fragColor = shadow_px; -} diff --git a/app/shaders/dropshadow.xml b/app/shaders/dropshadow.xml deleted file mode 100644 index 20e77fb84..000000000 --- a/app/shaders/dropshadow.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - Drop Shadow - - - Stylize - Stylise - - - - Generate a drop shadow of a clip. - - - - - Input - - - - - Color - Colour - - - - - Softness - 0 - - 10 - - - - - - Opacity - 0 - - 80 - - 100 - - - - - Distance - 0 - - 10 - - - - - - Direction - - 45 - - - - - - diff --git a/app/shaders/polygon.frag b/app/shaders/polygon.frag new file mode 100644 index 000000000..ae45ddf73 --- /dev/null +++ b/app/shaders/polygon.frag @@ -0,0 +1,44 @@ +#version 150 + +uniform vec2[256] points_in; +uniform int points_in_count; +uniform vec4 color_in; + +uniform vec2 ove_resolution; + +in vec2 ove_texcoord; + +out vec4 fragColor; + +/* +int pnpoly(int npol, float *xp, float *yp, float x, float y) { + int i, j, c = 0; + for (i = 0, j = npol-1; i < npol; j = i++) { + if ((((yp[i] <= y) && (y < yp[j])) || + ((yp[j] <= y) && (y < yp[i]))) && + (x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i])) + c = !c; + } + return c; +} +*/ + +bool pnpoly(vec2 p) { + bool c = false; + int i, j; + for (i = 0, j = points_in_count-1; i < points_in_count; j = i++) { + if ((((points_in[i].y <= p.y) && (p.y < points_in[j].y)) || + ((points_in[j].y <= p.y) && (p.y < points_in[i].y))) && + (p.x < (points_in[j].x - points_in[i].x) * (p.y - points_in[i].y) / (points_in[j].y - points_in[i].y) + points_in[i].x)) + c = !c; + } + return c; +} + +void main(void) { + if (points_in_count > 0 && pnpoly(ove_texcoord * ove_resolution)) { + fragColor = color_in; + } else { + fragColor = vec4(0.0, 0.0, 0.0, 0.0); + } +} diff --git a/app/shaders/rgbwaveform.frag b/app/shaders/rgbwaveform.frag index 3e839b386..4e6b13f08 100644 --- a/app/shaders/rgbwaveform.frag +++ b/app/shaders/rgbwaveform.frag @@ -52,7 +52,7 @@ void main(void) { float waveform_x = (ove_texcoord.x - waveform_uv.x) / waveform_scale; float waveform_y = (ove_texcoord.y - waveform_uv.y) / waveform_scale; for (int i = 0; i < waveform_dims.y; i++) { - ratio = float(i) / float(waveform_dims.y); + ratio = float(i) / float(waveform_dims.y - 1); cur_col = texture( ove_maintex, vec2(waveform_x, ratio) diff --git a/app/shaders/shaders.qrc b/app/shaders/shaders.qrc deleted file mode 100644 index 7e6f4876c..000000000 --- a/app/shaders/shaders.qrc +++ /dev/null @@ -1,22 +0,0 @@ - - - alphaover.frag - alphaover.xml - blur.frag - blur.xml - colorgradient.frag - colorwheel.frag - crossdissolve.frag - crossdissolve.xml - dropshadow.frag - dropshadow.xml - diptoblack.frag - diptoblack.xml - rgbwaveform.frag - solid.frag - solid.xml - stroke.frag - stroke.xml - matrix.vert - - diff --git a/app/shaders/shaders.qrc.in b/app/shaders/shaders.qrc.in new file mode 100644 index 000000000..e1366f006 --- /dev/null +++ b/app/shaders/shaders.qrc.in @@ -0,0 +1,5 @@ + + + @QRC_BODY@ + + diff --git a/app/shaders/solid.xml b/app/shaders/solid.xml deleted file mode 100644 index d45fef56f..000000000 --- a/app/shaders/solid.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - Solid - - - Generator - - - - Generate a solid color. - - - Generate a solid colour. - - - - - Color - Colour - - 1.0 - 0.0 - 0.0 - 1.0 - - - - - - diff --git a/app/shaders/stroke.frag b/app/shaders/stroke.frag index 8d43b34ea..bdd7d2691 100644 --- a/app/shaders/stroke.frag +++ b/app/shaders/stroke.frag @@ -2,7 +2,7 @@ // Node parameter inputs uniform sampler2D tex_in; -uniform vec3 color_in; +uniform vec4 color_in; uniform float radius_in; uniform float opacity_in; uniform bool inner_in; @@ -61,15 +61,14 @@ void main(void) { } } - stroke_weight *= opacity_in * 0.01; + stroke_weight *= opacity_in; if (inner_in) { stroke_weight *= pixel_here.a; } // Make RGBA color - vec4 stroke_col = vec4(vec3(1.0) * stroke_weight, stroke_weight); - //vec4 stroke_col = vec4(color_in * stroke_weight, stroke_weight); + vec4 stroke_col = color_in * stroke_weight; if (inner_in) { // Alpha over the stroke over the texture diff --git a/app/shaders/stroke.xml b/app/shaders/stroke.xml deleted file mode 100644 index 1f562eef0..000000000 --- a/app/shaders/stroke.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - Stroke - - - Stylize - Stylise - - - - Creates a stroke outline around an image. - - - - - Input - - - - - Color - Colour - - - - - Radius - 0 - - 10 - - - - - - Opacity - 0 - - 100 - - 100 - - - - - Inner - - false - - - - - - diff --git a/app/widget/CMakeLists.txt b/app/widget/CMakeLists.txt index f33f5d064..ddc6dfb72 100644 --- a/app/widget/CMakeLists.txt +++ b/app/widget/CMakeLists.txt @@ -31,6 +31,7 @@ add_subdirectory(nodecopypaste) add_subdirectory(nodeview) add_subdirectory(nodeparamview) add_subdirectory(panel) +add_subdirectory(pixelsampler) add_subdirectory(playbackcontrols) add_subdirectory(projectexplorer) add_subdirectory(projecttoolbar) @@ -41,6 +42,7 @@ add_subdirectory(taskview) add_subdirectory(timebased) add_subdirectory(timelinewidget) add_subdirectory(timeruler) +add_subdirectory(timetarget) add_subdirectory(toolbar) add_subdirectory(viewer) diff --git a/app/widget/keyframeview/CMakeLists.txt b/app/widget/keyframeview/CMakeLists.txt index 17f7bce21..713f9bb20 100644 --- a/app/widget/keyframeview/CMakeLists.txt +++ b/app/widget/keyframeview/CMakeLists.txt @@ -24,7 +24,5 @@ set(OLIVE_SOURCES widget/keyframeview/keyframeviewitem.cpp widget/keyframeview/keyframeviewundo.h widget/keyframeview/keyframeviewundo.cpp - widget/keyframeview/timetargetobject.h - widget/keyframeview/timetargetobject.cpp PARENT_SCOPE ) diff --git a/app/widget/keyframeview/keyframeviewbase.h b/app/widget/keyframeview/keyframeviewbase.h index 000394641..04e7d6096 100644 --- a/app/widget/keyframeview/keyframeviewbase.h +++ b/app/widget/keyframeview/keyframeviewbase.h @@ -23,7 +23,7 @@ #include "keyframeviewitem.h" #include "node/keyframe.h" -#include "timetargetobject.h" +#include "widget/timetarget/timetarget.h" #include "widget/curvewidget/beziercontrolpointitem.h" #include "widget/timelinewidget/view/timelineviewbase.h" diff --git a/app/widget/keyframeview/keyframeviewitem.h b/app/widget/keyframeview/keyframeviewitem.h index ca5a6d660..ee5c06031 100644 --- a/app/widget/keyframeview/keyframeviewitem.h +++ b/app/widget/keyframeview/keyframeviewitem.h @@ -24,7 +24,7 @@ #include #include "node/keyframe.h" -#include "timetargetobject.h" +#include "widget/timetarget/timetarget.h" OLIVE_NAMESPACE_ENTER diff --git a/app/widget/nodeparamview/CMakeLists.txt b/app/widget/nodeparamview/CMakeLists.txt index 393759ba4..58f5320e4 100644 --- a/app/widget/nodeparamview/CMakeLists.txt +++ b/app/widget/nodeparamview/CMakeLists.txt @@ -18,6 +18,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} widget/nodeparamview/nodeparamview.h widget/nodeparamview/nodeparamview.cpp + widget/nodeparamview/nodeparamviewarraywidget.h + widget/nodeparamview/nodeparamviewarraywidget.cpp widget/nodeparamview/nodeparamviewconnectedlabel.h widget/nodeparamview/nodeparamviewconnectedlabel.cpp widget/nodeparamview/nodeparamviewitem.h diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 0ddc3e2ed..eaae8d0eb 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -127,7 +127,7 @@ void NodeParamView::SetNodes(QList nodes) // If we already have item widgets, delete them all now foreach (NodeParamViewItem* item, items_) { emit ClosedNode(item->GetNode()); - + emit FoundGizmos(nullptr); delete item; } items_.clear(); @@ -142,6 +142,8 @@ void NodeParamView::SetNodes(QList nodes) if (!nodes_.isEmpty()) { // For each node, create a widget + bool found_gizmos = false; + foreach (Node* node, nodes_) { NodeParamViewItem* item = new NodeParamViewItem(node); @@ -161,6 +163,11 @@ void NodeParamView::SetNodes(QList nodes) Qt::QueuedConnection); emit OpenedNode(node); + + if (!found_gizmos && node->HasGizmos()) { + emit FoundGizmos(node); + found_gizmos = true; + } } ViewerOutput* viewer = nodes_.first()->FindOutputNode(); diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index 6f71b0994..fd6fe4220 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -55,6 +55,8 @@ signals: void ClosedNode(Node* n); + void FoundGizmos(Node* n); + protected: virtual void resizeEvent(QResizeEvent *event) override; diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp new file mode 100644 index 000000000..46418bb92 --- /dev/null +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp @@ -0,0 +1,55 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "nodeparamviewarraywidget.h" + +#include + +OLIVE_NAMESPACE_ENTER + +NodeParamViewArrayWidget::NodeParamViewArrayWidget(NodeInputArray* array, QWidget* parent) : + QWidget(parent), + array_(array) +{ + QHBoxLayout* layout = new QHBoxLayout(this); + + count_lbl_ = new QLabel(); + layout->addWidget(count_lbl_, 1); + + plus_btn_ = new QPushButton(tr("+")); + layout->addWidget(plus_btn_); + + connect(plus_btn_, &QPushButton::clicked, this, &NodeParamViewArrayWidget::AddElement); + connect(array_, &NodeInputArray::SizeChanged, this, &NodeParamViewArrayWidget::UpdateCounter); + + UpdateCounter(); +} + +void NodeParamViewArrayWidget::UpdateCounter() +{ + count_lbl_->setText(tr("%1 elements").arg(array_->GetSize())); +} + +void NodeParamViewArrayWidget::AddElement() +{ + array_->Append(); +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.h b/app/widget/nodeparamview/nodeparamviewarraywidget.h new file mode 100644 index 000000000..e99455140 --- /dev/null +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.h @@ -0,0 +1,54 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef NODEPARAMVIEWARRAYWIDGET_H +#define NODEPARAMVIEWARRAYWIDGET_H + +#include +#include +#include + +#include "node/inputarray.h" + +OLIVE_NAMESPACE_ENTER + +class NodeParamViewArrayWidget : public QWidget +{ + Q_OBJECT +public: + NodeParamViewArrayWidget(NodeInputArray* array, QWidget* parent = nullptr); + +private: + NodeInputArray* array_; + + QLabel* count_lbl_; + + QPushButton* plus_btn_; + +private slots: + void UpdateCounter(); + + void AddElement(); + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // NODEPARAMVIEWARRAYWIDGET_H diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 326f674b3..262b2ee22 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -167,6 +167,7 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, content_layout->addLayout(array_label_layout, row_count, 0); NodeParamViewItemBody* sub_body = new NodeParamViewItemBody(static_cast(input)->sub_params()); + sub_bodies_.append(sub_body); sub_body->layout()->setMargin(0); content_layout->addWidget(sub_body, row_count + 1, 0, 1, max_col + 1); diff --git a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h index 395251099..002b00fc5 100644 --- a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h +++ b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h @@ -25,7 +25,7 @@ #include #include "node/input.h" -#include "widget/keyframeview/timetargetobject.h" +#include "widget/timetarget/timetarget.h" OLIVE_NAMESPACE_ENTER diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index aaf1fefba..67778be99 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -29,6 +29,7 @@ #include "core.h" #include "node/node.h" +#include "nodeparamviewarraywidget.h" #include "nodeparamviewundo.h" #include "project/item/sequence/sequence.h" #include "undo/undostack.h" @@ -67,113 +68,122 @@ const QList &NodeParamViewWidgetBridge::widgets() const void NodeParamViewWidgetBridge::CreateWidgets() { - // We assume the first data type is the "primary" type - switch (input_->data_type()) { - // None of these inputs have applicable UI widgets - case NodeParam::kNone: - case NodeParam::kAny: - case NodeParam::kTexture: - case NodeParam::kMatrix: - case NodeParam::kRational: - case NodeParam::kSamples: - case NodeParam::kDecimal: - case NodeParam::kNumber: - case NodeParam::kString: - case NodeParam::kBuffer: - case NodeParam::kVector: - break; - case NodeParam::kInt: - { - IntegerSlider* slider = new IntegerSlider(); - widgets_.append(slider); - connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } - case NodeParam::kFloat: - { - CreateSliders(1); - break; - } - case NodeParam::kVec2: - { - CreateSliders(2); - break; - } - case NodeParam::kVec3: - { - CreateSliders(3); - break; - } - case NodeParam::kVec4: - { - CreateSliders(4); - break; - } - case NodeParam::kCombo: - { - QComboBox* combobox = new QComboBox(); + if (input_->IsArray()) { - QStringList items = input_->get_combobox_strings(); - foreach (const QString& s, items) { - combobox->addItem(s); + NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(static_cast(input_)); + widgets_.append(w); + + } else { + + // We assume the first data type is the "primary" type + switch (input_->data_type()) { + // None of these inputs have applicable UI widgets + case NodeParam::kNone: + case NodeParam::kAny: + case NodeParam::kTexture: + case NodeParam::kMatrix: + case NodeParam::kRational: + case NodeParam::kSamples: + case NodeParam::kDecimal: + case NodeParam::kNumber: + case NodeParam::kString: + case NodeParam::kBuffer: + case NodeParam::kVector: + break; + case NodeParam::kInt: + { + IntegerSlider* slider = new IntegerSlider(); + widgets_.append(slider); + connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); + break; + } + case NodeParam::kFloat: + { + CreateSliders(1); + break; + } + case NodeParam::kVec2: + { + CreateSliders(2); + break; + } + case NodeParam::kVec3: + { + CreateSliders(3); + break; + } + case NodeParam::kVec4: + { + CreateSliders(4); + break; + } + case NodeParam::kCombo: + { + QComboBox* combobox = new QComboBox(); + + QStringList items = input_->get_combobox_strings(); + foreach (const QString& s, items) { + combobox->addItem(s); + } + + widgets_.append(combobox); + connect(combobox, static_cast(&QComboBox::currentIndexChanged), this, &NodeParamViewWidgetBridge::WidgetCallback); + break; + } + case NodeParam::kFile: + // FIXME: File selector + break; + case NodeParam::kColor: + { + // NOTE: Very convoluted way to get back to the project's color manager + ColorButton* color_button = new ColorButton(static_cast(input_->parentNode()->parent())->project()->color_manager()); + widgets_.append(color_button); + connect(color_button, &ColorButton::ColorChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); + break; + } + case NodeParam::kText: + { + QLineEdit* line_edit = new QLineEdit(); + widgets_.append(line_edit); + connect(line_edit, &QLineEdit::textEdited, this, &NodeParamViewWidgetBridge::WidgetCallback); + break; + } + case NodeParam::kBoolean: + { + QCheckBox* check_box = new QCheckBox(); + widgets_.append(check_box); + connect(check_box, &QCheckBox::clicked, this, &NodeParamViewWidgetBridge::WidgetCallback); + break; + } + case NodeParam::kFont: + { + QFontComboBox* font_combobox = new QFontComboBox(); + widgets_.append(font_combobox); + break; + } + case NodeParam::kFootage: + { + FootageComboBox* footage_combobox = new FootageComboBox(); + footage_combobox->SetRoot(static_cast(input_->parentNode()->parent())->project()->root()); + + connect(footage_combobox, &FootageComboBox::FootageChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); + + widgets_.append(footage_combobox); + + break; + } } - widgets_.append(combobox); - connect(combobox, static_cast(&QComboBox::currentIndexChanged), this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } - case NodeParam::kFile: - // FIXME: File selector - break; - case NodeParam::kColor: - { - // NOTE: Very convoluted way to get back to the project's color manager - ColorButton* color_button = new ColorButton(static_cast(input_->parentNode()->parent())->project()->color_manager()); - widgets_.append(color_button); - connect(color_button, &ColorButton::ColorChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } - case NodeParam::kText: - { - QLineEdit* line_edit = new QLineEdit(); - widgets_.append(line_edit); - connect(line_edit, &QLineEdit::textEdited, this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } - case NodeParam::kBoolean: - { - QCheckBox* check_box = new QCheckBox(); - widgets_.append(check_box); - connect(check_box, &QCheckBox::clicked, this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } - case NodeParam::kFont: - { - QFontComboBox* font_combobox = new QFontComboBox(); - widgets_.append(font_combobox); - break; - } - case NodeParam::kFootage: - { - FootageComboBox* footage_combobox = new FootageComboBox(); - footage_combobox->SetRoot(static_cast(input_->parentNode()->parent())->project()->root()); + // Check all properties + QHash::const_iterator iterator; - connect(footage_combobox, &FootageComboBox::FootageChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); + for (iterator=input_->properties().begin();iterator!=input_->properties().end();iterator++) { + PropertyChanged(iterator.key(), iterator.value()); + } - widgets_.append(footage_combobox); + UpdateWidgetValues(); - break; } - } - - // Check all properties - QHash::const_iterator iterator; - - for (iterator=input_->properties().begin();iterator!=input_->properties().end();iterator++) { - PropertyChanged(iterator.key(), iterator.value()); - } - - UpdateWidgetValues(); } void NodeParamViewWidgetBridge::SetInputValue(const QVariant &value, int track) @@ -413,6 +423,10 @@ void NodeParamViewWidgetBridge::CreateSliders(int count) void NodeParamViewWidgetBridge::UpdateWidgetValues() { + if (input_->IsArray()) { + return; + } + rational node_time = GetCurrentTimeAsNodeTime(); // We assume the first data type is the "primary" type diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index d1543c73e..3d385a7bc 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -24,8 +24,8 @@ #include #include "node/input.h" -#include "widget/keyframeview/timetargetobject.h" #include "widget/slider/sliderbase.h" +#include "widget/timetarget/timetarget.h" OLIVE_NAMESPACE_ENTER diff --git a/app/widget/pixelsampler/CMakeLists.txt b/app/widget/pixelsampler/CMakeLists.txt new file mode 100644 index 000000000..ecf7831f7 --- /dev/null +++ b/app/widget/pixelsampler/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/pixelsampler/pixelsampler.h + widget/pixelsampler/pixelsampler.cpp + PARENT_SCOPE +) diff --git a/app/widget/viewer/pixelsamplerwidget.cpp b/app/widget/pixelsampler/pixelsampler.cpp similarity index 98% rename from app/widget/viewer/pixelsamplerwidget.cpp rename to app/widget/pixelsampler/pixelsampler.cpp index c955d21ed..22cde93f5 100644 --- a/app/widget/viewer/pixelsamplerwidget.cpp +++ b/app/widget/pixelsampler/pixelsampler.cpp @@ -18,7 +18,7 @@ ***/ -#include "pixelsamplerwidget.h" +#include "pixelsampler.h" #include diff --git a/app/widget/viewer/pixelsamplerwidget.h b/app/widget/pixelsampler/pixelsampler.h similarity index 100% rename from app/widget/viewer/pixelsamplerwidget.h rename to app/widget/pixelsampler/pixelsampler.h diff --git a/app/widget/scope/CMakeLists.txt b/app/widget/scope/CMakeLists.txt index a14b351dd..91c4f0b44 100644 --- a/app/widget/scope/CMakeLists.txt +++ b/app/widget/scope/CMakeLists.txt @@ -15,6 +15,7 @@ # along with this program. If not, see . add_subdirectory(histogram) +add_subdirectory(scopebase) add_subdirectory(waveform) set(OLIVE_SOURCES diff --git a/app/widget/scope/histogram/histogram.cpp b/app/widget/scope/histogram/histogram.cpp index b9028f85a..9665e32de 100644 --- a/app/widget/scope/histogram/histogram.cpp +++ b/app/widget/scope/histogram/histogram.cpp @@ -29,189 +29,8 @@ OLIVE_NAMESPACE_ENTER HistogramScope::HistogramScope(QWidget* parent) : - ManagedDisplayWidget(parent), - buffer_(nullptr) + ScopeBase(parent) { - EnableDefaultContextMenu(); - - connect(&worker_, &HistogramScopeWorker::Finished, this, &HistogramScope::FinishedProcessing, Qt::QueuedConnection); - worker_.start(QThread::IdlePriority); -} - -HistogramScope::~HistogramScope() -{ - worker_.Cancel(); - worker_.quit(); - worker_.wait(); -} - -void HistogramScope::SetBuffer(Frame* frame) -{ - buffer_ = frame; - - if (isVisible()) { - StartUpdate(); - } -} - -void HistogramScope::FinishedProcessing(QVector red, QVector green, QVector blue) -{ - red_val_ = red; - green_val_ = green; - blue_val_ = blue; - - update(); -} - -void HistogramScope::paintGL() -{ - QVector red_lines(red_val_.size()); - QVector green_lines(green_val_.size()); - QVector blue_lines(blue_val_.size()); - - for (int i=0;i data(w * kRGBChannels, 0); - - int max_w = w-1; - - for (int x=0;xConvertColor(c); - } - - data[qFloor(clamp(c.red(), 0.0f, 1.0f) * max_w)]++; - data[qFloor(clamp(c.green(), 0.0f, 1.0f) * max_w) + w]++; - data[qFloor(clamp(c.blue(), 0.0f, 1.0f) * max_w) + w * 2]++; - } - } - - int max_val = 0; - - foreach (const int& i, data) { - if (i > max_val) { - max_val = i; - } - } - - if (!max_val) { - // Prevent divide by zero - return; - } - - QVector red_lines(w); - QVector green_lines(w); - QVector blue_lines(w); - - for (int i=0;i(data.at(i)) / static_cast(max_val)); - green_lines.replace(i, static_cast(data.at(i + w)) / static_cast(max_val)); - blue_lines.replace(i, static_cast(data.at(i + w * 2)) / static_cast(max_val)); - } - - emit Finished(red_lines, green_lines, blue_lines); - } -} - -void HistogramScopeWorker::QueueNext(const Frame &f, ColorProcessorPtr processor, int width) -{ - next_lock_.lock(); - - next_ = f; - next_width_ = width; - next_processor_ = processor; - - next_wait_.wakeOne(); - - next_lock_.unlock(); -} - -void HistogramScopeWorker::Cancel() -{ - cancelled_ = true; - next_lock_.lock(); - next_wait_.wakeOne(); - next_lock_.unlock(); } OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/histogram/histogram.h b/app/widget/scope/histogram/histogram.h index 3efad3919..625074a89 100644 --- a/app/widget/scope/histogram/histogram.h +++ b/app/widget/scope/histogram/histogram.h @@ -21,80 +21,20 @@ #ifndef HISTOGRAMSCOPE_H #define HISTOGRAMSCOPE_H -#include -#include -#include - -#include "codec/frame.h" -#include "render/colorprocessor.h" -#include "widget/manageddisplay/manageddisplay.h" +#include "widget/scope/scopebase/scopebase.h" OLIVE_NAMESPACE_ENTER -class HistogramScopeWorker : public QThread -{ - Q_OBJECT -public: - HistogramScopeWorker(); - - // Thread-safe - void QueueNext(const Frame& f, ColorProcessorPtr processor, int width); - - // Thread-safe - void Cancel(); - -protected: - virtual void run() override; - -signals: - void Finished(QVector red, QVector green, QVector blue); - -private: - QAtomicInt cancelled_; - - QMutex next_lock_; - QWaitCondition next_wait_; - Frame next_; - int next_width_; - ColorProcessorPtr next_processor_; - -}; - -class HistogramScope : public ManagedDisplayWidget +class HistogramScope : public ScopeBase { Q_OBJECT public: HistogramScope(QWidget* parent = nullptr); - virtual ~HistogramScope() override; - -public slots: - void SetBuffer(Frame* frame); - protected: - virtual void paintGL() override; + //virtual OpenGLShaderPtr CreateShader() override; - virtual void resizeEvent(QResizeEvent* e) override; - - virtual void ColorProcessorChangedEvent() override; - - virtual void showEvent(QShowEvent* e) override; - -private: - void StartUpdate(); - - Frame* buffer_; - - QVector red_val_; - - QVector green_val_; - - QVector blue_val_; - - HistogramScopeWorker worker_; - -private slots: - void FinishedProcessing(QVector red, QVector green, QVector blue); + //virtual void DrawScope() override; }; diff --git a/app/widget/scope/scopebase/CMakeLists.txt b/app/widget/scope/scopebase/CMakeLists.txt new file mode 100644 index 000000000..ca6d924c3 --- /dev/null +++ b/app/widget/scope/scopebase/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/scope/scopebase/scopebase.h + widget/scope/scopebase/scopebase.cpp + PARENT_SCOPE +) diff --git a/app/widget/scope/scopebase/scopebase.cpp b/app/widget/scope/scopebase/scopebase.cpp new file mode 100644 index 000000000..c58b9e8bd --- /dev/null +++ b/app/widget/scope/scopebase/scopebase.cpp @@ -0,0 +1,163 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "scopebase.h" + +#include "render/backend/opengl/openglrenderfunctions.h" + +OLIVE_NAMESPACE_ENTER + +ScopeBase::ScopeBase(QWidget* parent) : + ManagedDisplayWidget(parent), + buffer_(nullptr) +{ + EnableDefaultContextMenu(); +} + +ScopeBase::~ScopeBase() +{ + CleanUp(); + + if (context()) { + disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ScopeBase::CleanUp); + } +} + +void ScopeBase::SetBuffer(Frame *frame) +{ + buffer_ = frame; + + UploadTextureFromBuffer(); +} + +void ScopeBase::showEvent(QShowEvent* e) +{ + ManagedDisplayWidget::showEvent(e); + + UploadTextureFromBuffer(); +} + +OpenGLShaderPtr ScopeBase::CreateShader() +{ + return OpenGLShader::CreateDefault(); +} + +void ScopeBase::DrawScope() +{ + managed_tex().Bind(); + + OpenGLRenderFunctions::Blit(pipeline()); + + managed_tex().Release(); +} + +OpenGLShaderPtr ScopeBase::pipeline() +{ + return pipeline_; +} + +OpenGLTexture &ScopeBase::managed_tex() +{ + return managed_tex_; +} + +void ScopeBase::UploadTextureFromBuffer() +{ + if (!isVisible()) { + return; + } + + if (buffer_) { + makeCurrent(); + + if (!texture_.IsCreated() + || texture_.width() != buffer_->width() + || texture_.height() != buffer_->height() + || texture_.format() != buffer_->format()) { + texture_.Destroy(); + managed_tex_.Destroy(); + + texture_.Create(context(), buffer_); + managed_tex_.Create(context(), buffer_->video_params()); + } else { + texture_.Upload(buffer_); + } + + doneCurrent(); + } + + update(); +} + +void ScopeBase::CleanUp() +{ + makeCurrent(); + + pipeline_ = nullptr; + texture_.Destroy(); + managed_tex_.Destroy(); + framebuffer_.Destroy(); + + doneCurrent(); +} + +void ScopeBase::initializeGL() +{ + ManagedDisplayWidget::initializeGL(); + + pipeline_ = CreateShader(); + + framebuffer_.Create(context()); + + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ScopeBase::CleanUp, Qt::DirectConnection); + + UploadTextureFromBuffer(); +} + +void ScopeBase::paintGL() +{ + QOpenGLFunctions* f = context()->functions(); + + f->glClearColor(0, 0, 0, 0); + f->glClear(GL_COLOR_BUFFER_BIT); + + if (buffer_ && pipeline() && texture_.IsCreated()) { + // Convert reference frame to display space + framebuffer_.Attach(&managed_tex_); + framebuffer_.Bind(); + + texture_.Bind(); + + f->glViewport(0, 0, texture_.width(), texture_.height()); + + color_service()->ProcessOpenGL(); + + texture_.Release(); + + framebuffer_.Release(); + framebuffer_.Detach(); + + f->glViewport(0, 0, width(), height()); + + DrawScope(); + } +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/scopebase/scopebase.h b/app/widget/scope/scopebase/scopebase.h new file mode 100644 index 000000000..3098af212 --- /dev/null +++ b/app/widget/scope/scopebase/scopebase.h @@ -0,0 +1,78 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef SCOPEBASE_H +#define SCOPEBASE_H + +#include "codec/frame.h" +#include "render/backend/opengl/openglcolorprocessor.h" +#include "render/backend/opengl/openglframebuffer.h" +#include "render/backend/opengl/openglshader.h" +#include "render/backend/opengl/opengltexture.h" +#include "widget/manageddisplay/manageddisplay.h" + +OLIVE_NAMESPACE_ENTER + +class ScopeBase : public ManagedDisplayWidget +{ +public: + ScopeBase(QWidget* parent = nullptr); + + virtual ~ScopeBase() override; + +public slots: + void SetBuffer(Frame* frame); + +protected: + virtual void initializeGL() override; + + virtual void paintGL() override; + + virtual void showEvent(QShowEvent* e) override; + + virtual OpenGLShaderPtr CreateShader(); + + virtual void DrawScope(); + + OpenGLShaderPtr pipeline(); + + OpenGLTexture& managed_tex(); + +private: + void UploadTextureFromBuffer(); + + OpenGLShaderPtr pipeline_; + + OpenGLTexture texture_; + + OpenGLTexture managed_tex_; + + OpenGLFramebuffer framebuffer_; + + Frame* buffer_; + +private slots: + void CleanUp(); + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // SCOPEBASE_H diff --git a/app/widget/scope/waveform/waveform.cpp b/app/widget/scope/waveform/waveform.cpp index 5f9225b3f..daefe84e7 100644 --- a/app/widget/scope/waveform/waveform.cpp +++ b/app/widget/scope/waveform/waveform.cpp @@ -32,59 +32,24 @@ OLIVE_NAMESPACE_ENTER WaveformScope::WaveformScope(QWidget* parent) : - ManagedDisplayWidget(parent), - buffer_(nullptr) + ScopeBase(parent) { - EnableDefaultContextMenu(); } -WaveformScope::~WaveformScope() +OpenGLShaderPtr WaveformScope::CreateShader() { - CleanUp(); + OpenGLShaderPtr pipeline = OpenGLShader::Create(); - if (context()) { - disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &WaveformScope::CleanUp); - } + pipeline->create(); + pipeline->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex()); + pipeline->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(":/shaders/rgbwaveform.frag")); + pipeline->link(); + + return pipeline; } -void WaveformScope::SetBuffer(Frame *frame) +void WaveformScope::DrawScope() { - buffer_ = frame; - - UploadTextureFromBuffer(); -} - -void WaveformScope::showEvent(QShowEvent* e) -{ - ManagedDisplayWidget::showEvent(e); - - UploadTextureFromBuffer(); -} - -void WaveformScope::initializeGL() -{ - ManagedDisplayWidget::initializeGL(); - - pipeline_ = OpenGLShader::Create(); - pipeline_->create(); - pipeline_->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex()); - pipeline_->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(":/shaders/rgbwaveform.frag")); - pipeline_->link(); - - framebuffer_.Create(context()); - - connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &WaveformScope::CleanUp, Qt::DirectConnection); - - UploadTextureFromBuffer(); -} - -void WaveformScope::paintGL() -{ - QOpenGLFunctions* f = context()->functions(); - - f->glClearColor(0, 0, 0, 0); - f->glClear(GL_COLOR_BUFFER_BIT); - float waveform_scale = 0.80f; float waveform_dim_x = width() * waveform_scale; float waveform_dim_y = height() * waveform_scale; @@ -93,66 +58,47 @@ void WaveformScope::paintGL() float waveform_end_dim_x = width() - waveform_start_dim_x; float waveform_end_dim_y = height() - waveform_start_dim_y; - if (buffer_ && pipeline_ && texture_.IsCreated()) { - // Convert reference frame to display space - framebuffer_.Attach(&managed_tex_); - framebuffer_.Bind(); + // Draw waveform through shader + pipeline()->bind(); + pipeline()->setUniformValue("ove_resolution", managed_tex().width(), managed_tex().height()); + pipeline()->setUniformValue("ove_viewport", width(), height()); + GLfloat luma[3] = {0.0, 0.0, 0.0}; + color_manager()->GetDefaultLumaCoefs(luma); + pipeline()->setUniformValue("luma_coeffs", luma[0], luma[1], luma[2]); - texture_.Bind(); + // Scale of the waveform relative to the viewport surface. + pipeline()->setUniformValue("waveform_scale", waveform_scale); + pipeline()->setUniformValue( + "waveform_dims", waveform_dim_x, waveform_dim_y); - f->glViewport(0, 0, texture_.width(), texture_.height()); + pipeline()->setUniformValue( + "waveform_region", + waveform_start_dim_x, waveform_start_dim_y, + waveform_end_dim_x, waveform_end_dim_y); - color_service()->ProcessOpenGL(); + float waveform_start_uv_x = waveform_start_dim_x / width(); + float waveform_start_uv_y = waveform_start_dim_y / height(); + float waveform_end_uv_x = waveform_end_dim_x / width(); + float waveform_end_uv_y = waveform_end_dim_y / height(); + pipeline()->setUniformValue( + "waveform_uv", + waveform_start_uv_x, waveform_start_uv_y, + waveform_end_uv_x, waveform_end_uv_y); - texture_.Release(); + pipeline()->release(); - framebuffer_.Release(); - framebuffer_.Detach(); + managed_tex().Bind(); - // Draw waveform through shader - pipeline_->bind(); - pipeline_->setUniformValue("ove_resolution", texture_.width(), texture_.height()); - pipeline_->setUniformValue("ove_viewport", width(), height()); - GLfloat luma[3] = {0.0, 0.0, 0.0}; - color_manager()->GetDefaultLumaCoefs(luma); - pipeline_->setUniformValue("luma_coeffs", luma[0], luma[1], luma[2]); + OpenGLRenderFunctions::Blit(pipeline()); - // Scale of the waveform relative to the viewport surface. - pipeline_->setUniformValue("waveform_scale", waveform_scale); - pipeline_->setUniformValue( - "waveform_dims", waveform_dim_x, waveform_dim_y); - - pipeline_->setUniformValue( - "waveform_region", - waveform_start_dim_x, waveform_start_dim_y, - waveform_end_dim_x, waveform_end_dim_y); - - float waveform_start_uv_x = waveform_start_dim_x / width(); - float waveform_start_uv_y = waveform_start_dim_y / height(); - float waveform_end_uv_x = waveform_end_dim_x / width(); - float waveform_end_uv_y = waveform_end_dim_y / height(); - pipeline_->setUniformValue( - "waveform_uv", - waveform_start_uv_x, waveform_start_uv_y, - waveform_end_uv_x, waveform_end_uv_y); - - pipeline_->release(); - - f->glViewport(0, 0, width(), height()); - - managed_tex_.Bind(); - - OpenGLRenderFunctions::Blit(pipeline_); - - managed_tex_.Release(); - } + managed_tex().Release(); // Draw line overlays QPainter p(this); QFontMetrics font_metrics = QFontMetrics(QFont()); QString label; float ire_increment = 0.1f; - float ire_steps = int(1.0 / ire_increment); + int ire_steps = qRound(1.0 / ire_increment); QVector ire_lines(ire_steps + 1); int font_x_offset = 0; int font_y_offset = font_metrics.capHeight() / 2.0f; @@ -179,44 +125,4 @@ void WaveformScope::paintGL() p.drawLines(ire_lines); } -void WaveformScope::UploadTextureFromBuffer() -{ - if (!isVisible()) { - return; - } - - if (buffer_) { - makeCurrent(); - - if (!texture_.IsCreated() - || texture_.width() != buffer_->width() - || texture_.height() != buffer_->height() - || texture_.format() != buffer_->format()) { - texture_.Destroy(); - managed_tex_.Destroy(); - - texture_.Create(context(), buffer_); - managed_tex_.Create(context(), buffer_->video_params()); - } else { - texture_.Upload(buffer_); - } - - doneCurrent(); - } - - update(); -} - -void WaveformScope::CleanUp() -{ - makeCurrent(); - - pipeline_ = nullptr; - texture_.Destroy(); - managed_tex_.Destroy(); - framebuffer_.Destroy(); - - doneCurrent(); -} - OLIVE_NAMESPACE_EXIT diff --git a/app/widget/scope/waveform/waveform.h b/app/widget/scope/waveform/waveform.h index 4243a9d3a..04a464e52 100644 --- a/app/widget/scope/waveform/waveform.h +++ b/app/widget/scope/waveform/waveform.h @@ -21,48 +21,20 @@ #ifndef WAVEFORMSCOPE_H #define WAVEFORMSCOPE_H -#include "codec/frame.h" -#include "render/backend/opengl/openglcolorprocessor.h" -#include "render/backend/opengl/openglframebuffer.h" -#include "render/backend/opengl/openglshader.h" -#include "render/backend/opengl/opengltexture.h" -#include "widget/manageddisplay/manageddisplay.h" +#include "widget/scope/scopebase/scopebase.h" OLIVE_NAMESPACE_ENTER -class WaveformScope : public ManagedDisplayWidget +class WaveformScope : public ScopeBase { Q_OBJECT public: WaveformScope(QWidget* parent = nullptr); - virtual ~WaveformScope() override; - -public slots: - void SetBuffer(Frame* frame); - protected: - virtual void initializeGL() override; + virtual OpenGLShaderPtr CreateShader() override; - virtual void paintGL() override; - - virtual void showEvent(QShowEvent* e) override; - -private: - void UploadTextureFromBuffer(); - - OpenGLShaderPtr pipeline_; - - OpenGLTexture texture_; - - OpenGLTexture managed_tex_; - - OpenGLFramebuffer framebuffer_; - - Frame* buffer_; - -private slots: - void CleanUp(); + virtual void DrawScope() override; }; diff --git a/app/widget/slider/sliderlabel.cpp b/app/widget/slider/sliderlabel.cpp index a54a54cf0..aee4a8409 100644 --- a/app/widget/slider/sliderlabel.cpp +++ b/app/widget/slider/sliderlabel.cpp @@ -22,6 +22,11 @@ #include #include +#include + +#ifdef Q_OS_MAC +#include +#endif OLIVE_NAMESPACE_ENTER @@ -48,40 +53,51 @@ SliderLabel::SliderLabel(QWidget *parent) : setFocusPolicy(Qt::TabFocus); } -void SliderLabel::mousePressEvent(QMouseEvent *ev) +void SliderLabel::mousePressEvent(QMouseEvent *) { - QLabel::mousePressEvent(ev); + emit drag_start(); +#if defined(Q_OS_MAC) + CGAssociateMouseAndMouseCursorPosition(false); + CGDisplayHideCursor(kCGDirectMainDisplay); + CGGetLastMouseDelta(nullptr, nullptr); +#else drag_start_ = QCursor::pos(); static_cast(QApplication::instance())->setOverrideCursor(Qt::BlankCursor); - - emit drag_start(); +#endif } -void SliderLabel::mouseMoveEvent(QMouseEvent *ev) +void SliderLabel::mouseMoveEvent(QMouseEvent *) { - QLabel::mouseMoveEvent(ev); - - QPoint current_pos = QCursor::pos(); - - int x_mvmt = current_pos.x() - drag_start_.x(); - int y_mvmt = drag_start_.y() - current_pos.y(); - - emit dragged(x_mvmt + y_mvmt); + int32_t x_mvmt, y_mvmt; // Keep cursor in the same position +#if defined(Q_OS_MAC) + CGGetLastMouseDelta(&x_mvmt, &y_mvmt); +#else + QPoint current_pos = QCursor::pos(); + + x_mvmt = current_pos.x() - drag_start_.x(); + y_mvmt = drag_start_.y() - current_pos.y(); + QCursor::setPos(drag_start_); +#endif + + emit dragged(x_mvmt + y_mvmt); } -void SliderLabel::mouseReleaseEvent(QMouseEvent *ev) +void SliderLabel::mouseReleaseEvent(QMouseEvent *) { - QWidget::mouseReleaseEvent(ev); +#if defined(Q_OS_MAC) + CGAssociateMouseAndMouseCursorPosition(true); + CGDisplayShowCursor(kCGDirectMainDisplay); +#else + static_cast(QApplication::instance())->restoreOverrideCursor(); +#endif // Emit a clicked signal emit drag_stop(); - - static_cast(QApplication::instance())->restoreOverrideCursor(); } void SliderLabel::focusInEvent(QFocusEvent *event) diff --git a/app/widget/slider/sliderlabel.h b/app/widget/slider/sliderlabel.h index 92771e669..c83e86bfc 100644 --- a/app/widget/slider/sliderlabel.h +++ b/app/widget/slider/sliderlabel.h @@ -54,6 +54,8 @@ signals: private: QPoint drag_start_; + bool cancel_mm_event_; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/timetarget/CMakeLists.txt b/app/widget/timetarget/CMakeLists.txt new file mode 100644 index 000000000..98f834bd1 --- /dev/null +++ b/app/widget/timetarget/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/timetarget/timetarget.h + widget/timetarget/timetarget.cpp + PARENT_SCOPE +) diff --git a/app/widget/keyframeview/timetargetobject.cpp b/app/widget/timetarget/timetarget.cpp similarity index 98% rename from app/widget/keyframeview/timetargetobject.cpp rename to app/widget/timetarget/timetarget.cpp index 0ea00d0d9..51ec506d3 100644 --- a/app/widget/keyframeview/timetargetobject.cpp +++ b/app/widget/timetarget/timetarget.cpp @@ -18,7 +18,7 @@ ***/ -#include "timetargetobject.h" +#include "timetarget.h" OLIVE_NAMESPACE_ENTER diff --git a/app/widget/keyframeview/timetargetobject.h b/app/widget/timetarget/timetarget.h similarity index 100% rename from app/widget/keyframeview/timetargetobject.h rename to app/widget/timetarget/timetarget.h diff --git a/app/widget/viewer/CMakeLists.txt b/app/widget/viewer/CMakeLists.txt index e96b9e639..70df051ae 100644 --- a/app/widget/viewer/CMakeLists.txt +++ b/app/widget/viewer/CMakeLists.txt @@ -20,8 +20,8 @@ set(OLIVE_SOURCES widget/viewer/audiowaveformview.cpp widget/viewer/footageviewer.h widget/viewer/footageviewer.cpp - widget/viewer/pixelsamplerwidget.h - widget/viewer/pixelsamplerwidget.cpp + widget/viewer/gizmotraverser.h + widget/viewer/gizmotraverser.cpp widget/viewer/viewer.h widget/viewer/viewer.cpp widget/viewer/viewerdisplay.h diff --git a/app/widget/viewer/gizmotraverser.cpp b/app/widget/viewer/gizmotraverser.cpp new file mode 100644 index 000000000..1e77fd925 --- /dev/null +++ b/app/widget/viewer/gizmotraverser.cpp @@ -0,0 +1,39 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "gizmotraverser.h" + +OLIVE_NAMESPACE_ENTER + +void GizmoTraverser::FootageProcessingEvent(StreamPtr stream, const TimeRange &/*input_time*/, NodeValueTable *table) +{ + if (stream->type() == Stream::kVideo || stream->type() == Stream::kAudio) { + + ImageStreamPtr image_stream = std::static_pointer_cast(stream); + + table->Push(NodeParam::kTexture, QSize(image_stream->width(), + image_stream->height())); + + } else if (stream->type() == Stream::kAudio) { + // FIXME: Get samples + } +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/widget/viewer/gizmotraverser.h b/app/widget/viewer/gizmotraverser.h new file mode 100644 index 000000000..97b2cfc43 --- /dev/null +++ b/app/widget/viewer/gizmotraverser.h @@ -0,0 +1,40 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef GIZMOTRAVERSER_H +#define GIZMOTRAVERSER_H + +#include "node/traverser.h" + +OLIVE_NAMESPACE_ENTER + +class GizmoTraverser : public NodeTraverser +{ +public: + GizmoTraverser() = default; + +protected: + virtual void FootageProcessingEvent(StreamPtr stream, const TimeRange &input_time, NodeValueTable* table) override; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // GIZMOTRAVERSER_H diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index d4ebb84f0..292d07cc0 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -105,6 +105,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) : connect(video_renderer_, &VideoRenderBackend::CachedTimeReady, this, &ViewerWidget::RendererCachedTime); connect(video_renderer_, &VideoRenderBackend::CachedTimeReady, ruler(), &TimeRuler::CacheTimeReady); connect(video_renderer_, &VideoRenderBackend::RangeInvalidated, ruler(), &TimeRuler::CacheInvalidatedRange); + connect(video_renderer_, &VideoRenderBackend::GeneratedFrame, this, &ViewerWidget::RendererGeneratedFrame); audio_renderer_ = new AudioBackend(this); waveform_view_->SetBackend(audio_renderer_); @@ -130,6 +131,8 @@ void ViewerWidget::TimeChangedEvent(const int64_t &i) UpdateTextureFromNode(time_set); PushScrubbedAudio(); + + main_gl_widget()->SetTime(time_set); } last_time_ = i; @@ -333,6 +336,12 @@ ColorManager *ViewerWidget::color_manager() const return main_gl_widget()->color_manager(); } +void ViewerWidget::SetGizmos(Node *node) +{ + main_gl_widget()->SetTimeTarget(GetConnectedNode()); + main_gl_widget()->SetGizmos(node); +} + void ViewerWidget::UpdateTextureFromNode(const rational& time) { if (!GetConnectedNode() || time >= GetConnectedNode()->Length()) { @@ -514,6 +523,13 @@ void ViewerWidget::ContextMenuScopeTriggered(QAction *action) emit RequestScopePanel(static_cast(action->data().toInt())); } +void ViewerWidget::RendererGeneratedFrame(FramePtr f) +{ + foreach (ViewerDisplayWidget* glw, gl_widgets_) { + glw->SetImageFromLoadBuffer(f.get()); + } +} + void ViewerWidget::UpdateRendererParameters() { if (!GetConnectedNode()) { @@ -532,6 +548,8 @@ void ViewerWidget::UpdateRendererParameters() video_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()), nullptr); } + main_gl_widget()->SetVideoParams(vparam); + AudioRenderingParams aparam(GetConnectedNode()->audio_params(), SampleFormat::kInternalFormat); @@ -868,7 +886,7 @@ void ViewerWidget::SetZoomFromMenu(QAction *action) void ViewerWidget::InvalidateVisible(NodeInput* source) { - video_renderer_->InvalidateCache(TimeRange(GetTime(), GetTime()), source); + video_renderer_->InvalidateVisible(TimeRange(GetTime(), GetTime()), source); } OLIVE_NAMESPACE_EXIT diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 57b6e2970..afeeaadd0 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -87,6 +87,8 @@ public: ColorManager* color_manager() const; + void SetGizmos(Node* node); + public slots: void Play(bool in_to_out_only); @@ -234,6 +236,8 @@ private slots: void ContextMenuScopeTriggered(QAction* action); + void RendererGeneratedFrame(FramePtr f); + }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 469fcd7aa..22af4c754 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -30,6 +30,7 @@ #include #include "common/define.h" +#include "gizmotraverser.h" #include "render/backend/opengl/openglrenderfunctions.h" #include "render/backend/opengl/openglshader.h" #include "render/pixelformat.h" @@ -43,7 +44,9 @@ bool ViewerDisplayWidget::nouveau_check_done_ = false; ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) : ManagedDisplayWidget(parent), has_image_(false), - signal_cursor_color_(false) + signal_cursor_color_(false), + gizmos_(nullptr), + gizmo_click_(false) { } @@ -161,8 +164,40 @@ void ViewerDisplayWidget::SetSafeMargins(const ViewerSafeMarginInfo &safe_margin update(); } +void ViewerDisplayWidget::SetGizmos(Node *node) +{ + gizmos_ = node; + + update(); +} + +void ViewerDisplayWidget::SetVideoParams(const VideoRenderingParams ¶ms) +{ + gizmo_params_ = params; + + if (gizmos_) { + update(); + } +} + +void ViewerDisplayWidget::SetTime(const rational &time) +{ + time_ = time; + + if (gizmos_) { + update(); + } +} + void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event) { + if (gizmos_) { + if (gizmos_->GizmoPress(GetTexturePosition(event->pos()))) { + gizmo_click_ = true; + return; + } + } + QOpenGLWidget::mousePressEvent(event); emit DragStarted(); @@ -170,6 +205,11 @@ void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event) void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event) { + if (gizmo_click_) { + gizmos_->GizmoMove(GetTexturePosition(event->pos())); + return; + } + QOpenGLWidget::mouseMoveEvent(event); if (signal_cursor_color_) { @@ -193,6 +233,18 @@ void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event) } } +void ViewerDisplayWidget::mouseReleaseEvent(QMouseEvent *event) +{ + if (gizmo_click_) { + gizmos_->GizmoRelease(GetTexturePosition(event->pos())); + gizmo_click_ = false; + + return; + } + + QOpenGLWidget::mouseReleaseEvent(event); +} + void ViewerDisplayWidget::initializeGL() { ManagedDisplayWidget::initializeGL(); @@ -225,7 +277,7 @@ void ViewerDisplayWidget::paintGL() f->glClear(GL_COLOR_BUFFER_BIT); // We only draw if we have a pipeline - if (has_image_ && color_service() && texture_.IsCreated()) { + if (has_image_ && color_service()) { // Bind retrieved texture f->glBindTexture(GL_TEXTURE_2D, texture_.texture()); @@ -238,6 +290,18 @@ void ViewerDisplayWidget::paintGL() } + // Draw gizmos if we have any + if (gizmos_) { + GizmoTraverser gt; + + rational node_time = GetAdjustedTime(GetTimeTarget(), gizmos_, time_, NodeParam::kInput); + + NodeValueDatabase db = gt.GenerateDatabase(gizmos_, TimeRange(node_time, node_time)); + + QPainter p(this); + gizmos_->DrawGizmos(db, &p, QVector2D(GetTexturePosition(size()))); + } + // Draw action/title safe areas if (safe_margin_.is_enabled()) { QPainter p(this); @@ -271,6 +335,22 @@ void ViewerDisplayWidget::paintGL() } } +QPointF ViewerDisplayWidget::GetTexturePosition(const QPoint &screen_pos) +{ + return GetTexturePosition(screen_pos.x(), screen_pos.y()); +} + +QPointF ViewerDisplayWidget::GetTexturePosition(const QSize &size) +{ + return GetTexturePosition(size.width(), size.height()); +} + +QPointF ViewerDisplayWidget::GetTexturePosition(const double &x, const double &y) +{ + return QPointF(x / gizmo_params_.width(), + y / gizmo_params_.height()); +} + #ifdef Q_OS_LINUX void ViewerDisplayWidget::ShowNouveauWarning() { diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 43e42eed2..a9903075e 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -23,6 +23,7 @@ #include +#include "node/node.h" #include "render/backend/opengl/openglcolorprocessor.h" #include "render/backend/opengl/openglframebuffer.h" #include "render/backend/opengl/openglshader.h" @@ -31,6 +32,7 @@ #include "render/colormanager.h" #include "viewersafemargininfo.h" #include "widget/manageddisplay/manageddisplay.h" +#include "widget/timetarget/timetarget.h" OLIVE_NAMESPACE_ENTER @@ -49,7 +51,7 @@ OLIVE_NAMESPACE_ENTER * the same texture object, use SetTexture() since it will nearly always be faster to just set it than to check *and* * set it. */ -class ViewerDisplayWidget : public ManagedDisplayWidget +class ViewerDisplayWidget : public ManagedDisplayWidget, public TimeTargetObject { Q_OBJECT public: @@ -76,6 +78,10 @@ public: const ViewerSafeMarginInfo& GetSafeMargin() const; void SetSafeMargins(const ViewerSafeMarginInfo& safe_margin); + void SetGizmos(Node* node); + void SetVideoParams(const VideoRenderingParams& params); + void SetTime(const rational& time); + public slots: /** * @brief Set the transformation matrix to draw with @@ -123,15 +129,20 @@ signals: protected: /** - * @brief Override the mouse press event simply to emit the DragStarted() signal + * @brief Override the mouse press event for the DragStarted() signal and gizmos */ virtual void mousePressEvent(QMouseEvent* event) override; /** - * @brief Override mouse move to provide functionality for + * @brief Override mouse move to signal for the pixel sampler and gizmos */ virtual void mouseMoveEvent(QMouseEvent* event) override; + /** + * @brief Override mouse release event for gizmos + */ + virtual void mouseReleaseEvent(QMouseEvent* event) override; + /** * @brief Initialize function to set up the OpenGL context upon its construction * @@ -147,6 +158,10 @@ protected: virtual void paintGL() override; private: + QPointF GetTexturePosition(const QPoint& screen_pos); + QPointF GetTexturePosition(const QSize& size); + QPointF GetTexturePosition(const double& x, const double& y); + /** * @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL(). */ @@ -172,6 +187,14 @@ private: ViewerSafeMarginInfo safe_margin_; + Node* gizmos_; + + VideoRenderingParams gizmo_params_; + + bool gizmo_click_; + + rational time_; + private slots: /** * @brief Slot to connect just before the OpenGL context is destroyed to clean up resources diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index bd375f994..008a90ccf 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -85,6 +85,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(param_panel_, &ParamPanel::RequestSelectNode, node_panel_, &NodePanel::Select); connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp); connect(param_panel_, &ParamPanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp); + connect(param_panel_, &ParamPanel::FoundGizmos, sequence_viewer_panel_, &SequenceViewerPanel::SetGizmos); connect(PanelManager::instance(), &PanelManager::FocusedPanelChanged, this, &MainWindow::FocusedPanelChanged); sequence_viewer_panel_->ConnectTimeBasedPanel(param_panel_);