build: split the engine into liboakengine.so; worker drops the UI entirely
Physical split: app/{audio,cli,codec,common,config,node,pluginSupport,
render,task,timeline,undo,tool,shaders} plus coreengine, version and
ui/icons+colorcoding move to a new top-level engine/ tree, built as
liboakengine.so (shared). The render backends (oakgl/oakvulkan) move
with it and link the engine library instead of embedding a static
render-core subset (libolive-rendercore is gone).
- oak-render-worker now links liboakengine instead of the whole
libolive-editor object set: 336MB -> 2.9MB, no Qt Widgets UI
- the editor links liboakengine for the engine and keeps only UI
objects in libolive-editor
- install/packaging: GNUInstallDirs libdir on Linux, bundle copy on
macOS, oakengine.dll staged for NSIS, AppImage validation entry
- fix backend lookup for the new layout: DynamicRenderer searched
../app but backends now live in engine/; a stale pre-split liboakgl
in the build tree got dlopened instead, re-initialized and later
destroyed the interposed engine statics (full-suite segfault at
DialogSequenceParameterTab, found via gdb watchpoint)
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
# Modifications Copyright (C) 2025 mikesolar
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(audio)
|
||||
add_subdirectory(block)
|
||||
add_subdirectory(color)
|
||||
add_subdirectory(distort)
|
||||
add_subdirectory(effect)
|
||||
add_subdirectory(filter)
|
||||
add_subdirectory(generator)
|
||||
add_subdirectory(gizmo)
|
||||
add_subdirectory(group)
|
||||
add_subdirectory(input)
|
||||
add_subdirectory(keying)
|
||||
add_subdirectory(math)
|
||||
add_subdirectory(output)
|
||||
add_subdirectory(plugins)
|
||||
add_subdirectory(project)
|
||||
add_subdirectory(time)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/factory.cpp
|
||||
node/factory.h
|
||||
node/globals.cpp
|
||||
node/globals.h
|
||||
node/inputdragger.cpp
|
||||
node/inputdragger.h
|
||||
node/inputimmediate.cpp
|
||||
node/inputimmediate.h
|
||||
node/keyframe.cpp
|
||||
node/keyframe.h
|
||||
node/node.cpp
|
||||
node/node.h
|
||||
node/nodeundo.cpp
|
||||
node/nodeundo.h
|
||||
node/param.cpp
|
||||
node/param.h
|
||||
node/project.cpp
|
||||
node/project.h
|
||||
node/splitvalue.h
|
||||
node/traverser.cpp
|
||||
node/traverser.h
|
||||
node/value.cpp
|
||||
node/value.h
|
||||
node/valuedatabase.cpp
|
||||
node/valuedatabase.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(pan)
|
||||
add_subdirectory(volume)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/audio/pan/pan.h
|
||||
node/audio/pan/pan.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,130 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "pan.h"
|
||||
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString PanNode::k_samples_input = QStringLiteral("samples_in");
|
||||
const QString PanNode::k_panning_input = QStringLiteral("panning_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
PanNode::PanNode()
|
||||
{
|
||||
add_input(k_samples_input, NodeValue::k_samples,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_panning_input, NodeValue::k_float, 0.0);
|
||||
set_input_property(k_panning_input, QStringLiteral("min"), -1.0);
|
||||
set_input_property(k_panning_input, QStringLiteral("max"), 1.0);
|
||||
set_input_property(k_panning_input, QStringLiteral("view"),
|
||||
slider::k_percentage);
|
||||
|
||||
set_flag(k_audio_effect);
|
||||
set_effect_input(k_samples_input);
|
||||
}
|
||||
|
||||
QString PanNode::name() const
|
||||
{
|
||||
return tr("Pan");
|
||||
}
|
||||
|
||||
QString PanNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.pan");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> PanNode::category() const
|
||||
{
|
||||
return { k_category_filter };
|
||||
}
|
||||
|
||||
QString PanNode::description() const
|
||||
{
|
||||
return tr("Adjust the stereo panning of an audio source.");
|
||||
}
|
||||
|
||||
void PanNode::value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// Create a sample job
|
||||
SampleBuffer samples = value[k_samples_input].to_samples();
|
||||
if (samples.is_allocated()) {
|
||||
// This node is only compatible with stereo audio
|
||||
if (samples.audio_params().channel_count() == 2) {
|
||||
// If the input is static, we can just do it now which will be faster
|
||||
if (is_input_static(k_panning_input)) {
|
||||
float pan_volume = value[k_panning_input].to_double();
|
||||
if (!qIsNull(pan_volume)) {
|
||||
if (pan_volume > 0) {
|
||||
samples.transform_volume_for_channel(0,
|
||||
1.0f - pan_volume);
|
||||
} else {
|
||||
samples.transform_volume_for_channel(1,
|
||||
1.0f + pan_volume);
|
||||
}
|
||||
}
|
||||
|
||||
table->push(NodeValue(NodeValue::k_samples, samples, this));
|
||||
} else {
|
||||
// Requires job
|
||||
SampleJob job(globals.time(), k_samples_input, value);
|
||||
job.insert(k_panning_input, value);
|
||||
table->push(NodeValue::k_samples, QVariant::fromValue(job),
|
||||
this);
|
||||
}
|
||||
} else {
|
||||
// Pass right through
|
||||
table->push(value[k_samples_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PanNode::process_samples(const NodeValueRow &values,
|
||||
const SampleBuffer &input, SampleBuffer &output,
|
||||
int index) const
|
||||
{
|
||||
float pan_val = values[k_panning_input].to_double();
|
||||
|
||||
for (int i = 0; i < input.audio_params().channel_count(); i++) {
|
||||
output.data(i)[index] = input.data(i)[index];
|
||||
}
|
||||
|
||||
if (pan_val > 0) {
|
||||
output.data(0)[index] *= (1.0F - pan_val);
|
||||
} else if (pan_val < 0) {
|
||||
output.data(1)[index] *= (1.0F - qAbs(pan_val));
|
||||
}
|
||||
}
|
||||
|
||||
void PanNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_samples_input, tr("Samples"));
|
||||
set_input_name(k_panning_input, tr("Pan"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_PANNODE_H
|
||||
#define OAK_PANNODE_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class PanNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
PanNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(PanNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
virtual void process_samples(const NodeValueRow &values,
|
||||
const SampleBuffer &input, SampleBuffer &output,
|
||||
int index) const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
static const QString k_samples_input;
|
||||
static const QString k_panning_input;
|
||||
|
||||
private:
|
||||
NodeInput *samples_input_;
|
||||
NodeInput *panning_input_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_PANNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/audio/volume/volume.h
|
||||
node/audio/volume/volume.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,109 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "volume.h"
|
||||
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString VolumeNode::k_samples_input = QStringLiteral("samples_in");
|
||||
const QString VolumeNode::k_volume_input = QStringLiteral("volume_in");
|
||||
|
||||
#define super MathNodeBase
|
||||
|
||||
VolumeNode::VolumeNode()
|
||||
{
|
||||
add_input(k_samples_input, NodeValue::k_samples,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_volume_input, NodeValue::k_float, 1.0);
|
||||
set_input_property(k_volume_input, QStringLiteral("min"), 0.0);
|
||||
set_input_property(k_volume_input, QStringLiteral("view"),
|
||||
slider::k_decibel);
|
||||
|
||||
set_flag(k_audio_effect);
|
||||
set_effect_input(k_samples_input);
|
||||
}
|
||||
|
||||
QString VolumeNode::name() const
|
||||
{
|
||||
return tr("Volume");
|
||||
}
|
||||
|
||||
QString VolumeNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.volume");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> VolumeNode::category() const
|
||||
{
|
||||
return { k_category_filter };
|
||||
}
|
||||
|
||||
QString VolumeNode::description() const
|
||||
{
|
||||
return tr("Adjusts the volume of an audio source.");
|
||||
}
|
||||
|
||||
void VolumeNode::value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// Create a sample job
|
||||
SampleBuffer buffer = value[k_samples_input].to_samples();
|
||||
|
||||
if (buffer.is_allocated()) {
|
||||
// If the input is static, we can just do it now which will be faster
|
||||
if (is_input_static(k_volume_input)) {
|
||||
auto volume = value[k_volume_input].to_double();
|
||||
|
||||
if (!qFuzzyCompare(volume, 1.0)) {
|
||||
buffer.transform_volume(volume);
|
||||
}
|
||||
|
||||
table->push(NodeValue::k_samples, QVariant::fromValue(buffer), this);
|
||||
} else {
|
||||
// Requires job
|
||||
SampleJob job(globals.time(), k_samples_input, value);
|
||||
job.insert(k_volume_input, value);
|
||||
table->push(NodeValue::k_samples, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VolumeNode::process_samples(const NodeValueRow &values,
|
||||
const SampleBuffer &input, SampleBuffer &output,
|
||||
int index) const
|
||||
{
|
||||
return process_samples_internal(values, k_op_multiply, k_samples_input,
|
||||
k_volume_input, input, output, index);
|
||||
}
|
||||
|
||||
void VolumeNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_samples_input, tr("Samples"));
|
||||
set_input_name(k_volume_input, tr("Volume"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_VOLUMENODE_H
|
||||
#define OAK_VOLUMENODE_H
|
||||
|
||||
#include "node/math/math/mathbase.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class VolumeNode : public MathNodeBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
VolumeNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(VolumeNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
virtual void process_samples(const NodeValueRow &values,
|
||||
const SampleBuffer &input, SampleBuffer &output,
|
||||
int index) const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
static const QString k_samples_input;
|
||||
static const QString k_volume_input;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_VOLUMENODE_H
|
||||
@@ -0,0 +1,27 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(clip)
|
||||
add_subdirectory(gap)
|
||||
add_subdirectory(subtitle)
|
||||
add_subdirectory(transition)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/block/block.h
|
||||
node/block/block.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,152 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "block.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "node/inputdragger.h"
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super Node
|
||||
|
||||
const QString Block::k_length_input = QStringLiteral("length_in");
|
||||
|
||||
Block::Block()
|
||||
: previous_(nullptr)
|
||||
, next_(nullptr)
|
||||
, track_(nullptr)
|
||||
{
|
||||
add_input(k_length_input, NodeValue::k_rational,
|
||||
InputFlags(k_input_flag_not_connectable | k_input_flag_not_keyframable |
|
||||
k_input_flag_hidden));
|
||||
set_input_property(k_length_input, QStringLiteral("min"),
|
||||
QVariant::fromValue(Rational(0, 1)));
|
||||
set_input_property(k_length_input, QStringLiteral("view"),
|
||||
slider::k_time);
|
||||
set_input_property(k_length_input, QStringLiteral("viewlock"), true);
|
||||
|
||||
set_input_flag(k_enabled_input, k_input_flag_not_connectable);
|
||||
set_input_flag(k_enabled_input, k_input_flag_not_keyframable);
|
||||
|
||||
set_flag(k_dont_show_in_param_view);
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> Block::category() const
|
||||
{
|
||||
return { k_category_timeline };
|
||||
}
|
||||
|
||||
Rational Block::length() const
|
||||
{
|
||||
return get_standard_value(k_length_input).value<Rational>();
|
||||
}
|
||||
|
||||
void Block::set_length_and_media_out(const Rational &length)
|
||||
{
|
||||
if (length == this->length()) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_length_internal(length);
|
||||
}
|
||||
|
||||
void Block::set_length_and_media_in(const Rational &length)
|
||||
{
|
||||
if (length == this->length()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the length without setting media out
|
||||
set_length_internal(length);
|
||||
}
|
||||
|
||||
bool Block::is_enabled() const
|
||||
{
|
||||
return get_standard_value(k_enabled_input).toBool();
|
||||
}
|
||||
|
||||
void Block::set_enabled(bool e)
|
||||
{
|
||||
set_standard_value(k_enabled_input, e);
|
||||
|
||||
emit enabled_changed();
|
||||
}
|
||||
|
||||
void Block::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
super::InputValueChangedEvent(input, element);
|
||||
|
||||
if (input == k_length_input) {
|
||||
emit length_changed();
|
||||
} else if (input == k_enabled_input) {
|
||||
emit enabled_changed();
|
||||
}
|
||||
}
|
||||
|
||||
void Block::set_length_internal(const Rational &length)
|
||||
{
|
||||
set_standard_value(k_length_input, QVariant::fromValue(length));
|
||||
}
|
||||
|
||||
void Block::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_length_input, tr("Length"));
|
||||
set_input_name(k_enabled_input, tr("Enabled"));
|
||||
}
|
||||
|
||||
void Block::invalidate_cache(const TimeRange &range, const QString &from,
|
||||
int element, InvalidateCacheOptions options)
|
||||
{
|
||||
TimeRange r;
|
||||
|
||||
if (from == k_length_input) {
|
||||
// We must intercept the signal here
|
||||
r = TimeRange(qMin(length(), last_length_), RATIONAL_MAX);
|
||||
|
||||
if (!NodeInputDragger::is_input_being_dragged()) {
|
||||
last_length_ = length();
|
||||
}
|
||||
|
||||
options.insert(QStringLiteral("lengthevent"), true);
|
||||
} else {
|
||||
r = range;
|
||||
}
|
||||
|
||||
super::invalidate_cache(r, from, element, options);
|
||||
}
|
||||
|
||||
void Block::set_previous_next(Block *previous, Block *next)
|
||||
{
|
||||
if (previous) {
|
||||
previous->set_next(next);
|
||||
}
|
||||
if (next) {
|
||||
next->set_previous(previous);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_BLOCK_H
|
||||
#define OAK_BLOCK_H
|
||||
|
||||
#include "node/node.h"
|
||||
#include "timeline/timelinecommon.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class TransitionBlock;
|
||||
|
||||
/**
|
||||
* @brief A Node that represents a block of time, also displayable on a Timeline
|
||||
*/
|
||||
class Block : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Block();
|
||||
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
|
||||
const Rational &in() const
|
||||
{
|
||||
return in_point_;
|
||||
}
|
||||
|
||||
const Rational &out() const
|
||||
{
|
||||
return out_point_;
|
||||
}
|
||||
|
||||
void set_in(const Rational &in)
|
||||
{
|
||||
in_point_ = in;
|
||||
}
|
||||
|
||||
void set_out(const Rational &out)
|
||||
{
|
||||
out_point_ = out;
|
||||
}
|
||||
|
||||
Rational length() const;
|
||||
virtual void set_length_and_media_out(const Rational &length);
|
||||
virtual void set_length_and_media_in(const Rational &length);
|
||||
|
||||
TimeRange range() const
|
||||
{
|
||||
return TimeRange(in(), out());
|
||||
}
|
||||
|
||||
Block *previous() const
|
||||
{
|
||||
return previous_;
|
||||
}
|
||||
|
||||
Block *next() const
|
||||
{
|
||||
return next_;
|
||||
}
|
||||
|
||||
void set_previous(Block *previous)
|
||||
{
|
||||
previous_ = previous;
|
||||
}
|
||||
|
||||
void set_next(Block *next)
|
||||
{
|
||||
next_ = next;
|
||||
}
|
||||
|
||||
Track *track() const
|
||||
{
|
||||
return track_;
|
||||
}
|
||||
|
||||
void set_track(Track *track)
|
||||
{
|
||||
track_ = track;
|
||||
emit track_changed(track_);
|
||||
}
|
||||
|
||||
bool is_enabled() const;
|
||||
void set_enabled(bool e);
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual void invalidate_cache(
|
||||
const TimeRange &range, const QString &from, int element = -1,
|
||||
InvalidateCacheOptions options = InvalidateCacheOptions()) override;
|
||||
|
||||
static const QString k_length_input;
|
||||
|
||||
static void set_previous_next(Block *previous, Block *next);
|
||||
|
||||
public slots:
|
||||
|
||||
signals:
|
||||
void enabled_changed();
|
||||
|
||||
void length_changed();
|
||||
|
||||
void preview_changed();
|
||||
|
||||
void track_changed(Track *track);
|
||||
|
||||
protected:
|
||||
virtual void InputValueChangedEvent(const QString &input,
|
||||
int element) override;
|
||||
|
||||
Block *previous_;
|
||||
Block *next_;
|
||||
|
||||
private:
|
||||
void set_length_internal(const Rational &length);
|
||||
|
||||
Rational in_point_;
|
||||
Rational out_point_;
|
||||
Track *track_;
|
||||
|
||||
Rational last_length_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_BLOCK_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/block/clip/clip.h
|
||||
node/block/clip/clip.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,662 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "clip.h"
|
||||
|
||||
#include "config/config.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "node/sliderdisplaytype.h"
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super Block
|
||||
|
||||
const QString ClipBlock::k_buffer_in = QStringLiteral("buffer_in");
|
||||
const QString ClipBlock::k_media_in_input = QStringLiteral("media_in_in");
|
||||
const QString ClipBlock::k_speed_input = QStringLiteral("speed_in");
|
||||
const QString ClipBlock::k_reverse_input = QStringLiteral("reverse_in");
|
||||
const QString ClipBlock::k_maintain_audio_pitch_input =
|
||||
QStringLiteral("maintain_audio_pitch_in");
|
||||
const QString ClipBlock::k_auto_cache_input = QStringLiteral("autocache_in");
|
||||
const QString ClipBlock::k_loop_mode_input = QStringLiteral("loop_in");
|
||||
|
||||
ClipBlock::ClipBlock()
|
||||
: in_transition_(nullptr)
|
||||
, out_transition_(nullptr)
|
||||
, connected_viewer_(nullptr)
|
||||
{
|
||||
add_input(k_media_in_input, NodeValue::k_rational,
|
||||
InputFlags(k_input_flag_not_connectable | k_input_flag_not_keyframable));
|
||||
set_input_property(k_media_in_input, QStringLiteral("view"),
|
||||
slider::k_time);
|
||||
set_input_property(k_media_in_input, QStringLiteral("viewlock"), true);
|
||||
|
||||
add_input(k_speed_input, NodeValue::k_float, 1.0,
|
||||
InputFlags(k_input_flag_not_connectable | k_input_flag_not_keyframable));
|
||||
set_input_property(k_speed_input, QStringLiteral("view"),
|
||||
slider::k_percentage);
|
||||
set_input_property(k_speed_input, QStringLiteral("min"), 0.0);
|
||||
|
||||
add_input(k_reverse_input, NodeValue::k_boolean, false,
|
||||
InputFlags(k_input_flag_not_connectable | k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_maintain_audio_pitch_input, NodeValue::k_boolean, false,
|
||||
InputFlags(k_input_flag_not_connectable | k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_auto_cache_input, NodeValue::k_boolean, false,
|
||||
InputFlags(k_input_flag_not_connectable | k_input_flag_not_keyframable));
|
||||
|
||||
prepend_input(k_buffer_in, NodeValue::k_none,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
//SetValueHintForInput(kBufferIn, ValueHint(NodeValue::kBuffer));
|
||||
|
||||
set_effect_input(k_buffer_in);
|
||||
|
||||
add_input(k_loop_mode_input, NodeValue::k_combo, 0,
|
||||
InputFlags(k_input_flag_not_connectable | k_input_flag_not_keyframable));
|
||||
}
|
||||
|
||||
QString ClipBlock::name() const
|
||||
{
|
||||
if (connected_viewer_ && !connected_viewer_->get_label().isEmpty()) {
|
||||
return connected_viewer_->get_label();
|
||||
} else if (track()) {
|
||||
if (track()->type() == Track::k_video) {
|
||||
return tr("Video Clip");
|
||||
} else if (track()->type() == Track::k_audio) {
|
||||
return tr("Audio Clip");
|
||||
}
|
||||
}
|
||||
|
||||
return tr("Clip");
|
||||
}
|
||||
|
||||
QString ClipBlock::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.clip");
|
||||
}
|
||||
|
||||
QString ClipBlock::description() const
|
||||
{
|
||||
return tr("A time-based node that represents a media source.");
|
||||
}
|
||||
|
||||
void ClipBlock::set_length_and_media_out(const Rational &length)
|
||||
{
|
||||
if (length == this->length()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reverse()) {
|
||||
// Calculate media_in adjustment
|
||||
Rational proposed_media_in = sequence_to_media_time(
|
||||
this->length() - length, k_stm_ignore_reverse | k_stm_ignore_loop);
|
||||
set_media_in(proposed_media_in);
|
||||
}
|
||||
|
||||
super::set_length_and_media_out(length);
|
||||
}
|
||||
|
||||
void ClipBlock::set_length_and_media_in(const Rational &length)
|
||||
{
|
||||
if (length == this->length()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rational old_length = this->length();
|
||||
|
||||
super::set_length_and_media_in(length);
|
||||
|
||||
if (!reverse()) {
|
||||
// Calculate media_in adjustment
|
||||
set_media_in(sequence_to_media_time(old_length - length, k_stm_ignore_loop));
|
||||
}
|
||||
}
|
||||
|
||||
Rational ClipBlock::media_in() const
|
||||
{
|
||||
return get_standard_value(k_media_in_input).value<Rational>();
|
||||
}
|
||||
|
||||
Node::ValueHint ClipBlock::get_value_hint_for_input(const QString &input,
|
||||
int element) const
|
||||
{
|
||||
if (input == k_buffer_in) {
|
||||
// The buffer input takes whatever the connected node provides, so it
|
||||
// is declared as kNone and carries no stored hint. When the connected
|
||||
// node pushes more than one value type (a footage pushes both a
|
||||
// kTexture job and a kSamples job), a typeless lookup falls back to
|
||||
// the last value in the table, which may feed audio samples into a
|
||||
// video clip and produce a black frame. Prefer the value type that
|
||||
// matches this clip's track.
|
||||
switch (get_track_type()) {
|
||||
case Track::k_video:
|
||||
return ValueHint(QVector<NodeValue::Type>{ NodeValue::k_texture });
|
||||
case Track::k_audio:
|
||||
return ValueHint(QVector<NodeValue::Type>{ NodeValue::k_samples });
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return super::get_value_hint_for_input(input, element);
|
||||
}
|
||||
|
||||
void ClipBlock::set_media_in(const Rational &media_in)
|
||||
{
|
||||
set_standard_value(k_media_in_input, QVariant::fromValue(media_in));
|
||||
|
||||
request_invalidated_from_connected();
|
||||
}
|
||||
|
||||
void ClipBlock::set_autocache(bool e)
|
||||
{
|
||||
set_standard_value(k_auto_cache_input, e);
|
||||
}
|
||||
|
||||
void ClipBlock::discard_cache()
|
||||
{
|
||||
if (Node *connected = get_connected_output(k_buffer_in)) {
|
||||
Track::Type type = get_track_type();
|
||||
if (type == Track::k_video) {
|
||||
connected->video_frame_cache()->invalidate(
|
||||
TimeRange(RATIONAL_MIN, RATIONAL_MAX));
|
||||
} else if (type == Track::k_audio) {
|
||||
connected->audio_playback_cache()->invalidate(
|
||||
TimeRange(RATIONAL_MIN, RATIONAL_MAX));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rational ClipBlock::sequence_to_media_time(const Rational &sequence_time,
|
||||
uint64_t flags) const
|
||||
{
|
||||
// These constants are not considered "values" per se, so we don't modify them
|
||||
if (sequence_time == RATIONAL_MIN || sequence_time == RATIONAL_MAX) {
|
||||
return sequence_time;
|
||||
}
|
||||
|
||||
Rational media_time = sequence_time;
|
||||
|
||||
if (reverse() && !(flags & k_stm_ignore_reverse)) {
|
||||
media_time = length() - media_time;
|
||||
}
|
||||
|
||||
if (!(flags & k_stm_ignore_speed)) {
|
||||
double speed_value = speed();
|
||||
if (qIsNull(speed_value)) {
|
||||
// Effectively holds the frame at the in point
|
||||
media_time = 0;
|
||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||
// Multiply time
|
||||
media_time =
|
||||
Rational::from_double(media_time.to_double() * speed_value);
|
||||
}
|
||||
}
|
||||
|
||||
media_time += media_in();
|
||||
|
||||
/*if (!(flags & kSTMIgnoreLoop)
|
||||
&& this->loop_mode() != kLoopModeOff
|
||||
&& connected_viewer_
|
||||
&& !connected_viewer_->GetLength().isNull()
|
||||
&& (media_time < 0 || media_time >= connected_viewer_->GetLength())) {
|
||||
if (loop_mode() == kLoopModeLoop) {
|
||||
while (media_time < 0) {
|
||||
media_time += connected_viewer_->GetLength();
|
||||
}
|
||||
while (media_time >= connected_viewer_->GetLength()) {
|
||||
media_time -= connected_viewer_->GetLength();
|
||||
}
|
||||
} else if (loop_mode() == kLoopModeClamp) {
|
||||
media_time = std::clamp(media_time, Rational(0), connected_viewer_->GetLength()-connected_viewer_->GetVideoParams().frame_rate_as_time_base());
|
||||
}
|
||||
}*/
|
||||
|
||||
return media_time;
|
||||
}
|
||||
|
||||
Rational ClipBlock::media_to_sequence_time(const Rational &media_time) const
|
||||
{
|
||||
// These constants are not considered "values" per se, so we don't modify them
|
||||
if (media_time == RATIONAL_MIN || media_time == RATIONAL_MAX) {
|
||||
return media_time;
|
||||
}
|
||||
|
||||
Rational sequence_time = media_time - media_in();
|
||||
|
||||
double speed_value = speed();
|
||||
if (qIsNull(speed_value)) {
|
||||
// Speed zero holds the frame at the in point, so map to that frame
|
||||
sequence_time = media_in();
|
||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||
// Divide time
|
||||
sequence_time =
|
||||
Rational::from_double(sequence_time.to_double() / speed_value);
|
||||
}
|
||||
|
||||
if (reverse()) {
|
||||
sequence_time = length() - sequence_time;
|
||||
}
|
||||
|
||||
return sequence_time;
|
||||
}
|
||||
|
||||
void ClipBlock::request_range_from_connected(const TimeRange &range)
|
||||
{
|
||||
Track::Type type = get_track_type();
|
||||
|
||||
if (type == Track::k_video || type == Track::k_audio) {
|
||||
if (Node *connected = get_connected_output(k_buffer_in)) {
|
||||
TimeRange max_range = media_range();
|
||||
if (type == Track::k_video) {
|
||||
// Handle thumbnails
|
||||
request_range_for_cache(connected->thumbnail_cache(), max_range,
|
||||
range, true, false);
|
||||
{
|
||||
TimeRange thumb_range = range.intersected(max_range);
|
||||
if (get_adjusted_thumbnail_range(&thumb_range)) {
|
||||
connected->thumbnail_cache()->request(
|
||||
this->track()->sequence(), thumb_range);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle video cache
|
||||
request_range_for_cache(connected->video_frame_cache(), max_range,
|
||||
range, true, is_autocaching());
|
||||
} else if (type == Track::k_audio) {
|
||||
// Handle waveforms
|
||||
request_range_for_cache(
|
||||
connected->waveform_cache(), max_range, range, true,
|
||||
(OAK_CONFIG("TimelineWaveformMode").toInt() ==
|
||||
Timeline::k_waveforms_enabled));
|
||||
|
||||
// Handle audio cache
|
||||
request_range_for_cache(connected->audio_playback_cache(),
|
||||
max_range, range, true, is_autocaching());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::request_invalidated_from_connected(bool force_all,
|
||||
const TimeRange &intersect)
|
||||
{
|
||||
Track::Type type = get_track_type();
|
||||
|
||||
if (type == Track::k_video || type == Track::k_audio) {
|
||||
if (Node *connected = get_connected_output(k_buffer_in)) {
|
||||
TimeRange max_range = media_range();
|
||||
|
||||
if (!intersect.length().isNull()) {
|
||||
max_range = max_range.intersected(intersect);
|
||||
}
|
||||
|
||||
if (type == Track::k_video) {
|
||||
// Handle thumbnails
|
||||
TimeRange thumb_range = max_range;
|
||||
if (get_adjusted_thumbnail_range(&thumb_range)) {
|
||||
request_invalidated_for_cache(connected->thumbnail_cache(),
|
||||
thumb_range);
|
||||
}
|
||||
|
||||
// Handle video cache
|
||||
if (is_autocaching() || force_all) {
|
||||
request_invalidated_for_cache(connected->video_frame_cache(),
|
||||
max_range);
|
||||
}
|
||||
} else if (type == Track::k_audio) {
|
||||
// Handle waveforms
|
||||
if (OAK_CONFIG("TimelineWaveformMode").toInt() ==
|
||||
Timeline::k_waveforms_enabled) {
|
||||
request_invalidated_for_cache(connected->waveform_cache(),
|
||||
max_range);
|
||||
}
|
||||
|
||||
// Handle audio cache
|
||||
if (is_autocaching() || force_all) {
|
||||
request_invalidated_for_cache(
|
||||
connected->audio_playback_cache(), max_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::request_range_for_cache(PlaybackCache *cache,
|
||||
const TimeRange &max_range,
|
||||
const TimeRange &range, bool invalidate,
|
||||
bool request)
|
||||
{
|
||||
TimeRange r = range.intersected(max_range);
|
||||
|
||||
if (invalidate) {
|
||||
cache->invalidate(r);
|
||||
}
|
||||
|
||||
if (request) {
|
||||
cache->request(this->track()->sequence(), r);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::request_invalidated_for_cache(PlaybackCache *cache,
|
||||
const TimeRange &max_range)
|
||||
{
|
||||
TimeRangeList invalid = cache->get_invalidated_ranges(max_range);
|
||||
|
||||
for (const PlaybackCache::Passthrough &p : cache->get_passthroughs()) {
|
||||
invalid.remove(p);
|
||||
}
|
||||
|
||||
for (const TimeRange &r : invalid) {
|
||||
request_range_for_cache(cache, max_range, r, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClipBlock::get_adjusted_thumbnail_range(TimeRange *r) const
|
||||
{
|
||||
switch (static_cast<Timeline::ThumbnailMode>(
|
||||
OAK_CONFIG("TimelineThumbnailMode").toInt())) {
|
||||
case Timeline::k_thumbnail_off:
|
||||
// Don't cache any range
|
||||
return false;
|
||||
case Timeline::k_thumbnail_in_out: {
|
||||
// Only cache in point
|
||||
Rational in = this->media_range().in();
|
||||
if (r->contains(in)) {
|
||||
// Cache only the in point
|
||||
*r = TimeRange(in, in + thumbnail_cache()->get_timebase());
|
||||
return true;
|
||||
} else {
|
||||
// Cache nothing
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case Timeline::k_thumbnail_on:
|
||||
// Cache entire range
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClipBlock::invalidate_cache(const TimeRange &range, const QString &from,
|
||||
int element, InvalidateCacheOptions options)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
// If signal is from texture input, transform all times from media time to sequence time
|
||||
if (from == k_buffer_in) {
|
||||
// Render caches where necessary
|
||||
if (are_caches_enabled()) {
|
||||
request_range_from_connected(range);
|
||||
}
|
||||
|
||||
// Adjust range from media time to sequence time
|
||||
TimeRange adj;
|
||||
double speed_value = speed();
|
||||
|
||||
if (qIsNull(speed_value)) {
|
||||
// Handle 0 speed by invalidating the whole clip
|
||||
adj = TimeRange(RATIONAL_MIN, RATIONAL_MAX);
|
||||
} else {
|
||||
adj = TimeRange(media_to_sequence_time(range.in()),
|
||||
media_to_sequence_time(range.out()));
|
||||
}
|
||||
|
||||
// Find connected viewer node
|
||||
auto viewers = find_input_nodes_connected_to_input<ViewerOutput>(
|
||||
NodeInput(this, k_buffer_in), 1);
|
||||
ViewerOutput *new_connected_viewer =
|
||||
viewers.isEmpty() ? nullptr : viewers.first();
|
||||
|
||||
if (new_connected_viewer != connected_viewer_) {
|
||||
if (connected_viewer_) {
|
||||
disconnect(connected_viewer_->get_markers(),
|
||||
&TimelineMarkerList::marker_added, this,
|
||||
&ClipBlock::preview_changed);
|
||||
disconnect(connected_viewer_->get_markers(),
|
||||
&TimelineMarkerList::marker_removed, this,
|
||||
&ClipBlock::preview_changed);
|
||||
disconnect(connected_viewer_->get_markers(),
|
||||
&TimelineMarkerList::marker_modified, this,
|
||||
&ClipBlock::preview_changed);
|
||||
}
|
||||
|
||||
connected_viewer_ = new_connected_viewer;
|
||||
|
||||
if (connected_viewer_) {
|
||||
connect(connected_viewer_->get_markers(),
|
||||
&TimelineMarkerList::marker_added, this,
|
||||
&ClipBlock::preview_changed);
|
||||
connect(connected_viewer_->get_markers(),
|
||||
&TimelineMarkerList::marker_removed, this,
|
||||
&ClipBlock::preview_changed);
|
||||
connect(connected_viewer_->get_markers(),
|
||||
&TimelineMarkerList::marker_modified, this,
|
||||
&ClipBlock::preview_changed);
|
||||
}
|
||||
}
|
||||
|
||||
super::invalidate_cache(adj, from, element, options);
|
||||
} else {
|
||||
// Otherwise, pass signal along normally
|
||||
super::invalidate_cache(range, from, element, options);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::LinkChangeEvent()
|
||||
{
|
||||
block_links_.clear();
|
||||
|
||||
foreach (Node *n, links()) {
|
||||
ClipBlock *b = dynamic_cast<ClipBlock *>(n);
|
||||
|
||||
if (b) {
|
||||
block_links_.append(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::InputConnectedEvent(const QString &input, int element,
|
||||
Node *output)
|
||||
{
|
||||
super::InputConnectedEvent(input, element, output);
|
||||
|
||||
if (input == k_buffer_in) {
|
||||
connect(output->thumbnail_cache(), &FrameHashCache::invalidated, this,
|
||||
&Block::preview_changed);
|
||||
connect(output->waveform_cache(), &AudioPlaybackCache::invalidated,
|
||||
this, &Block::preview_changed);
|
||||
connect(output->video_frame_cache(), &FrameHashCache::invalidated, this,
|
||||
&Block::preview_changed);
|
||||
connect(output->audio_playback_cache(),
|
||||
&AudioPlaybackCache::invalidated, this, &Block::preview_changed);
|
||||
connect(output->thumbnail_cache(), &FrameHashCache::validated, this,
|
||||
&Block::preview_changed);
|
||||
connect(output->waveform_cache(), &AudioPlaybackCache::validated, this,
|
||||
&Block::preview_changed);
|
||||
connect(output->video_frame_cache(), &FrameHashCache::validated, this,
|
||||
&Block::preview_changed);
|
||||
connect(output->audio_playback_cache(), &AudioPlaybackCache::validated,
|
||||
this, &Block::preview_changed);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::InputDisconnectedEvent(const QString &input, int element,
|
||||
Node *output)
|
||||
{
|
||||
super::InputDisconnectedEvent(input, element, output);
|
||||
|
||||
if (input == k_buffer_in) {
|
||||
disconnect(output->thumbnail_cache(), &FrameHashCache::invalidated,
|
||||
this, &Block::preview_changed);
|
||||
disconnect(output->waveform_cache(), &AudioPlaybackCache::invalidated,
|
||||
this, &Block::preview_changed);
|
||||
disconnect(output->video_frame_cache(), &FrameHashCache::invalidated,
|
||||
this, &Block::preview_changed);
|
||||
disconnect(output->audio_playback_cache(),
|
||||
&AudioPlaybackCache::invalidated, this,
|
||||
&Block::preview_changed);
|
||||
disconnect(output->thumbnail_cache(), &FrameHashCache::validated, this,
|
||||
&Block::preview_changed);
|
||||
disconnect(output->waveform_cache(), &AudioPlaybackCache::validated,
|
||||
this, &Block::preview_changed);
|
||||
disconnect(output->video_frame_cache(), &FrameHashCache::validated,
|
||||
this, &Block::preview_changed);
|
||||
disconnect(output->audio_playback_cache(),
|
||||
&AudioPlaybackCache::validated, this,
|
||||
&Block::preview_changed);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
super::InputValueChangedEvent(input, element);
|
||||
|
||||
if (input == k_auto_cache_input) {
|
||||
if (is_autocaching()) {
|
||||
request_invalidated_from_connected();
|
||||
} else {
|
||||
Track::Type type = get_track_type();
|
||||
|
||||
if (Node *connected = get_connected_output(k_buffer_in)) {
|
||||
if (type == Track::k_video) {
|
||||
emit connected->video_frame_cache()->cancel_all();
|
||||
} else if (type == Track::k_audio) {
|
||||
emit connected->audio_playback_cache()->cancel_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (input == k_loop_mode_input) {
|
||||
emit preview_changed();
|
||||
}
|
||||
}
|
||||
|
||||
TimeRange ClipBlock::input_time_adjustment(const QString &input, int element,
|
||||
const TimeRange &input_time,
|
||||
bool clamp) const
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
if (input == k_buffer_in) {
|
||||
return TimeRange(sequence_to_media_time(input_time.in()),
|
||||
sequence_to_media_time(input_time.out()));
|
||||
}
|
||||
|
||||
return super::input_time_adjustment(input, element, input_time, clamp);
|
||||
}
|
||||
|
||||
TimeRange ClipBlock::output_time_adjustment(const QString &input, int element,
|
||||
const TimeRange &input_time) const
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
if (input == k_buffer_in) {
|
||||
return TimeRange(media_to_sequence_time(input_time.in()),
|
||||
media_to_sequence_time(input_time.out()));
|
||||
}
|
||||
|
||||
return super::output_time_adjustment(input, element, input_time);
|
||||
}
|
||||
|
||||
void ClipBlock::value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
Q_UNUSED(globals)
|
||||
|
||||
// We discard most values here except for the buffer we received
|
||||
NodeValue data = value[k_buffer_in];
|
||||
|
||||
table->clear();
|
||||
if (data.type() != NodeValue::k_none) {
|
||||
table->push(data);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_buffer_in, tr("Buffer"));
|
||||
set_input_name(k_media_in_input, tr("Media In"));
|
||||
set_input_name(k_speed_input, tr("Speed"));
|
||||
set_input_name(k_reverse_input, tr("Reverse"));
|
||||
set_input_name(k_maintain_audio_pitch_input, tr("Maintain Audio Pitch"));
|
||||
set_input_name(k_loop_mode_input, tr("Loop"));
|
||||
set_combo_box_strings(k_loop_mode_input, { tr("None"), tr("Loop"), tr("Clamp") });
|
||||
}
|
||||
|
||||
void ClipBlock::add_cache_passthrough_from(ClipBlock *other)
|
||||
{
|
||||
if (auto tc = this->video_frame_cache()) {
|
||||
if (auto oc = other->video_frame_cache()) {
|
||||
tc->set_passthrough(oc);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto tc = this->audio_playback_cache()) {
|
||||
if (auto oc = other->audio_playback_cache()) {
|
||||
tc->set_passthrough(oc);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto tc = this->thumbnails()) {
|
||||
if (auto oc = other->thumbnails()) {
|
||||
tc->set_passthrough(oc);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto tc = this->waveform()) {
|
||||
if (auto oc = other->waveform()) {
|
||||
tc->set_passthrough(oc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::ConnectedToPreviewEvent()
|
||||
{
|
||||
request_invalidated_from_connected();
|
||||
}
|
||||
|
||||
TimeRange ClipBlock::media_range() const
|
||||
{
|
||||
return input_time_adjustment(k_buffer_in, -1, TimeRange(0, length()), false);
|
||||
}
|
||||
|
||||
MultiCamNode *ClipBlock::find_multicam()
|
||||
{
|
||||
auto v = find_input_nodes_connected_to_input<MultiCamNode>(
|
||||
NodeInput(this, k_buffer_in), 1);
|
||||
if (v.empty()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return v.first();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CLIPBLOCK_H
|
||||
#define OAK_CLIPBLOCK_H
|
||||
|
||||
#include "audio/audiovisualwaveform.h"
|
||||
#include "codec/decoder.h"
|
||||
#include "node/block/block.h"
|
||||
#include "node/input/multicam/multicamnode.h"
|
||||
#include "node/output/track/track.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ViewerOutput;
|
||||
|
||||
/**
|
||||
* @brief Node that represents a block of Media
|
||||
*/
|
||||
class ClipBlock : public Block {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClipBlock();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(ClipBlock)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void set_length_and_media_out(const Rational &length) override;
|
||||
virtual void set_length_and_media_in(const Rational &length) override;
|
||||
|
||||
Track::Type get_track_type() const
|
||||
{
|
||||
if (track()) {
|
||||
return track()->type();
|
||||
} else {
|
||||
return Track::k_none;
|
||||
}
|
||||
}
|
||||
|
||||
virtual Node::ValueHint
|
||||
get_value_hint_for_input(const QString &input, int element = -1) const override;
|
||||
|
||||
Rational media_in() const;
|
||||
void set_media_in(const Rational &media_in);
|
||||
|
||||
bool is_autocaching() const
|
||||
{
|
||||
return get_standard_value(k_auto_cache_input).toBool();
|
||||
}
|
||||
void set_autocache(bool e);
|
||||
|
||||
void discard_cache();
|
||||
|
||||
virtual void invalidate_cache(const TimeRange &range, const QString &from,
|
||||
int element,
|
||||
InvalidateCacheOptions options) override;
|
||||
|
||||
virtual TimeRange input_time_adjustment(const QString &input, int element,
|
||||
const TimeRange &input_time,
|
||||
bool clamp) const override;
|
||||
|
||||
virtual TimeRange
|
||||
output_time_adjustment(const QString &input, int element,
|
||||
const TimeRange &input_time) const override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
void
|
||||
request_invalidated_from_connected(bool force_all = false,
|
||||
const TimeRange &intersect = TimeRange());
|
||||
|
||||
double speed() const
|
||||
{
|
||||
return get_standard_value(k_speed_input).toDouble();
|
||||
}
|
||||
|
||||
bool reverse() const
|
||||
{
|
||||
return get_standard_value(k_reverse_input).toBool();
|
||||
}
|
||||
|
||||
void set_reverse(bool e)
|
||||
{
|
||||
set_standard_value(k_reverse_input, e);
|
||||
}
|
||||
|
||||
bool maintain_audio_pitch() const
|
||||
{
|
||||
return get_standard_value(k_maintain_audio_pitch_input).toBool();
|
||||
}
|
||||
|
||||
void set_maintain_audio_pitch(bool e)
|
||||
{
|
||||
set_standard_value(k_maintain_audio_pitch_input, e);
|
||||
}
|
||||
|
||||
TransitionBlock *in_transition()
|
||||
{
|
||||
return in_transition_;
|
||||
}
|
||||
|
||||
void set_in_transition(TransitionBlock *t)
|
||||
{
|
||||
in_transition_ = t;
|
||||
}
|
||||
|
||||
TransitionBlock *out_transition()
|
||||
{
|
||||
return out_transition_;
|
||||
}
|
||||
|
||||
void set_out_transition(TransitionBlock *t)
|
||||
{
|
||||
out_transition_ = t;
|
||||
}
|
||||
|
||||
const QVector<Block *> &block_links() const
|
||||
{
|
||||
return block_links_;
|
||||
}
|
||||
|
||||
FrameHashCache *connected_video_cache() const
|
||||
{
|
||||
if (Node *n = get_connected_output(k_buffer_in)) {
|
||||
return n->video_frame_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AudioPlaybackCache *connected_audio_cache() const
|
||||
{
|
||||
if (Node *n = get_connected_output(k_buffer_in)) {
|
||||
return n->audio_playback_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FrameHashCache *thumbnails()
|
||||
{
|
||||
if (Node *n = get_connected_output(k_buffer_in)) {
|
||||
return n->thumbnail_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AudioWaveformCache *waveform()
|
||||
{
|
||||
if (Node *n = get_connected_output(k_buffer_in)) {
|
||||
return n->waveform_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void add_cache_passthrough_from(ClipBlock *other);
|
||||
|
||||
ViewerOutput *connected_viewer() const
|
||||
{
|
||||
return connected_viewer_;
|
||||
}
|
||||
|
||||
virtual TimeRange get_video_cache_range() const override
|
||||
{
|
||||
return TimeRange(0, length());
|
||||
}
|
||||
|
||||
virtual TimeRange get_audio_cache_range() const override
|
||||
{
|
||||
return TimeRange(0, length());
|
||||
}
|
||||
|
||||
virtual void ConnectedToPreviewEvent() override;
|
||||
|
||||
TimeRange media_range() const;
|
||||
|
||||
/**
|
||||
* @brief Get currently set loop mode
|
||||
*/
|
||||
LoopMode loop_mode() const
|
||||
{
|
||||
return static_cast<LoopMode>(get_standard_value(k_loop_mode_input).toInt());
|
||||
}
|
||||
|
||||
void set_loop_mode(LoopMode l)
|
||||
{
|
||||
set_standard_value(k_loop_mode_input, int(l));
|
||||
}
|
||||
|
||||
MultiCamNode *find_multicam();
|
||||
|
||||
static const QString k_buffer_in;
|
||||
static const QString k_media_in_input;
|
||||
static const QString k_speed_input;
|
||||
static const QString k_reverse_input;
|
||||
static const QString k_maintain_audio_pitch_input;
|
||||
static const QString k_loop_mode_input;
|
||||
|
||||
static const QString k_auto_cache_input;
|
||||
|
||||
protected:
|
||||
virtual void LinkChangeEvent() override;
|
||||
|
||||
virtual void InputConnectedEvent(const QString &input, int element,
|
||||
Node *output) override;
|
||||
|
||||
virtual void InputDisconnectedEvent(const QString &input, int element,
|
||||
Node *output) override;
|
||||
|
||||
virtual void InputValueChangedEvent(const QString &input,
|
||||
int element) override;
|
||||
|
||||
private:
|
||||
enum SequenceToMediaTimeFlag {
|
||||
k_stm_none = 0x0,
|
||||
k_stm_ignore_reverse = 0x1,
|
||||
k_stm_ignore_speed = 0x2,
|
||||
k_stm_ignore_loop = 0x4
|
||||
};
|
||||
|
||||
Rational sequence_to_media_time(const Rational &sequence_time,
|
||||
uint64_t flags = k_stm_none) const;
|
||||
|
||||
Rational media_to_sequence_time(const Rational &media_time) const;
|
||||
|
||||
void request_range_from_connected(const TimeRange &range);
|
||||
|
||||
void request_range_for_cache(PlaybackCache *cache, const TimeRange &max_range,
|
||||
const TimeRange &range, bool invalidate,
|
||||
bool request);
|
||||
void request_invalidated_for_cache(PlaybackCache *cache,
|
||||
const TimeRange &max_range);
|
||||
|
||||
bool get_adjusted_thumbnail_range(TimeRange *r) const;
|
||||
|
||||
QVector<Block *> block_links_;
|
||||
|
||||
TransitionBlock *in_transition_;
|
||||
TransitionBlock *out_transition_;
|
||||
|
||||
ViewerOutput *connected_viewer_;
|
||||
|
||||
private:
|
||||
Rational last_media_in_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEBLOCK_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/block/gap/gap.h
|
||||
node/block/gap/gap.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "gap.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
GapBlock::GapBlock()
|
||||
{
|
||||
}
|
||||
|
||||
QString GapBlock::name() const
|
||||
{
|
||||
return tr("Gap");
|
||||
}
|
||||
|
||||
QString GapBlock::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.gap");
|
||||
}
|
||||
|
||||
QString GapBlock::description() const
|
||||
{
|
||||
return tr("A time-based node that represents an empty space.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_GAPBLOCK_H
|
||||
#define OAK_GAPBLOCK_H
|
||||
|
||||
#include "node/block/block.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Node that represents nothing in its respective track for a certain period of time
|
||||
*/
|
||||
class GapBlock : public Block {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GapBlock();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(GapBlock)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QString description() const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEBLOCK_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/block/subtitle/subtitle.cpp
|
||||
node/block/subtitle/subtitle.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,74 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "subtitle.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super ClipBlock
|
||||
|
||||
const QString SubtitleBlock::k_text_in = QStringLiteral("text_in");
|
||||
|
||||
SubtitleBlock::SubtitleBlock()
|
||||
{
|
||||
add_input(k_text_in, NodeValue::k_text,
|
||||
InputFlags(k_input_flag_not_connectable | k_input_flag_not_keyframable));
|
||||
|
||||
set_input_flag(k_buffer_in, k_input_flag_hidden);
|
||||
set_input_flag(k_length_input, k_input_flag_hidden);
|
||||
set_input_flag(k_media_in_input, k_input_flag_hidden);
|
||||
set_input_flag(k_speed_input, k_input_flag_hidden);
|
||||
set_input_flag(k_reverse_input, k_input_flag_hidden);
|
||||
set_input_flag(k_maintain_audio_pitch_input, k_input_flag_hidden);
|
||||
|
||||
// Undo block flag that hides in param view
|
||||
set_flag(k_dont_show_in_param_view, false);
|
||||
}
|
||||
|
||||
QString SubtitleBlock::name() const
|
||||
{
|
||||
if (get_text().isEmpty()) {
|
||||
return tr("Subtitle");
|
||||
} else {
|
||||
return get_text();
|
||||
}
|
||||
}
|
||||
|
||||
QString SubtitleBlock::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.subtitle");
|
||||
}
|
||||
|
||||
QString SubtitleBlock::description() const
|
||||
{
|
||||
return tr(
|
||||
"A time-based node representing a single subtitle element for a certain period of time.");
|
||||
}
|
||||
|
||||
void SubtitleBlock::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_text_in, tr("Text"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_SUBTITLEBLOCK_H
|
||||
#define OAK_SUBTITLEBLOCK_H
|
||||
|
||||
#include "node/block/clip/clip.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class SubtitleBlock : public ClipBlock {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SubtitleBlock();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(SubtitleBlock)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
static const QString k_text_in;
|
||||
|
||||
QString get_text() const
|
||||
{
|
||||
return get_standard_value(k_text_in).toString();
|
||||
}
|
||||
|
||||
void set_text(const QString &text)
|
||||
{
|
||||
set_standard_value(k_text_in, text);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_SUBTITLEBLOCK_H
|
||||
@@ -0,0 +1,25 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(crossdissolve)
|
||||
add_subdirectory(diptocolor)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/block/transition/transition.h
|
||||
node/block/transition/transition.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/block/transition/crossdissolve/crossdissolvetransition.h
|
||||
node/block/transition/crossdissolve/crossdissolvetransition.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,95 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "crossdissolvetransition.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
CrossDissolveTransition::CrossDissolveTransition()
|
||||
{
|
||||
}
|
||||
|
||||
QString CrossDissolveTransition::name() const
|
||||
{
|
||||
return tr("Cross Dissolve");
|
||||
}
|
||||
|
||||
QString CrossDissolveTransition::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.crossdissolve");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> CrossDissolveTransition::category() const
|
||||
{
|
||||
return { k_category_transition };
|
||||
}
|
||||
|
||||
QString CrossDissolveTransition::description() const
|
||||
{
|
||||
return tr("Smoothly transition between two clips.");
|
||||
}
|
||||
|
||||
ShaderCode
|
||||
CrossDissolveTransition::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
|
||||
return ShaderCode(
|
||||
FileFunctions::read_file_as_string(":/shaders/crossdissolve.frag"),
|
||||
QString());
|
||||
}
|
||||
|
||||
void CrossDissolveTransition::SampleJobEvent(const SampleBuffer &from_samples,
|
||||
const SampleBuffer &to_samples,
|
||||
SampleBuffer &out_samples,
|
||||
double time_in) const
|
||||
{
|
||||
for (size_t i = 0; i < out_samples.sample_count(); i++) {
|
||||
double this_sample_time =
|
||||
out_samples.audio_params().samples_to_time(i).to_double() + time_in;
|
||||
double progress = get_total_progress(this_sample_time);
|
||||
|
||||
for (int j = 0; j < out_samples.audio_params().channel_count(); j++) {
|
||||
out_samples.data(j)[i] = 0;
|
||||
|
||||
if (from_samples.is_allocated()) {
|
||||
if (i < from_samples.sample_count()) {
|
||||
out_samples.data(j)[i] += from_samples.data(j)[i] *
|
||||
transform_curve(1.0 - progress);
|
||||
}
|
||||
}
|
||||
|
||||
if (to_samples.is_allocated()) {
|
||||
// Offset input samples from the end
|
||||
size_t remain =
|
||||
(out_samples.sample_count() - to_samples.sample_count());
|
||||
if (i >= remain) {
|
||||
qint64 in_index = i - remain;
|
||||
out_samples.data(j)[i] +=
|
||||
to_samples.data(j)[in_index] * transform_curve(progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CROSSDISSOLVETRANSITION_H
|
||||
#define OAK_CROSSDISSOLVETRANSITION_H
|
||||
|
||||
#include "node/block/transition/transition.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class CrossDissolveTransition : public TransitionBlock {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CrossDissolveTransition();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(CrossDissolveTransition)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
//virtual void Retranslate() override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
|
||||
protected:
|
||||
virtual void SampleJobEvent(const SampleBuffer &from_samples,
|
||||
const SampleBuffer &to_samples,
|
||||
SampleBuffer &out_samples,
|
||||
double time_in) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_CROSSDISSOLVETRANSITION_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/block/transition/diptocolor/diptocolortransition.h
|
||||
node/block/transition/diptocolor/diptocolortransition.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,80 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "diptocolortransition.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString DipToColorTransition::k_color_input = QStringLiteral("color_in");
|
||||
|
||||
#define super TransitionBlock
|
||||
|
||||
DipToColorTransition::DipToColorTransition()
|
||||
{
|
||||
add_input(k_color_input, NodeValue::k_color,
|
||||
QVariant::fromValue(Color(0, 0, 0)));
|
||||
}
|
||||
|
||||
QString DipToColorTransition::name() const
|
||||
{
|
||||
return tr("Dip To Color");
|
||||
}
|
||||
|
||||
QString DipToColorTransition::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.diptocolor");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> DipToColorTransition::category() const
|
||||
{
|
||||
return { k_category_transition };
|
||||
}
|
||||
|
||||
QString DipToColorTransition::description() const
|
||||
{
|
||||
return tr("Transition between clips by dipping to a color.");
|
||||
}
|
||||
|
||||
ShaderCode
|
||||
DipToColorTransition::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
|
||||
return ShaderCode(
|
||||
FileFunctions::read_file_as_string(":/shaders/diptoblack.frag"),
|
||||
QString());
|
||||
}
|
||||
|
||||
void DipToColorTransition::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_color_input, tr("Color"));
|
||||
}
|
||||
|
||||
void DipToColorTransition::ShaderJobEvent(const NodeValueRow &value,
|
||||
ShaderJob *job) const
|
||||
{
|
||||
job->insert(k_color_input, value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_DIPTOCOLORTRANSITION_H
|
||||
#define OAK_DIPTOCOLORTRANSITION_H
|
||||
|
||||
#include "node/block/transition/transition.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class DipToColorTransition : public TransitionBlock {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DipToColorTransition();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(DipToColorTransition)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
static const QString k_color_input;
|
||||
|
||||
protected:
|
||||
virtual void ShaderJobEvent(const NodeValueRow &value,
|
||||
ShaderJob *job) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_DIPTOCOLORTRANSITION_H
|
||||
@@ -0,0 +1,344 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "transition.h"
|
||||
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super Block
|
||||
|
||||
const QString TransitionBlock::k_out_block_input = QStringLiteral("out_block_in");
|
||||
const QString TransitionBlock::k_in_block_input = QStringLiteral("in_block_in");
|
||||
const QString TransitionBlock::k_curve_input = QStringLiteral("curve_in");
|
||||
const QString TransitionBlock::k_center_input = QStringLiteral("center_in");
|
||||
|
||||
TransitionBlock::TransitionBlock()
|
||||
: connected_out_block_(nullptr)
|
||||
, connected_in_block_(nullptr)
|
||||
{
|
||||
add_input(k_out_block_input, NodeValue::k_none,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_in_block_input, NodeValue::k_none,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_curve_input, NodeValue::k_combo,
|
||||
InputFlags(k_input_flag_not_keyframable | k_input_flag_not_connectable));
|
||||
|
||||
add_input(k_center_input, NodeValue::k_rational,
|
||||
InputFlags(k_input_flag_not_keyframable | k_input_flag_not_connectable));
|
||||
set_input_property(k_center_input, QStringLiteral("view"),
|
||||
slider::k_time);
|
||||
set_input_property(k_center_input, QStringLiteral("viewlock"), true);
|
||||
|
||||
set_flag(k_dont_show_in_param_view, false);
|
||||
}
|
||||
|
||||
void TransitionBlock::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_out_block_input, tr("From"));
|
||||
set_input_name(k_in_block_input, tr("To"));
|
||||
set_input_name(k_curve_input, tr("Curve"));
|
||||
set_input_name(k_center_input, tr("Center Offset"));
|
||||
|
||||
// These must correspond to the CurveType enum
|
||||
set_combo_box_strings(k_curve_input,
|
||||
{ tr("Linear"), tr("Exponential"), tr("Logarithmic") });
|
||||
}
|
||||
|
||||
Rational TransitionBlock::in_offset() const
|
||||
{
|
||||
if (is_dual_transition()) {
|
||||
return length() / 2 + offset_center();
|
||||
} else if (connected_in_block()) {
|
||||
return length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Rational TransitionBlock::out_offset() const
|
||||
{
|
||||
if (is_dual_transition()) {
|
||||
return length() / 2 - offset_center();
|
||||
} else if (connected_out_block()) {
|
||||
return length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Rational TransitionBlock::offset_center() const
|
||||
{
|
||||
return get_standard_value(k_center_input).value<Rational>();
|
||||
}
|
||||
|
||||
void TransitionBlock::set_offset_center(const Rational &r)
|
||||
{
|
||||
set_standard_value(k_center_input, QVariant::fromValue(r));
|
||||
}
|
||||
|
||||
void TransitionBlock::set_offsets_and_length(const Rational &in_offset,
|
||||
const Rational &out_offset)
|
||||
{
|
||||
Rational len = in_offset + out_offset;
|
||||
Rational center = len / 2 - in_offset;
|
||||
|
||||
set_length_and_media_out(len);
|
||||
set_offset_center(center);
|
||||
}
|
||||
|
||||
Block *TransitionBlock::connected_out_block() const
|
||||
{
|
||||
return connected_out_block_;
|
||||
}
|
||||
|
||||
Block *TransitionBlock::connected_in_block() const
|
||||
{
|
||||
return connected_in_block_;
|
||||
}
|
||||
|
||||
double TransitionBlock::get_total_progress(const double &time) const
|
||||
{
|
||||
return get_internal_transition_time(time) / length().to_double();
|
||||
}
|
||||
|
||||
double TransitionBlock::get_out_progress(const double &time) const
|
||||
{
|
||||
if (out_offset() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::clamp(
|
||||
1.0 - (get_internal_transition_time(time) / out_offset().to_double()), 0.0,
|
||||
1.0);
|
||||
}
|
||||
|
||||
double TransitionBlock::get_in_progress(const double &time) const
|
||||
{
|
||||
if (in_offset() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::clamp(
|
||||
(get_internal_transition_time(time) - out_offset().to_double()) /
|
||||
in_offset().to_double(),
|
||||
0.0, 1.0);
|
||||
}
|
||||
|
||||
double TransitionBlock::get_internal_transition_time(const double &time) const
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
void TransitionBlock::insert_transition_times(AcceleratedJob *job,
|
||||
const double &time) const
|
||||
{
|
||||
// Provides total transition progress from 0.0 (start) - 1.0 (end)
|
||||
job->insert(QStringLiteral("ove_tprog_all"),
|
||||
NodeValue(NodeValue::k_float, get_total_progress(time), this));
|
||||
|
||||
// Provides progress of out section from 1.0 (start) - 0.0 (end)
|
||||
job->insert(QStringLiteral("ove_tprog_out"),
|
||||
NodeValue(NodeValue::k_float, get_out_progress(time), this));
|
||||
|
||||
// Provides progress of in section from 0.0 (start) - 1.0 (end)
|
||||
job->insert(QStringLiteral("ove_tprog_in"),
|
||||
NodeValue(NodeValue::k_float, get_in_progress(time), this));
|
||||
}
|
||||
|
||||
void TransitionBlock::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
NodeValue out_buffer = value[k_out_block_input];
|
||||
NodeValue in_buffer = value[k_in_block_input];
|
||||
NodeValue::Type data_type = (out_buffer.type() != NodeValue::k_none) ?
|
||||
out_buffer.type() :
|
||||
in_buffer.type();
|
||||
|
||||
NodeValue::Type job_type = NodeValue::k_none;
|
||||
QVariant push_job;
|
||||
|
||||
if (data_type == NodeValue::k_texture) {
|
||||
// This must be a visual transition
|
||||
ShaderJob job;
|
||||
|
||||
if (out_buffer.type() != NodeValue::k_none) {
|
||||
job.insert(k_out_block_input, out_buffer);
|
||||
} else {
|
||||
job.insert(k_out_block_input, NodeValue(NodeValue::k_texture, nullptr));
|
||||
}
|
||||
|
||||
if (in_buffer.type() != NodeValue::k_none) {
|
||||
job.insert(k_in_block_input, in_buffer);
|
||||
} else {
|
||||
job.insert(k_in_block_input, NodeValue(NodeValue::k_texture, nullptr));
|
||||
}
|
||||
|
||||
job.insert(k_curve_input, value);
|
||||
|
||||
double time = globals.time().in().to_double();
|
||||
insert_transition_times(&job, time);
|
||||
|
||||
ShaderJobEvent(value, &job);
|
||||
|
||||
job_type = NodeValue::k_texture;
|
||||
push_job = QVariant::fromValue(Texture::job(globals.vparams(), job));
|
||||
} else if (data_type == NodeValue::k_samples) {
|
||||
// This must be an audio transition
|
||||
SampleBuffer from_samples = out_buffer.to_samples();
|
||||
SampleBuffer to_samples = in_buffer.to_samples();
|
||||
|
||||
if (from_samples.is_allocated() || to_samples.is_allocated()) {
|
||||
double time_in = globals.time().in().to_double();
|
||||
double time_out = globals.time().out().to_double();
|
||||
|
||||
const AudioParams ¶ms = (from_samples.is_allocated()) ?
|
||||
from_samples.audio_params() :
|
||||
to_samples.audio_params();
|
||||
|
||||
SampleBuffer out_samples;
|
||||
|
||||
if (params.is_valid()) {
|
||||
int nb_samples = params.time_to_samples(time_out - time_in);
|
||||
|
||||
out_samples = SampleBuffer(params, nb_samples);
|
||||
SampleJobEvent(from_samples, to_samples, out_samples, time_in);
|
||||
}
|
||||
|
||||
job_type = NodeValue::k_samples;
|
||||
push_job = QVariant::fromValue(out_samples);
|
||||
}
|
||||
}
|
||||
|
||||
if (!push_job.isNull()) {
|
||||
table->push(job_type, push_job, this);
|
||||
}
|
||||
}
|
||||
|
||||
void TransitionBlock::invalidate_cache(const TimeRange &range,
|
||||
const QString &from, int element,
|
||||
InvalidateCacheOptions options)
|
||||
{
|
||||
TimeRange r = range;
|
||||
|
||||
if (from == k_out_block_input || from == k_in_block_input) {
|
||||
Block *n = dynamic_cast<Block *>(get_connected_output(from));
|
||||
if (n) {
|
||||
r = Track::transform_range_from_block(n, r);
|
||||
}
|
||||
}
|
||||
|
||||
super::invalidate_cache(r, from, element, options);
|
||||
}
|
||||
|
||||
double TransitionBlock::transform_curve(double linear) const
|
||||
{
|
||||
switch (static_cast<CurveType>(get_standard_value(k_curve_input).toInt())) {
|
||||
case k_linear:
|
||||
break;
|
||||
case k_exponential:
|
||||
linear *= linear;
|
||||
break;
|
||||
case k_logarithmic:
|
||||
linear = std::sqrt(linear);
|
||||
break;
|
||||
}
|
||||
|
||||
return linear;
|
||||
}
|
||||
|
||||
void TransitionBlock::InputConnectedEvent(const QString &input, int element,
|
||||
Node *output)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
if (input == k_out_block_input) {
|
||||
// If node is not a block, this will just be null
|
||||
if ((connected_out_block_ = dynamic_cast<ClipBlock *>(output))) {
|
||||
connected_out_block_->set_out_transition(this);
|
||||
}
|
||||
} else if (input == k_in_block_input) {
|
||||
// If node is not a block, this will just be null
|
||||
if ((connected_in_block_ = dynamic_cast<ClipBlock *>(output))) {
|
||||
connected_in_block_->set_in_transition(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TransitionBlock::InputDisconnectedEvent(const QString &input, int element,
|
||||
Node *output)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
Q_UNUSED(output)
|
||||
|
||||
if (input == k_out_block_input) {
|
||||
if (connected_out_block_) {
|
||||
connected_out_block_->set_out_transition(nullptr);
|
||||
connected_out_block_ = nullptr;
|
||||
}
|
||||
} else if (input == k_in_block_input) {
|
||||
if (connected_in_block_) {
|
||||
connected_in_block_->set_in_transition(nullptr);
|
||||
connected_in_block_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimeRange TransitionBlock::input_time_adjustment(const QString &input,
|
||||
int element,
|
||||
const TimeRange &input_time,
|
||||
bool clamp) const
|
||||
{
|
||||
if (input == k_in_block_input || input == k_out_block_input) {
|
||||
Block *block = dynamic_cast<Block *>(get_connected_output(input));
|
||||
if (block) {
|
||||
// Retransform time as if it came from the track
|
||||
return input_time + in() - block->in();
|
||||
}
|
||||
}
|
||||
|
||||
return super::input_time_adjustment(input, element, input_time, clamp);
|
||||
}
|
||||
|
||||
TimeRange
|
||||
TransitionBlock::output_time_adjustment(const QString &input, int element,
|
||||
const TimeRange &input_time) const
|
||||
{
|
||||
if (input == k_in_block_input || input == k_out_block_input) {
|
||||
Block *block = dynamic_cast<Block *>(get_connected_output(input));
|
||||
if (block) {
|
||||
return input_time + block->in() - in();
|
||||
}
|
||||
}
|
||||
|
||||
return super::output_time_adjustment(input, element, input_time);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_TRANSITIONBLOCK_H
|
||||
#define OAK_TRANSITIONBLOCK_H
|
||||
|
||||
#include "node/block/block.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ClipBlock;
|
||||
|
||||
class TransitionBlock : public Block {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TransitionBlock();
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
Rational in_offset() const;
|
||||
Rational out_offset() const;
|
||||
|
||||
/**
|
||||
* @brief Return the "middle point" of the transition, relative to the transition
|
||||
*
|
||||
* Used to calculate in/out offsets.
|
||||
*
|
||||
* 0 means the center of the transition is right in the middle and the in and out offsets will
|
||||
* be equal.
|
||||
*/
|
||||
Rational offset_center() const;
|
||||
void set_offset_center(const Rational &r);
|
||||
|
||||
void set_offsets_and_length(const Rational &in_offset,
|
||||
const Rational &out_offset);
|
||||
|
||||
bool is_dual_transition() const
|
||||
{
|
||||
return connected_out_block() && connected_in_block();
|
||||
}
|
||||
|
||||
Block *connected_out_block() const;
|
||||
Block *connected_in_block() const;
|
||||
|
||||
double get_total_progress(const double &time) const;
|
||||
double get_out_progress(const double &time) const;
|
||||
double get_in_progress(const double &time) const;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
virtual void invalidate_cache(
|
||||
const TimeRange &range, const QString &from, int element = -1,
|
||||
InvalidateCacheOptions options = InvalidateCacheOptions()) override;
|
||||
|
||||
static const QString k_out_block_input;
|
||||
static const QString k_in_block_input;
|
||||
static const QString k_curve_input;
|
||||
static const QString k_center_input;
|
||||
|
||||
protected:
|
||||
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob *job) const
|
||||
{
|
||||
}
|
||||
|
||||
virtual void SampleJobEvent(const SampleBuffer &from_samples,
|
||||
const SampleBuffer &to_samples,
|
||||
SampleBuffer &out_samples, double time_in) const
|
||||
{
|
||||
}
|
||||
|
||||
double transform_curve(double linear) const;
|
||||
|
||||
virtual void InputConnectedEvent(const QString &input, int element,
|
||||
Node *output) override;
|
||||
|
||||
virtual void InputDisconnectedEvent(const QString &input, int element,
|
||||
Node *output) override;
|
||||
|
||||
virtual TimeRange input_time_adjustment(const QString &input, int element,
|
||||
const TimeRange &input_time,
|
||||
bool clamp) const override;
|
||||
|
||||
virtual TimeRange
|
||||
output_time_adjustment(const QString &input, int element,
|
||||
const TimeRange &input_time) const override;
|
||||
|
||||
private:
|
||||
enum CurveType { k_linear, k_exponential, k_logarithmic };
|
||||
|
||||
double get_internal_transition_time(const double &time) const;
|
||||
|
||||
void insert_transition_times(AcceleratedJob *job, const double &time) const;
|
||||
|
||||
ClipBlock *connected_out_block_;
|
||||
|
||||
ClipBlock *connected_in_block_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_TRANSITIONBLOCK_H
|
||||
@@ -0,0 +1,29 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(colormanager)
|
||||
add_subdirectory(displaytransform)
|
||||
add_subdirectory(ociobase)
|
||||
add_subdirectory(ociogradingtransformlinear)
|
||||
add_subdirectory(ociogradingtransformlog)
|
||||
add_subdirectory(ociolut)
|
||||
add_subdirectory(threewaycolor)
|
||||
add_subdirectory(whitebalance)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/color/colormanager/colormanager.cpp
|
||||
node/color/colormanager/colormanager.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,328 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "colormanager.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "common/define.h"
|
||||
#include "common/filefunctions.h"
|
||||
#include "config/config.h"
|
||||
#include "node/project.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super Node
|
||||
|
||||
ocio::ConstConfigRcPtr ColorManager::default_config = nullptr;
|
||||
|
||||
ColorManager::ColorManager(Project *project)
|
||||
: QObject(project)
|
||||
, config_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorManager::init()
|
||||
{
|
||||
// Set config to our built-in default
|
||||
config_ = get_default_config();
|
||||
set_default_input_color_space(config_->getCanonicalName(ocio::ROLE_DEFAULT));
|
||||
project()->set_color_reference_space(ocio::ROLE_SCENE_LINEAR);
|
||||
}
|
||||
|
||||
ocio::ConstConfigRcPtr ColorManager::get_config() const
|
||||
{
|
||||
return config_;
|
||||
}
|
||||
|
||||
ocio::ConstConfigRcPtr
|
||||
ColorManager::create_config_from_file(const QString &filename)
|
||||
{
|
||||
return ocio::Config::CreateFromFile(filename.toUtf8());
|
||||
}
|
||||
|
||||
QString ColorManager::get_config_filename() const
|
||||
{
|
||||
return project()->get_color_config_filename();
|
||||
}
|
||||
|
||||
ocio::ConstConfigRcPtr ColorManager::get_default_config()
|
||||
{
|
||||
// Set up on first use: Project construction calls ColorManager::Init()
|
||||
// unconditionally, so without this any Project created before
|
||||
// SetUpDefaultConfig() crashed dereferencing a null config.
|
||||
if (!default_config) {
|
||||
set_up_default_config();
|
||||
}
|
||||
|
||||
return default_config;
|
||||
}
|
||||
|
||||
void ColorManager::set_up_default_config()
|
||||
{
|
||||
if (!qEnvironmentVariableIsEmpty("OCIO")) {
|
||||
// Attempt to set config from "OCIO" environment variable
|
||||
try {
|
||||
default_config = ocio::Config::CreateFromEnv();
|
||||
|
||||
return;
|
||||
} catch (ocio::Exception &e) {
|
||||
qWarning()
|
||||
<< "Failed to load config from OCIO environment variable config:"
|
||||
<< e.what();
|
||||
}
|
||||
}
|
||||
|
||||
// Extract OCIO config - kind of hacky, but it'll work
|
||||
QString dir =
|
||||
QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))
|
||||
.filePath(QStringLiteral("ocioconf"));
|
||||
|
||||
FileFunctions::copy_directory(QStringLiteral(":/ocioconf"), dir, true);
|
||||
|
||||
qDebug() << "Extracting default OCIO config to" << dir;
|
||||
|
||||
default_config =
|
||||
create_config_from_file(QDir(dir).filePath(QStringLiteral("config.ocio")));
|
||||
}
|
||||
|
||||
void ColorManager::set_config_filename(const QString &filename)
|
||||
{
|
||||
project()->set_color_config_filename(filename);
|
||||
}
|
||||
|
||||
QStringList ColorManager::list_available_displays()
|
||||
{
|
||||
QStringList displays;
|
||||
|
||||
int number_of_displays = config_->getNumDisplays();
|
||||
|
||||
for (int i = 0; i < number_of_displays; i++) {
|
||||
displays.append(config_->getDisplay(i));
|
||||
}
|
||||
|
||||
return displays;
|
||||
}
|
||||
|
||||
QString ColorManager::get_default_display()
|
||||
{
|
||||
return config_->getDefaultDisplay();
|
||||
}
|
||||
|
||||
QStringList ColorManager::list_available_views(QString display)
|
||||
{
|
||||
QStringList views;
|
||||
|
||||
int number_of_views = config_->getNumViews(display.toUtf8());
|
||||
|
||||
for (int i = 0; i < number_of_views; i++) {
|
||||
views.append(config_->getView(display.toUtf8(), i));
|
||||
}
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
QString ColorManager::get_default_view(const QString &display)
|
||||
{
|
||||
return config_->getDefaultView(display.toUtf8());
|
||||
}
|
||||
|
||||
QStringList ColorManager::list_available_looks()
|
||||
{
|
||||
QStringList looks;
|
||||
|
||||
int number_of_looks = config_->getNumLooks();
|
||||
|
||||
for (int i = 0; i < number_of_looks; i++) {
|
||||
looks.append(config_->getLookNameByIndex(i));
|
||||
}
|
||||
|
||||
return looks;
|
||||
}
|
||||
|
||||
QStringList ColorManager::list_available_colorspaces() const
|
||||
{
|
||||
return list_available_colorspaces(config_);
|
||||
}
|
||||
|
||||
QString ColorManager::get_default_input_color_space() const
|
||||
{
|
||||
return project()->get_default_input_color_space();
|
||||
}
|
||||
|
||||
void ColorManager::set_default_input_color_space(const QString &s)
|
||||
{
|
||||
project()->set_default_input_color_space(s);
|
||||
}
|
||||
|
||||
QString ColorManager::get_colorspace_for_ffmpeg_tags(int primaries,
|
||||
int trc) const
|
||||
{
|
||||
// FFmpeg AVColorPrimaries/AVColorTransferCharacteristic values mapped to
|
||||
// candidate colorspace names, in order of preference
|
||||
struct TagMapping {
|
||||
int primaries;
|
||||
int trc;
|
||||
const char *candidates[3];
|
||||
};
|
||||
|
||||
static const TagMapping k_tag_mappings[] = {
|
||||
{ 1, 1, { "Rec.709 OETF", "Rec.709", "BT.709" } },
|
||||
{ 1, 13, { "sRGB OETF", "sRGB", nullptr } },
|
||||
{ 6, 6, { "Rec.601 OETF (NTSC)", "Rec.601 NTSC", nullptr } },
|
||||
{ 5, 5, { "Rec.601 OETF (PAL)", "Rec.601 PAL", nullptr } },
|
||||
{ 5, 6, { "Rec.601 OETF (PAL)", "Rec.601 PAL", nullptr } },
|
||||
{ 9, 16, { "Rec.2020 PQ", "BT.2020 PQ", "ST 2084 PQ" } },
|
||||
{ 9, 18, { "Rec.2020 HLG", "BT.2020 HLG", "HLG" } },
|
||||
{ 9, 1, { "Rec.2020", "BT.2020", nullptr } },
|
||||
{ 9, 14, { "Rec.2020", "BT.2020", nullptr } },
|
||||
{ 9, 15, { "Rec.2020", "BT.2020", nullptr } },
|
||||
};
|
||||
|
||||
// 0 = unset, 2 = AVCOL_PRI/TRC_UNSPECIFIED
|
||||
if (primaries == 0 || primaries == 2 || trc == 0 || trc == 2) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
const QStringList available = list_available_colorspaces();
|
||||
|
||||
for (const TagMapping &mapping : k_tag_mappings) {
|
||||
if (mapping.primaries == primaries && mapping.trc == trc) {
|
||||
for (const char *candidate : mapping.candidates) {
|
||||
if (candidate &&
|
||||
available.contains(QString::fromUtf8(candidate))) {
|
||||
return QString::fromUtf8(candidate);
|
||||
}
|
||||
}
|
||||
// Known tag pair, but the config has no matching colorspace
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString ColorManager::get_reference_color_space() const
|
||||
{
|
||||
return project()->get_color_reference_space();
|
||||
}
|
||||
|
||||
QString ColorManager::get_compliant_color_space(const QString &s)
|
||||
{
|
||||
if (list_available_colorspaces().contains(s)) {
|
||||
return s;
|
||||
} else {
|
||||
return get_default_input_color_space();
|
||||
}
|
||||
}
|
||||
|
||||
ColorTransform
|
||||
ColorManager::get_compliant_color_space(const ColorTransform &transform,
|
||||
bool force_display)
|
||||
{
|
||||
if (transform.is_display() || force_display) {
|
||||
// Get display information
|
||||
QString display = transform.display();
|
||||
QString view = transform.view();
|
||||
QString look = transform.look();
|
||||
|
||||
// Check if display still exists in config
|
||||
if (!list_available_displays().contains(display)) {
|
||||
display = get_default_display();
|
||||
}
|
||||
|
||||
// Check if view still exists in display
|
||||
if (!list_available_views(display).contains(view)) {
|
||||
view = get_default_view(display);
|
||||
}
|
||||
|
||||
// Check if looks still exists
|
||||
if (!list_available_looks().contains(look)) {
|
||||
look.clear();
|
||||
}
|
||||
|
||||
return ColorTransform(display, view, look);
|
||||
|
||||
} else {
|
||||
QString output = transform.output();
|
||||
|
||||
if (!list_available_colorspaces().contains(output)) {
|
||||
output = get_default_input_color_space();
|
||||
}
|
||||
|
||||
return ColorTransform(output);
|
||||
}
|
||||
}
|
||||
|
||||
QStringList
|
||||
ColorManager::list_available_colorspaces(ocio::ConstConfigRcPtr config)
|
||||
{
|
||||
QStringList spaces;
|
||||
|
||||
if (config) {
|
||||
int number_of_colorspaces = config->getNumColorSpaces();
|
||||
|
||||
for (int i = 0; i < number_of_colorspaces; i++) {
|
||||
spaces.append(config->getColorSpaceNameByIndex(i));
|
||||
}
|
||||
}
|
||||
|
||||
return spaces;
|
||||
}
|
||||
|
||||
void ColorManager::get_default_luma_coefs(double *rgb) const
|
||||
{
|
||||
config_->getDefaultLumaCoefs(rgb);
|
||||
}
|
||||
|
||||
Project *ColorManager::project() const
|
||||
{
|
||||
return static_cast<Project *>(parent());
|
||||
}
|
||||
|
||||
void ColorManager::update_config_from_filename()
|
||||
{
|
||||
try {
|
||||
QString config_filename = get_config_filename();
|
||||
QString old_default_cs = get_default_input_color_space();
|
||||
|
||||
config_ = ocio::Config::CreateFromFile(config_filename.toUtf8());
|
||||
|
||||
// Set new default colorspace appropriately
|
||||
QString new_default = old_default_cs;
|
||||
QStringList available_cs = list_available_colorspaces();
|
||||
for (int i = 0; i < available_cs.size(); i++) {
|
||||
const QString &c = available_cs.at(i);
|
||||
if (c.compare(old_default_cs, Qt::CaseInsensitive)) {
|
||||
new_default = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
set_default_input_color_space(new_default);
|
||||
|
||||
emit config_changed(config_filename);
|
||||
} catch (ocio::Exception &) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_COLORSERVICE_H
|
||||
#define OAK_COLORSERVICE_H
|
||||
|
||||
#include <memory>
|
||||
#include <QMutex>
|
||||
|
||||
#include "codec/frame.h"
|
||||
#include "node/node.h"
|
||||
#include "render/colorprocessor.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ColorManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorManager(Project *project);
|
||||
|
||||
void init();
|
||||
|
||||
ocio::ConstConfigRcPtr get_config() const;
|
||||
|
||||
static ocio::ConstConfigRcPtr create_config_from_file(const QString &filename);
|
||||
|
||||
QString get_config_filename() const;
|
||||
|
||||
static ocio::ConstConfigRcPtr get_default_config();
|
||||
|
||||
static void set_up_default_config();
|
||||
|
||||
void set_config_filename(const QString &filename);
|
||||
|
||||
QStringList list_available_displays();
|
||||
|
||||
QString get_default_display();
|
||||
|
||||
QStringList list_available_views(QString display);
|
||||
|
||||
QString get_default_view(const QString &display);
|
||||
|
||||
QStringList list_available_looks();
|
||||
|
||||
QStringList list_available_colorspaces() const;
|
||||
|
||||
QString get_default_input_color_space() const;
|
||||
|
||||
/**
|
||||
* @brief Auto-detects an input colorspace from media color tags
|
||||
*
|
||||
* Maps raw FFmpeg color primaries/transfer values (as exposed on
|
||||
* VideoParams) to a colorspace of the active OCIO config. Returns an
|
||||
* empty string when the tags are unknown or the config has no matching
|
||||
* colorspace, in which case the default input colorspace applies.
|
||||
*/
|
||||
QString get_colorspace_for_ffmpeg_tags(int primaries, int trc) const;
|
||||
|
||||
void set_default_input_color_space(const QString &s);
|
||||
|
||||
QString get_reference_color_space() const;
|
||||
|
||||
QString get_compliant_color_space(const QString &s);
|
||||
|
||||
ColorTransform get_compliant_color_space(const ColorTransform &transform,
|
||||
bool force_display = false);
|
||||
|
||||
static QStringList list_available_colorspaces(ocio::ConstConfigRcPtr config);
|
||||
|
||||
void get_default_luma_coefs(double *rgb) const;
|
||||
|
||||
Project *project() const;
|
||||
|
||||
void update_config_from_filename();
|
||||
|
||||
signals:
|
||||
void config_changed(const QString &s);
|
||||
|
||||
void reference_space_changed(const QString &s);
|
||||
|
||||
void default_input_changed(const QString &s);
|
||||
|
||||
private:
|
||||
ocio::ConstConfigRcPtr config_;
|
||||
|
||||
static ocio::ConstConfigRcPtr default_config;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_COLORSERVICE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/color/displaytransform/displaytransform.cpp
|
||||
node/color/displaytransform/displaytransform.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,157 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "displaytransform.h"
|
||||
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString DisplayTransformNode::k_display_input =
|
||||
QStringLiteral("display_in");
|
||||
const QString DisplayTransformNode::k_view_input = QStringLiteral("view_in");
|
||||
const QString DisplayTransformNode::k_direction_input = QStringLiteral("dir_in");
|
||||
|
||||
#define super OCIOBaseNode
|
||||
|
||||
DisplayTransformNode::DisplayTransformNode()
|
||||
{
|
||||
add_input(k_display_input, NodeValue::k_combo, 0,
|
||||
InputFlags(k_input_flag_not_keyframable | k_input_flag_not_connectable));
|
||||
|
||||
add_input(k_view_input, NodeValue::k_combo, 0,
|
||||
InputFlags(k_input_flag_not_keyframable | k_input_flag_not_connectable));
|
||||
|
||||
add_input(k_direction_input, NodeValue::k_combo, 0,
|
||||
InputFlags(k_input_flag_not_keyframable | k_input_flag_not_connectable));
|
||||
}
|
||||
|
||||
QString DisplayTransformNode::name() const
|
||||
{
|
||||
return tr("Display Transform");
|
||||
}
|
||||
|
||||
QString DisplayTransformNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.displaytransform");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> DisplayTransformNode::category() const
|
||||
{
|
||||
return { k_category_color };
|
||||
}
|
||||
|
||||
QString DisplayTransformNode::description() const
|
||||
{
|
||||
return tr("Converts an image to or from a display color space.");
|
||||
}
|
||||
|
||||
void DisplayTransformNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_display_input, tr("Display"));
|
||||
set_input_name(k_view_input, tr("View"));
|
||||
set_input_name(k_direction_input, tr("Direction"));
|
||||
set_combo_box_strings(k_direction_input, { tr("Forward"), tr("Inverse") });
|
||||
}
|
||||
|
||||
void DisplayTransformNode::InputValueChangedEvent(const QString &input,
|
||||
int element)
|
||||
{
|
||||
Q_UNUSED(element);
|
||||
if (input == k_display_input || input == k_direction_input ||
|
||||
input == k_view_input) {
|
||||
if (input == k_display_input) {
|
||||
update_views();
|
||||
}
|
||||
generate_processor();
|
||||
}
|
||||
}
|
||||
|
||||
QString DisplayTransformNode::get_display() const
|
||||
{
|
||||
if (manager()) {
|
||||
int index = get_standard_value(k_display_input).toInt();
|
||||
if (index < manager()->list_available_displays().size()) {
|
||||
return manager()->list_available_displays().at(index);
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString DisplayTransformNode::get_view() const
|
||||
{
|
||||
if (manager()) {
|
||||
QString display = get_display();
|
||||
if (!display.isEmpty()) {
|
||||
int index = get_standard_value(k_view_input).toInt();
|
||||
QStringList views = manager()->list_available_views(display);
|
||||
if (index < views.size()) {
|
||||
return views.at(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
ColorProcessor::Direction DisplayTransformNode::get_direction() const
|
||||
{
|
||||
return static_cast<ColorProcessor::Direction>(
|
||||
get_standard_value(k_direction_input).toInt());
|
||||
;
|
||||
}
|
||||
|
||||
void DisplayTransformNode::update_displays()
|
||||
{
|
||||
if (manager()) {
|
||||
set_combo_box_strings(k_display_input, manager()->list_available_displays());
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayTransformNode::update_views()
|
||||
{
|
||||
if (manager()) {
|
||||
set_combo_box_strings(k_view_input,
|
||||
manager()->list_available_views(get_display()));
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayTransformNode::config_changed()
|
||||
{
|
||||
update_displays();
|
||||
update_views();
|
||||
generate_processor();
|
||||
}
|
||||
|
||||
void DisplayTransformNode::generate_processor()
|
||||
{
|
||||
if (manager()) {
|
||||
ColorTransform transform(get_display(), get_view(), QString());
|
||||
set_processor(ColorProcessor::create(
|
||||
manager(), manager()->get_reference_color_space(), transform,
|
||||
get_direction()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_DISPLAYTRANSFORMNODE_H
|
||||
#define OAK_DISPLAYTRANSFORMNODE_H
|
||||
|
||||
#include "node/color/ociobase/ociobase.h"
|
||||
#include "render/colorprocessor.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class DisplayTransformNode : public OCIOBaseNode {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DisplayTransformNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(DisplayTransformNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
virtual void InputValueChangedEvent(const QString &input,
|
||||
int element) override;
|
||||
|
||||
QString get_display() const;
|
||||
QString get_view() const;
|
||||
ColorProcessor::Direction get_direction() const;
|
||||
|
||||
static const QString k_display_input;
|
||||
static const QString k_view_input;
|
||||
static const QString k_direction_input;
|
||||
|
||||
protected slots:
|
||||
virtual void config_changed() override;
|
||||
|
||||
private:
|
||||
void generate_processor();
|
||||
|
||||
void update_displays();
|
||||
|
||||
void update_views();
|
||||
};
|
||||
|
||||
} // olive
|
||||
|
||||
#endif // OAK_DISPLAYTRANSFORMNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/color/ociobase/ociobase.cpp
|
||||
node/color/ociobase/ociobase.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,82 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "ociobase.h"
|
||||
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "node/project.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString OCIOBaseNode::k_texture_input = QStringLiteral("tex_in");
|
||||
|
||||
OCIOBaseNode::OCIOBaseNode()
|
||||
: manager_(nullptr)
|
||||
, processor_(nullptr)
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
set_effect_input(k_texture_input);
|
||||
|
||||
set_flag(k_video_effect);
|
||||
}
|
||||
|
||||
void OCIOBaseNode::AddedToGraphEvent(Project *p)
|
||||
{
|
||||
manager_ = p->color_manager();
|
||||
connect(manager_, &ColorManager::config_changed, this,
|
||||
&OCIOBaseNode::config_changed);
|
||||
config_changed();
|
||||
}
|
||||
|
||||
void OCIOBaseNode::RemovedFromGraphEvent(Project *p)
|
||||
{
|
||||
if (manager_) {
|
||||
disconnect(manager_, &ColorManager::config_changed, this,
|
||||
&OCIOBaseNode::config_changed);
|
||||
manager_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOBaseNode::value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
auto tex_met = value[k_texture_input];
|
||||
TexturePtr t = tex_met.to_texture();
|
||||
if (t) {
|
||||
if (processor_) {
|
||||
ColorTransformJob job;
|
||||
|
||||
job.set_color_processor(processor_);
|
||||
job.set_input_texture(tex_met);
|
||||
|
||||
table->push(NodeValue::k_texture, t->to_job(job), this);
|
||||
} else {
|
||||
// Processor isn't ready yet (e.g. still being generated
|
||||
// asynchronously), pass the input through unchanged.
|
||||
table->push(NodeValue::k_texture, QVariant::fromValue(t), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_OCIOBASENODE_H
|
||||
#define OAK_OCIOBASENODE_H
|
||||
|
||||
#include "node/node.h"
|
||||
#include "render/job/colortransformjob.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class OCIOBaseNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OCIOBaseNode();
|
||||
|
||||
virtual void AddedToGraphEvent(Project *p) override;
|
||||
virtual void RemovedFromGraphEvent(Project *p) override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
|
||||
protected slots:
|
||||
virtual void config_changed() = 0;
|
||||
|
||||
protected:
|
||||
ColorManager *manager() const
|
||||
{
|
||||
return manager_;
|
||||
}
|
||||
|
||||
ColorProcessorPtr processor() const
|
||||
{
|
||||
return processor_;
|
||||
}
|
||||
void set_processor(ColorProcessorPtr p)
|
||||
{
|
||||
processor_ = p;
|
||||
}
|
||||
|
||||
private:
|
||||
ColorManager *manager_;
|
||||
|
||||
ColorProcessorPtr processor_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_OCIOBASENODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp
|
||||
node/color/ociogradingtransformlinear/ociogradingtransformlinear.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,311 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "ociogradingtransformlinear.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "common/ocioutils.h"
|
||||
#include "node/project.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString OCIOGradingTransformLinearNode::k_contrast_input =
|
||||
QStringLiteral("ocio_grading_primary_contrast");
|
||||
const QString OCIOGradingTransformLinearNode::k_offset_input =
|
||||
QStringLiteral("ocio_grading_primary_offset");
|
||||
const QString OCIOGradingTransformLinearNode::k_exposure_input =
|
||||
QStringLiteral("ocio_grading_primary_exposure");
|
||||
const QString OCIOGradingTransformLinearNode::k_saturation_input =
|
||||
QStringLiteral("ocio_grading_primary_saturation");
|
||||
const QString OCIOGradingTransformLinearNode::k_pivot_input =
|
||||
QStringLiteral("ocio_grading_primary_pivot");
|
||||
const QString OCIOGradingTransformLinearNode::k_clamp_black_enable_input =
|
||||
QStringLiteral("clamp_black_enable_in");
|
||||
const QString OCIOGradingTransformLinearNode::k_clamp_black_input =
|
||||
QStringLiteral("ocio_grading_primary_clampBlack");
|
||||
const QString OCIOGradingTransformLinearNode::k_clamp_white_enable_input =
|
||||
QStringLiteral("clamp_white_enable_in");
|
||||
const QString OCIOGradingTransformLinearNode::k_clamp_white_input =
|
||||
QStringLiteral("ocio_grading_primary_clampWhite");
|
||||
|
||||
#define super OCIOBaseNode
|
||||
|
||||
OCIOGradingTransformLinearNode::OCIOGradingTransformLinearNode()
|
||||
{
|
||||
add_input(k_contrast_input, NodeValue::k_vec4, QVector4D{ 1.0, 1.0, 1.0, 1.0 });
|
||||
// Minimum based on ocio::GradingPrimary::validate
|
||||
set_input_property(k_contrast_input, QStringLiteral("min"),
|
||||
QVector4D{ 0.01f, 0.01f, 0.01f, 0.01f });
|
||||
set_input_property(k_contrast_input, QStringLiteral("base"), 0.01);
|
||||
set_vec4_input_colors(k_contrast_input);
|
||||
|
||||
add_input(k_offset_input, NodeValue::k_vec4, QVector4D{ 0.0, 0.0, 0.0, 0.0 });
|
||||
set_input_property(k_offset_input, QStringLiteral("base"), 0.01);
|
||||
set_vec4_input_colors(k_offset_input);
|
||||
|
||||
add_input(k_exposure_input, NodeValue::k_vec4, QVector4D{ 0.0, 0.0, 0.0, 0.0 });
|
||||
set_input_property(k_exposure_input, QStringLiteral("base"), 0.01);
|
||||
set_vec4_input_colors(k_exposure_input);
|
||||
|
||||
add_input(k_saturation_input, NodeValue::k_float, 1.0);
|
||||
set_input_property(k_saturation_input, QStringLiteral("view"),
|
||||
slider::k_percentage);
|
||||
set_input_property(k_saturation_input, QStringLiteral("min"), 0.0);
|
||||
|
||||
add_input(k_pivot_input, NodeValue::k_float,
|
||||
0.18); // Default listed in ocio::GradingPrimary
|
||||
set_input_property(k_pivot_input, QStringLiteral("base"), 0.01);
|
||||
|
||||
add_input(k_clamp_black_enable_input, NodeValue::k_boolean, false);
|
||||
|
||||
add_input(k_clamp_black_input, NodeValue::k_float, 0.0);
|
||||
set_input_property(k_clamp_black_input, QStringLiteral("enabled"),
|
||||
get_standard_value(k_clamp_black_enable_input).toBool());
|
||||
set_input_property(k_clamp_black_input, QStringLiteral("base"), 0.01);
|
||||
|
||||
add_input(k_clamp_white_enable_input, NodeValue::k_boolean, false);
|
||||
|
||||
add_input(k_clamp_white_input, NodeValue::k_float, 1.0);
|
||||
set_input_property(k_clamp_white_input, QStringLiteral("enabled"),
|
||||
get_standard_value(k_clamp_white_enable_input).toBool());
|
||||
set_input_property(k_clamp_white_input, QStringLiteral("base"), 0.01);
|
||||
|
||||
// Constrain the white clamp minimum to just above the (static) black clamp
|
||||
// as per ocio::GradingPrimary::validate. When the black clamp is keyframed
|
||||
// or connected, Value() enforces the invariant per frame instead.
|
||||
update_clamp_white_minimum();
|
||||
}
|
||||
|
||||
QString OCIOGradingTransformLinearNode::name() const
|
||||
{
|
||||
return tr("OCIO Color Grading (Linear)");
|
||||
}
|
||||
|
||||
QString OCIOGradingTransformLinearNode::id() const
|
||||
{
|
||||
return QStringLiteral(
|
||||
"org.olivevideoeditor.Olive.ociogradingtransformlinear");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> OCIOGradingTransformLinearNode::category() const
|
||||
{
|
||||
return { k_category_color };
|
||||
}
|
||||
|
||||
QString OCIOGradingTransformLinearNode::description() const
|
||||
{
|
||||
return tr("Simple linear color grading using OpenColorIO.");
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_contrast_input, tr("Contrast"));
|
||||
set_input_name(k_offset_input, tr("Offset"));
|
||||
set_input_name(k_exposure_input, tr("Exposure"));
|
||||
set_input_property(k_exposure_input, QStringLiteral("tooltip"),
|
||||
tr("Exposure increments in stops."));
|
||||
set_input_name(k_saturation_input, tr("Saturation"));
|
||||
set_input_name(k_pivot_input, tr("Pivot"));
|
||||
set_input_name(k_clamp_black_enable_input, tr("Enable Black Clamp"));
|
||||
set_input_name(k_clamp_black_input, tr("Black Clamp"));
|
||||
set_input_name(k_clamp_white_enable_input, tr("Enable White Clamp"));
|
||||
set_input_name(k_clamp_white_input, tr("White Clamp"));
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::InputValueChangedEvent(
|
||||
const QString &input, int element)
|
||||
{
|
||||
Q_UNUSED(element);
|
||||
|
||||
if (input == k_clamp_white_enable_input) {
|
||||
set_input_property(k_clamp_white_input, QStringLiteral("enabled"),
|
||||
get_standard_value(k_clamp_white_enable_input).toBool());
|
||||
} else if (input == k_clamp_black_enable_input) {
|
||||
set_input_property(k_clamp_black_input, QStringLiteral("enabled"),
|
||||
get_standard_value(k_clamp_black_enable_input).toBool());
|
||||
} else if (input == k_clamp_black_input) {
|
||||
// Ensure the white clamp is always greater than the black clamp as per
|
||||
// ocio::GradingPrimary::validate
|
||||
update_clamp_white_minimum();
|
||||
}
|
||||
|
||||
generate_processor();
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::InputConnectedEvent(const QString &input,
|
||||
int element, Node *output)
|
||||
{
|
||||
super::InputConnectedEvent(input, element, output);
|
||||
|
||||
if (input == k_clamp_black_input) {
|
||||
update_clamp_white_minimum();
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::InputDisconnectedEvent(const QString &input,
|
||||
int element,
|
||||
Node *output)
|
||||
{
|
||||
super::InputDisconnectedEvent(input, element, output);
|
||||
|
||||
if (input == k_clamp_black_input) {
|
||||
update_clamp_white_minimum();
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::update_clamp_white_minimum()
|
||||
{
|
||||
// A static UI minimum cannot follow an animated black clamp; for keyframed
|
||||
// or connected values the white>black invariant is enforced per frame in
|
||||
// Value() instead
|
||||
if (is_input_keyframing(k_clamp_black_input) ||
|
||||
is_input_connected(k_clamp_black_input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_input_property(k_clamp_white_input, QStringLiteral("min"),
|
||||
get_standard_value(k_clamp_black_input).toDouble() + 0.000001);
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::generate_processor()
|
||||
{
|
||||
if (manager()) {
|
||||
ocio::GradingPrimaryTransformRcPtr gp =
|
||||
ocio::GradingPrimaryTransform::Create(ocio::GRADING_LIN);
|
||||
gp->makeDynamic();
|
||||
gp->setDirection(ocio::TransformDirection::TRANSFORM_DIR_FORWARD);
|
||||
|
||||
try {
|
||||
set_processor(ColorProcessor::create(
|
||||
manager()->get_config()->getProcessor(gp)));
|
||||
} catch (const ocio::Exception &e) {
|
||||
std::cerr << std::endl << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
if (processor()) {
|
||||
ColorTransformJob job(value);
|
||||
|
||||
job.set_color_processor(processor());
|
||||
job.set_input_texture(value[k_texture_input]);
|
||||
|
||||
const int master_channel = 0;
|
||||
const int red_channel = 1;
|
||||
const int green_channel = 2;
|
||||
const int blue_channel = 3;
|
||||
|
||||
// Oddly, OCIO uses RGBMs when setting the GradingPrimary on the CPU, but uses vec3s on the GPU.
|
||||
// Even more oddly, the conversion from RGBM to vec3 does not appear to have a public API.
|
||||
// Therefore, this code has been duplicated from OCIO here:
|
||||
// https://github.com/AcademySoftwareFoundation/OpenColorIO/blob/3abbe5b20521169580fcfe3692aca81859859953/src/OpenColorIO/ops/gradingprimary/GradingPrimary.cpp#L157
|
||||
QVector4D offset = value[k_offset_input].to_vec4();
|
||||
offset[red_channel] += offset[master_channel];
|
||||
offset[green_channel] += offset[master_channel];
|
||||
offset[blue_channel] += offset[master_channel];
|
||||
job.insert(k_offset_input,
|
||||
NodeValue(NodeValue::k_vec3,
|
||||
QVector3D(offset[red_channel],
|
||||
offset[green_channel],
|
||||
offset[blue_channel])));
|
||||
|
||||
QVector4D exposure = value[k_exposure_input].to_vec4();
|
||||
exposure[red_channel] = std::pow(2.0f, exposure[master_channel] +
|
||||
exposure[red_channel]);
|
||||
exposure[green_channel] = std::pow(
|
||||
2.0f, exposure[master_channel] + exposure[green_channel]);
|
||||
exposure[blue_channel] = std::pow(2.0f, exposure[master_channel] +
|
||||
exposure[blue_channel]);
|
||||
job.insert(k_exposure_input,
|
||||
NodeValue(NodeValue::k_vec3,
|
||||
QVector3D(exposure[red_channel],
|
||||
exposure[green_channel],
|
||||
exposure[blue_channel])));
|
||||
|
||||
QVector4D contrast = value[k_contrast_input].to_vec4();
|
||||
contrast[red_channel] *= contrast[master_channel];
|
||||
contrast[green_channel] *= contrast[master_channel];
|
||||
contrast[blue_channel] *= contrast[master_channel];
|
||||
job.insert(k_contrast_input,
|
||||
NodeValue(NodeValue::k_vec3,
|
||||
QVector3D(contrast[red_channel],
|
||||
contrast[green_channel],
|
||||
contrast[blue_channel])));
|
||||
|
||||
if (!value[k_clamp_black_enable_input].to_bool()) {
|
||||
job.insert(k_clamp_black_input,
|
||||
NodeValue(NodeValue::k_float,
|
||||
ocio::GradingPrimary::NoClampBlack()));
|
||||
}
|
||||
|
||||
if (!value[k_clamp_white_enable_input].to_bool()) {
|
||||
job.insert(k_clamp_white_input,
|
||||
NodeValue(NodeValue::k_float,
|
||||
ocio::GradingPrimary::NoClampWhite()));
|
||||
}
|
||||
|
||||
if (value[k_clamp_black_enable_input].to_bool() &&
|
||||
value[k_clamp_white_enable_input].to_bool()) {
|
||||
// ocio::GradingPrimary::validate requires the white clamp to be
|
||||
// greater than the black clamp. Keyframed or connected values
|
||||
// can violate this at arbitrary times, so enforce the invariant
|
||||
// per frame here.
|
||||
const double clamp_black = value[k_clamp_black_input].to_double();
|
||||
const double clamp_white = value[k_clamp_white_input].to_double();
|
||||
if (clamp_white <= clamp_black) {
|
||||
job.insert(k_clamp_white_input,
|
||||
NodeValue(NodeValue::k_float,
|
||||
clamp_black + 0.000001));
|
||||
}
|
||||
}
|
||||
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::config_changed()
|
||||
{
|
||||
generate_processor();
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::set_vec4_input_colors(const QString &input)
|
||||
{
|
||||
set_input_property(input, QStringLiteral("color0"),
|
||||
QColor(192, 192, 192).name());
|
||||
set_input_property(input, QStringLiteral("color1"), QColor(255, 0, 0).name());
|
||||
set_input_property(input, QStringLiteral("color2"), QColor(0, 255, 0).name());
|
||||
set_input_property(input, QStringLiteral("color3"), QColor(0, 0, 255).name());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_OCIOGRADINGTRANSFORMLINEARNODE_H
|
||||
#define OAK_OCIOGRADINGTRANSFORMLINEARNODE_H
|
||||
|
||||
#include "node/color/ociobase/ociobase.h"
|
||||
#include "render/colorprocessor.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class OCIOGradingTransformLinearNode : public OCIOBaseNode {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OCIOGradingTransformLinearNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(OCIOGradingTransformLinearNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
virtual void InputValueChangedEvent(const QString &input,
|
||||
int element) override;
|
||||
virtual void InputConnectedEvent(const QString &input, int element,
|
||||
Node *output) override;
|
||||
virtual void InputDisconnectedEvent(const QString &input, int element,
|
||||
Node *output) override;
|
||||
void generate_processor();
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
static const QString k_contrast_input;
|
||||
static const QString k_offset_input;
|
||||
static const QString k_exposure_input;
|
||||
static const QString k_saturation_input;
|
||||
static const QString k_pivot_input;
|
||||
static const QString k_clamp_black_enable_input;
|
||||
static const QString k_clamp_black_input;
|
||||
static const QString k_clamp_white_enable_input;
|
||||
static const QString k_clamp_white_input;
|
||||
|
||||
protected slots:
|
||||
virtual void config_changed() override;
|
||||
|
||||
private:
|
||||
void set_vec4_input_colors(const QString &input);
|
||||
|
||||
/**
|
||||
* @brief Constrains the white clamp UI minimum to just above the black
|
||||
* clamp, as required by ocio::GradingPrimary::validate
|
||||
*
|
||||
* Only applies while the black clamp is a static value; when it is
|
||||
* keyframed or connected the invariant is enforced per frame in Value()
|
||||
* instead.
|
||||
*/
|
||||
void update_clamp_white_minimum();
|
||||
};
|
||||
|
||||
} // olive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
# Modifications Copyright (C) 2026 Oak Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/color/ociogradingtransformlog/ociogradingtransformlog.cpp
|
||||
node/color/ociogradingtransformlog/ociogradingtransformlog.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,302 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "ociogradingtransformlog.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "common/ocioutils.h"
|
||||
#include "node/project.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
// These ids double as the OCIO GPU uniform names for the dynamic
|
||||
// GradingPrimaryTransform; do not rename them. OCIO's log style maps the
|
||||
// classic wheels as: brightness = lift, contrast = gain, gamma = gamma.
|
||||
const QString OCIOGradingTransformLogNode::k_lift_input =
|
||||
QStringLiteral("ocio_grading_primary_brightness");
|
||||
const QString OCIOGradingTransformLogNode::k_gain_input =
|
||||
QStringLiteral("ocio_grading_primary_contrast");
|
||||
const QString OCIOGradingTransformLogNode::k_gamma_input =
|
||||
QStringLiteral("ocio_grading_primary_gamma");
|
||||
const QString OCIOGradingTransformLogNode::k_saturation_input =
|
||||
QStringLiteral("ocio_grading_primary_saturation");
|
||||
const QString OCIOGradingTransformLogNode::k_pivot_input =
|
||||
QStringLiteral("ocio_grading_primary_pivot");
|
||||
const QString OCIOGradingTransformLogNode::k_clamp_black_enable_input =
|
||||
QStringLiteral("clamp_black_enable_in");
|
||||
const QString OCIOGradingTransformLogNode::k_clamp_black_input =
|
||||
QStringLiteral("ocio_grading_primary_clampBlack");
|
||||
const QString OCIOGradingTransformLogNode::k_clamp_white_enable_input =
|
||||
QStringLiteral("clamp_white_enable_in");
|
||||
const QString OCIOGradingTransformLogNode::k_clamp_white_input =
|
||||
QStringLiteral("ocio_grading_primary_clampWhite");
|
||||
|
||||
#define super OCIOBaseNode
|
||||
|
||||
OCIOGradingTransformLogNode::OCIOGradingTransformLogNode()
|
||||
{
|
||||
add_input(k_lift_input, NodeValue::k_vec4, QVector4D{ 0.0, 0.0, 0.0, 0.0 });
|
||||
set_input_property(k_lift_input, QStringLiteral("base"), 0.01);
|
||||
set_vec4_input_colors(k_lift_input);
|
||||
|
||||
add_input(k_gain_input, NodeValue::k_vec4, QVector4D{ 1.0, 1.0, 1.0, 1.0 });
|
||||
set_input_property(k_gain_input, QStringLiteral("base"), 0.01);
|
||||
set_vec4_input_colors(k_gain_input);
|
||||
|
||||
add_input(k_gamma_input, NodeValue::k_vec4, QVector4D{ 1.0, 1.0, 1.0, 1.0 });
|
||||
set_input_property(k_gamma_input, QStringLiteral("base"), 0.01);
|
||||
set_vec4_input_colors(k_gamma_input);
|
||||
|
||||
add_input(k_saturation_input, NodeValue::k_float, 1.0);
|
||||
set_input_property(k_saturation_input, QStringLiteral("view"),
|
||||
slider::k_percentage);
|
||||
set_input_property(k_saturation_input, QStringLiteral("min"), 0.0);
|
||||
|
||||
add_input(k_pivot_input, NodeValue::k_float,
|
||||
-0.2); // Default for GRADING_LOG listed in ocio::GradingPrimary
|
||||
set_input_property(k_pivot_input, QStringLiteral("base"), 0.01);
|
||||
|
||||
add_input(k_clamp_black_enable_input, NodeValue::k_boolean, false);
|
||||
|
||||
add_input(k_clamp_black_input, NodeValue::k_float, 0.0);
|
||||
set_input_property(k_clamp_black_input, QStringLiteral("enabled"),
|
||||
get_standard_value(k_clamp_black_enable_input).toBool());
|
||||
set_input_property(k_clamp_black_input, QStringLiteral("base"), 0.01);
|
||||
|
||||
add_input(k_clamp_white_enable_input, NodeValue::k_boolean, false);
|
||||
|
||||
add_input(k_clamp_white_input, NodeValue::k_float, 1.0);
|
||||
set_input_property(k_clamp_white_input, QStringLiteral("enabled"),
|
||||
get_standard_value(k_clamp_white_enable_input).toBool());
|
||||
set_input_property(k_clamp_white_input, QStringLiteral("base"), 0.01);
|
||||
|
||||
// Constrain the white clamp minimum to just above the (static) black clamp
|
||||
// as per ocio::GradingPrimary::validate. When the black clamp is keyframed
|
||||
// or connected, Value() enforces the invariant per frame instead.
|
||||
update_clamp_white_minimum();
|
||||
}
|
||||
|
||||
QString OCIOGradingTransformLogNode::name() const
|
||||
{
|
||||
return tr("OCIO Color Grading (Log)");
|
||||
}
|
||||
|
||||
QString OCIOGradingTransformLogNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.ociogradingtransformlog");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> OCIOGradingTransformLogNode::category() const
|
||||
{
|
||||
return { k_category_color };
|
||||
}
|
||||
|
||||
QString OCIOGradingTransformLogNode::description() const
|
||||
{
|
||||
return tr("Lift/gamma/gain color grading using OpenColorIO.");
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLogNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_lift_input, tr("Lift"));
|
||||
set_input_name(k_gain_input, tr("Gain"));
|
||||
set_input_name(k_gamma_input, tr("Gamma"));
|
||||
set_input_name(k_saturation_input, tr("Saturation"));
|
||||
set_input_name(k_pivot_input, tr("Pivot"));
|
||||
set_input_name(k_clamp_black_enable_input, tr("Enable Black Clamp"));
|
||||
set_input_name(k_clamp_black_input, tr("Black Clamp"));
|
||||
set_input_name(k_clamp_white_enable_input, tr("Enable White Clamp"));
|
||||
set_input_name(k_clamp_white_input, tr("White Clamp"));
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLogNode::InputValueChangedEvent(const QString &input,
|
||||
int element)
|
||||
{
|
||||
Q_UNUSED(element);
|
||||
|
||||
if (input == k_clamp_white_enable_input) {
|
||||
set_input_property(k_clamp_white_input, QStringLiteral("enabled"),
|
||||
get_standard_value(k_clamp_white_enable_input).toBool());
|
||||
} else if (input == k_clamp_black_enable_input) {
|
||||
set_input_property(k_clamp_black_input, QStringLiteral("enabled"),
|
||||
get_standard_value(k_clamp_black_enable_input).toBool());
|
||||
} else if (input == k_clamp_black_input) {
|
||||
// Ensure the white clamp is always greater than the black clamp as per
|
||||
// ocio::GradingPrimary::validate
|
||||
update_clamp_white_minimum();
|
||||
}
|
||||
|
||||
generate_processor();
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLogNode::InputConnectedEvent(const QString &input,
|
||||
int element, Node *output)
|
||||
{
|
||||
super::InputConnectedEvent(input, element, output);
|
||||
|
||||
if (input == k_clamp_black_input) {
|
||||
update_clamp_white_minimum();
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLogNode::InputDisconnectedEvent(const QString &input,
|
||||
int element,
|
||||
Node *output)
|
||||
{
|
||||
super::InputDisconnectedEvent(input, element, output);
|
||||
|
||||
if (input == k_clamp_black_input) {
|
||||
update_clamp_white_minimum();
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLogNode::update_clamp_white_minimum()
|
||||
{
|
||||
// A static UI minimum cannot follow an animated black clamp; for keyframed
|
||||
// or connected values the white>black invariant is enforced per frame in
|
||||
// Value() instead
|
||||
if (is_input_keyframing(k_clamp_black_input) ||
|
||||
is_input_connected(k_clamp_black_input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_input_property(k_clamp_white_input, QStringLiteral("min"),
|
||||
get_standard_value(k_clamp_black_input).toDouble() + 0.000001);
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLogNode::generate_processor()
|
||||
{
|
||||
if (manager()) {
|
||||
ocio::GradingPrimaryTransformRcPtr gp =
|
||||
ocio::GradingPrimaryTransform::Create(ocio::GRADING_LOG);
|
||||
gp->makeDynamic();
|
||||
gp->setDirection(ocio::TransformDirection::TRANSFORM_DIR_FORWARD);
|
||||
|
||||
try {
|
||||
set_processor(ColorProcessor::create(
|
||||
manager()->get_config()->getProcessor(gp)));
|
||||
} catch (const ocio::Exception &e) {
|
||||
std::cerr << std::endl << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLogNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
if (processor()) {
|
||||
ColorTransformJob job(value);
|
||||
|
||||
job.set_color_processor(processor());
|
||||
job.set_input_texture(value[k_texture_input]);
|
||||
|
||||
const int master_channel = 0;
|
||||
const int red_channel = 1;
|
||||
const int green_channel = 2;
|
||||
const int blue_channel = 3;
|
||||
|
||||
// OCIO expects vec3s on the GPU but RGBMs (master + RGB) on the
|
||||
// CPU; the per-style master combination below mirrors
|
||||
// ocio::GradingPrimary. Lift is additive, gain/gamma multiply.
|
||||
QVector4D lift = value[k_lift_input].to_vec4();
|
||||
lift[red_channel] += lift[master_channel];
|
||||
lift[green_channel] += lift[master_channel];
|
||||
lift[blue_channel] += lift[master_channel];
|
||||
job.insert(k_lift_input,
|
||||
NodeValue(NodeValue::k_vec3,
|
||||
QVector3D(lift[red_channel], lift[green_channel],
|
||||
lift[blue_channel])));
|
||||
|
||||
QVector4D gain = value[k_gain_input].to_vec4();
|
||||
gain[red_channel] *= gain[master_channel];
|
||||
gain[green_channel] *= gain[master_channel];
|
||||
gain[blue_channel] *= gain[master_channel];
|
||||
job.insert(k_gain_input,
|
||||
NodeValue(NodeValue::k_vec3,
|
||||
QVector3D(gain[red_channel], gain[green_channel],
|
||||
gain[blue_channel])));
|
||||
|
||||
QVector4D gamma = value[k_gamma_input].to_vec4();
|
||||
gamma[red_channel] *= gamma[master_channel];
|
||||
gamma[green_channel] *= gamma[master_channel];
|
||||
gamma[blue_channel] *= gamma[master_channel];
|
||||
job.insert(k_gamma_input,
|
||||
NodeValue(NodeValue::k_vec3,
|
||||
QVector3D(gamma[red_channel],
|
||||
gamma[green_channel],
|
||||
gamma[blue_channel])));
|
||||
|
||||
if (!value[k_clamp_black_enable_input].to_bool()) {
|
||||
job.insert(k_clamp_black_input,
|
||||
NodeValue(NodeValue::k_float,
|
||||
ocio::GradingPrimary::NoClampBlack()));
|
||||
}
|
||||
|
||||
if (!value[k_clamp_white_enable_input].to_bool()) {
|
||||
job.insert(k_clamp_white_input,
|
||||
NodeValue(NodeValue::k_float,
|
||||
ocio::GradingPrimary::NoClampWhite()));
|
||||
}
|
||||
|
||||
if (value[k_clamp_black_enable_input].to_bool() &&
|
||||
value[k_clamp_white_enable_input].to_bool()) {
|
||||
// ocio::GradingPrimary::validate requires the white clamp to be
|
||||
// greater than the black clamp. Keyframed or connected values
|
||||
// can violate this at arbitrary times, so enforce the invariant
|
||||
// per frame here.
|
||||
const double clamp_black = value[k_clamp_black_input].to_double();
|
||||
const double clamp_white = value[k_clamp_white_input].to_double();
|
||||
if (clamp_white <= clamp_black) {
|
||||
job.insert(k_clamp_white_input,
|
||||
NodeValue(NodeValue::k_float,
|
||||
clamp_black + 0.000001));
|
||||
}
|
||||
}
|
||||
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLogNode::config_changed()
|
||||
{
|
||||
generate_processor();
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLogNode::set_vec4_input_colors(const QString &input)
|
||||
{
|
||||
set_input_property(input, QStringLiteral("color0"),
|
||||
QColor(192, 192, 192).name());
|
||||
set_input_property(input, QStringLiteral("color1"), QColor(255, 0, 0).name());
|
||||
set_input_property(input, QStringLiteral("color2"), QColor(0, 255, 0).name());
|
||||
set_input_property(input, QStringLiteral("color3"), QColor(0, 0, 255).name());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_OCIOGRADINGTRANSFORMLOGNODE_H
|
||||
#define OAK_OCIOGRADINGTRANSFORMLOGNODE_H
|
||||
|
||||
#include "node/color/ociobase/ociobase.h"
|
||||
#include "render/colorprocessor.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Lift/gamma/gain grading node built on ocio::GRADING_LOG
|
||||
*
|
||||
* Mirrors OCIOGradingTransformLinearNode for the log grading style. OCIO's
|
||||
* log-style GPU uniforms map to the classic wheels as: brightness = lift,
|
||||
* contrast = gain, gamma = gamma.
|
||||
*/
|
||||
class OCIOGradingTransformLogNode : public OCIOBaseNode {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OCIOGradingTransformLogNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(OCIOGradingTransformLogNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
virtual void InputValueChangedEvent(const QString &input,
|
||||
int element) override;
|
||||
virtual void InputConnectedEvent(const QString &input, int element,
|
||||
Node *output) override;
|
||||
virtual void InputDisconnectedEvent(const QString &input, int element,
|
||||
Node *output) override;
|
||||
void generate_processor();
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
static const QString k_lift_input;
|
||||
static const QString k_gain_input;
|
||||
static const QString k_gamma_input;
|
||||
static const QString k_saturation_input;
|
||||
static const QString k_pivot_input;
|
||||
static const QString k_clamp_black_enable_input;
|
||||
static const QString k_clamp_black_input;
|
||||
static const QString k_clamp_white_enable_input;
|
||||
static const QString k_clamp_white_input;
|
||||
|
||||
protected slots:
|
||||
virtual void config_changed() override;
|
||||
|
||||
private:
|
||||
void set_vec4_input_colors(const QString &input);
|
||||
|
||||
/**
|
||||
* @brief Constrains the white clamp UI minimum to just above the black
|
||||
* clamp, as required by ocio::GradingPrimary::validate
|
||||
*
|
||||
* Only applies while the black clamp is a static value; when it is
|
||||
* keyframed or connected the invariant is enforced per frame in Value()
|
||||
* instead.
|
||||
*/
|
||||
void update_clamp_white_minimum();
|
||||
};
|
||||
|
||||
} // olive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2026 Oak 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.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/color/ociolut/ociolut.cpp
|
||||
node/color/ociolut/ociolut.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,304 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "ociolut.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFileInfo>
|
||||
#include <QMetaObject>
|
||||
|
||||
|
||||
#include "coreengine.h"
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "render/lutlibrary.h"
|
||||
#include "render/previewautocacher.h"
|
||||
#include "render/rendermanager.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString OCIOLutNode::k_file_input = QStringLiteral("lut_file_in");
|
||||
const QString OCIOLutNode::k_direction_input = QStringLiteral("lut_dir_in");
|
||||
|
||||
#define super OCIOBaseNode
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool is_main_process()
|
||||
{
|
||||
return qobject_cast<QApplication *>(QCoreApplication::instance()) !=
|
||||
nullptr;
|
||||
}
|
||||
|
||||
int read_direction_input(const Node *node)
|
||||
{
|
||||
QVariant v = node->get_standard_value(OCIOLutNode::k_direction_input);
|
||||
|
||||
bool ok = false;
|
||||
int direction = v.toInt(&ok);
|
||||
if (ok) {
|
||||
return direction;
|
||||
}
|
||||
|
||||
// Some old serializers stored the combo value as a string.
|
||||
const QString s = v.toString().toLower();
|
||||
if (s == QStringLiteral("forward") || s == QStringLiteral("0")) {
|
||||
return 0;
|
||||
}
|
||||
if (s == QStringLiteral("inverse") || s == QStringLiteral("1")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
qWarning() << "OCIOLutNode: unexpected direction value" << v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
OCIOLutNode::OCIOLutNode()
|
||||
{
|
||||
add_input(k_file_input, NodeValue::k_file, QString(),
|
||||
InputFlags(k_input_flag_not_keyframable | k_input_flag_not_connectable));
|
||||
const QString all_luts =
|
||||
QStringLiteral("*.") +
|
||||
LUTLibrary::supported_extensions().join(QStringLiteral(" *."));
|
||||
set_input_property(k_file_input, QStringLiteral("filter"),
|
||||
tr("LUT Files (%1);;All Files (*)").arg(all_luts));
|
||||
set_input_property(k_file_input, QStringLiteral("placeholder"),
|
||||
tr("Select a LUT file"));
|
||||
// Allow the UI to offer the global LUT library for this input
|
||||
set_input_property(k_file_input, QStringLiteral("lut_library"), true);
|
||||
|
||||
add_input(k_direction_input, NodeValue::k_combo, 0,
|
||||
InputFlags(k_input_flag_not_keyframable | k_input_flag_not_connectable));
|
||||
|
||||
qRegisterMetaType<olive::ColorProcessorPtr>();
|
||||
}
|
||||
|
||||
QString OCIOLutNode::name() const
|
||||
{
|
||||
return tr("OCIO LUT");
|
||||
}
|
||||
|
||||
QString OCIOLutNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.ociolut");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> OCIOLutNode::category() const
|
||||
{
|
||||
return { k_category_color };
|
||||
}
|
||||
|
||||
QString OCIOLutNode::description() const
|
||||
{
|
||||
return tr("Applies a LUT file through OpenColorIO.");
|
||||
}
|
||||
|
||||
void OCIOLutNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_file_input, tr("LUT File"));
|
||||
set_input_name(k_direction_input, tr("Direction"));
|
||||
set_combo_box_strings(k_direction_input, { tr("Forward"), tr("Inverse") });
|
||||
}
|
||||
|
||||
void OCIOLutNode::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
if (input == k_file_input || input == k_direction_input) {
|
||||
// In the worker process, creating the OCIO processor can be slow and we
|
||||
// are often called from LoadGraph while the main process is blocked
|
||||
// waiting for a response. Defer generation to Value() time so the worker
|
||||
// can ack the graph load immediately.
|
||||
if (is_main_process()) {
|
||||
generate_processor();
|
||||
} else {
|
||||
QMutexLocker locker(&gen_mutex_);
|
||||
processor_dirty_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOLutNode::config_changed()
|
||||
{
|
||||
if (is_main_process()) {
|
||||
generate_processor();
|
||||
} else {
|
||||
QMutexLocker locker(&gen_mutex_);
|
||||
processor_dirty_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOLutNode::value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// Ensure the processor is up-to-date before the base class emits the color
|
||||
// transform job. This is especially important in the render worker, where
|
||||
// processor creation is deferred until the first render.
|
||||
ensure_processor();
|
||||
|
||||
super::value(value, globals, table);
|
||||
}
|
||||
|
||||
void OCIOLutNode::generate_processor()
|
||||
{
|
||||
ensure_processor();
|
||||
|
||||
// The processor has changed. In the main GUI process, refresh the viewer by
|
||||
// invalidating the cache and cancelling background cache jobs.
|
||||
// Invalidating first ensures any in-flight renders that complete afterwards
|
||||
// won't write stale frames back. The worker process uses QGuiApplication and
|
||||
// has no RenderManager/PreviewAutoCacher, so skip this step to avoid crashing.
|
||||
if (is_main_process()) {
|
||||
invalidate_all(k_texture_input);
|
||||
if (RenderManager *rm = RenderManager::instance()) {
|
||||
if (PreviewAutoCacher *cacher = rm->get_cacher()) {
|
||||
cacher->cancel_video_tasks(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOLutNode::ensure_processor() const
|
||||
{
|
||||
QMutexLocker locker(&gen_mutex_);
|
||||
|
||||
if (!processor_dirty_ && last_processor_ &&
|
||||
get_standard_value(k_file_input).toString() == last_path_ &&
|
||||
read_direction_input(this) == last_direction_) {
|
||||
return;
|
||||
}
|
||||
|
||||
create_processor_from_inputs();
|
||||
}
|
||||
|
||||
void OCIOLutNode::set_last_error(const QString &error) const
|
||||
{
|
||||
if (last_error_ == error) {
|
||||
return;
|
||||
}
|
||||
|
||||
last_error_ = error;
|
||||
|
||||
// Make the error visible to the user instead of failing silently, but only
|
||||
// from the main process (the render worker has no status bar)
|
||||
if (!error.isEmpty() && is_main_process() && EngineCore::instance()) {
|
||||
EngineCore::instance()->show_status_bar_message(error, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
bool OCIOLutNode::create_processor_from_inputs() const
|
||||
{
|
||||
if (!manager()) {
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(nullptr);
|
||||
last_processor_.reset();
|
||||
last_path_.clear();
|
||||
last_direction_ = -1;
|
||||
processor_dirty_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString path = get_standard_value(k_file_input).toString();
|
||||
const int direction = read_direction_input(this);
|
||||
|
||||
if (path.isEmpty()) {
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(nullptr);
|
||||
last_processor_.reset();
|
||||
last_path_.clear();
|
||||
last_direction_ = -1;
|
||||
processor_dirty_ = false;
|
||||
set_last_error(QString());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Re-use the existing processor if the file and direction haven't changed.
|
||||
if (path == last_path_ && direction == last_direction_ && last_processor_) {
|
||||
processor_dirty_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const QFileInfo info(path);
|
||||
if (!info.exists() || !info.isFile()) {
|
||||
qWarning() << "OCIO LUT file does not exist:" << path;
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(nullptr);
|
||||
last_processor_.reset();
|
||||
last_path_.clear();
|
||||
last_direction_ = -1;
|
||||
processor_dirty_ = false;
|
||||
set_last_error(tr("OCIO LUT: file does not exist: %1").arg(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString suffix = info.suffix();
|
||||
if (!LUTLibrary::is_supported_extension(suffix)) {
|
||||
qWarning() << "Unsupported OCIO LUT file extension:" << path;
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(nullptr);
|
||||
last_processor_.reset();
|
||||
last_path_.clear();
|
||||
last_direction_ = -1;
|
||||
processor_dirty_ = false;
|
||||
set_last_error(tr("OCIO LUT: unsupported LUT file extension: %1")
|
||||
.arg(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
ColorProcessorPtr processor;
|
||||
try {
|
||||
const bool forward = static_cast<ColorProcessor::Direction>(
|
||||
direction) == ColorProcessor::k_normal;
|
||||
qDebug() << "OCIOLutNode: creating processor for" << path
|
||||
<< "direction=" << direction
|
||||
<< "ocio_dir=" << (forward ? "FORWARD" : "INVERSE")
|
||||
<< "process=" << (is_main_process() ? "main" : "worker");
|
||||
|
||||
ocio::FileTransformRcPtr transform = ocio::FileTransform::Create();
|
||||
transform->setSrc(path.toUtf8().constData());
|
||||
transform->setInterpolation(ocio::INTERP_LINEAR);
|
||||
transform->setDirection(forward ? ocio::TRANSFORM_DIR_FORWARD :
|
||||
ocio::TRANSFORM_DIR_INVERSE);
|
||||
|
||||
processor = ColorProcessor::create(
|
||||
manager()->get_config()->getProcessor(transform));
|
||||
} catch (const std::exception &e) {
|
||||
qWarning() << "OCIO LUT processor error:" << e.what();
|
||||
processor = nullptr;
|
||||
}
|
||||
|
||||
if (!processor) {
|
||||
set_last_error(tr("OCIO LUT: failed to load LUT file: %1").arg(path));
|
||||
} else {
|
||||
set_last_error(QString());
|
||||
}
|
||||
|
||||
last_path_ = path;
|
||||
last_direction_ = direction;
|
||||
last_processor_ = processor;
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(processor);
|
||||
processor_dirty_ = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
@@ -0,0 +1,85 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_OCIOLUTNODE_H
|
||||
#define OAK_OCIOLUTNODE_H
|
||||
|
||||
#include <QMutex>
|
||||
|
||||
#include "node/color/ociobase/ociobase.h"
|
||||
#include "render/colorprocessor.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class OCIOLutNode : public OCIOBaseNode {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OCIOLutNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(OCIOLutNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
virtual void InputValueChangedEvent(const QString &input,
|
||||
int element) override;
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
static const QString k_file_input;
|
||||
static const QString k_direction_input;
|
||||
|
||||
/**
|
||||
* @brief Human-readable description of why no LUT processor is active
|
||||
*
|
||||
* Empty when a valid LUT processor is in use or no LUT file has been
|
||||
* selected yet. This allows the UI (and tests) to surface silent
|
||||
* passthrough states (missing file, unsupported extension, OCIO errors).
|
||||
*/
|
||||
const QString &last_error() const
|
||||
{
|
||||
return last_error_;
|
||||
}
|
||||
|
||||
protected slots:
|
||||
virtual void config_changed() override;
|
||||
|
||||
private:
|
||||
void generate_processor();
|
||||
void ensure_processor() const;
|
||||
bool create_processor_from_inputs() const;
|
||||
|
||||
void set_last_error(const QString &error) const;
|
||||
|
||||
mutable QMutex gen_mutex_;
|
||||
mutable bool processor_dirty_ = true;
|
||||
mutable QString last_path_;
|
||||
mutable int last_direction_ = -1;
|
||||
mutable ColorProcessorPtr last_processor_;
|
||||
mutable QString last_error_;
|
||||
};
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // OAK_OCIOLUTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/color/threewaycolor/threewaycolor.h
|
||||
node/color/threewaycolor/threewaycolor.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,123 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2026 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "threewaycolor.h"
|
||||
|
||||
#include <QVector3D>
|
||||
|
||||
#include "node/project.h"
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super Node
|
||||
|
||||
const QString ThreeWayColorNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString ThreeWayColorNode::k_shadows_color_input =
|
||||
QStringLiteral("shadows_color_in");
|
||||
const QString ThreeWayColorNode::k_midtones_color_input =
|
||||
QStringLiteral("midtones_color_in");
|
||||
const QString ThreeWayColorNode::k_highlights_color_input =
|
||||
QStringLiteral("highlights_color_in");
|
||||
const QString ThreeWayColorNode::k_shadows_amount_input =
|
||||
QStringLiteral("shadows_amount_in");
|
||||
const QString ThreeWayColorNode::k_midtones_amount_input =
|
||||
QStringLiteral("midtones_amount_in");
|
||||
const QString ThreeWayColorNode::k_highlights_amount_input =
|
||||
QStringLiteral("highlights_amount_in");
|
||||
const QString ThreeWayColorNode::k_luma_coefficients_input =
|
||||
QStringLiteral("luma_coefficients_in");
|
||||
|
||||
ThreeWayColorNode::ThreeWayColorNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
const QVariant neutral = QVariant::fromValue(Color(0.5, 0.5, 0.5, 1.0));
|
||||
add_input(k_shadows_color_input, NodeValue::k_color, neutral);
|
||||
add_input(k_midtones_color_input, NodeValue::k_color, neutral);
|
||||
add_input(k_highlights_color_input, NodeValue::k_color, neutral);
|
||||
|
||||
add_input(k_shadows_amount_input, NodeValue::k_float, 1.0);
|
||||
add_input(k_midtones_amount_input, NodeValue::k_float, 1.0);
|
||||
add_input(k_highlights_amount_input, NodeValue::k_float, 1.0);
|
||||
|
||||
const QString min = QStringLiteral("min");
|
||||
const QString view = QStringLiteral("view");
|
||||
set_input_property(k_shadows_amount_input, min, 0.0);
|
||||
set_input_property(k_midtones_amount_input, min, 0.0);
|
||||
set_input_property(k_highlights_amount_input, min, 0.0);
|
||||
set_input_property(k_shadows_amount_input, view, slider::k_percentage);
|
||||
set_input_property(k_midtones_amount_input, view, slider::k_percentage);
|
||||
set_input_property(k_highlights_amount_input, view, slider::k_percentage);
|
||||
|
||||
set_effect_input(k_texture_input);
|
||||
set_flag(k_video_effect);
|
||||
}
|
||||
|
||||
void ThreeWayColorNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_shadows_color_input, tr("Shadows"));
|
||||
set_input_name(k_midtones_color_input, tr("Midtones"));
|
||||
set_input_name(k_highlights_color_input, tr("Highlights"));
|
||||
set_input_name(k_shadows_amount_input, tr("Shadows Amount"));
|
||||
set_input_name(k_midtones_amount_input, tr("Midtones Amount"));
|
||||
set_input_name(k_highlights_amount_input, tr("Highlights Amount"));
|
||||
}
|
||||
|
||||
ShaderCode ThreeWayColorNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(
|
||||
FileFunctions::read_file_as_string(":/shaders/threewaycolor.frag"));
|
||||
}
|
||||
|
||||
void ThreeWayColorNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
Q_UNUSED(globals)
|
||||
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
ShaderJob job(value);
|
||||
|
||||
double luma_coeffs[3] = { 0.0, 0.0, 0.0 };
|
||||
if (project() && project()->color_manager()) {
|
||||
project()->color_manager()->get_default_luma_coefs(luma_coeffs);
|
||||
} else {
|
||||
luma_coeffs[0] = 0.2126;
|
||||
luma_coeffs[1] = 0.7152;
|
||||
luma_coeffs[2] = 0.0722;
|
||||
}
|
||||
job.insert(k_luma_coefficients_input,
|
||||
NodeValue(NodeValue::k_vec3,
|
||||
QVector3D(luma_coeffs[0], luma_coeffs[1],
|
||||
luma_coeffs[2])));
|
||||
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2026 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_THREEWAYCOLORNODE_H
|
||||
#define OAK_THREEWAYCOLORNODE_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ThreeWayColorNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ThreeWayColorNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(ThreeWayColorNode)
|
||||
|
||||
virtual QString name() const override
|
||||
{
|
||||
return tr("Three-Way Color");
|
||||
}
|
||||
|
||||
virtual QString id() const override
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.threewaycolor");
|
||||
}
|
||||
|
||||
virtual QVector<CategoryID> category() const override
|
||||
{
|
||||
return { k_category_color };
|
||||
}
|
||||
|
||||
virtual QString description() const override
|
||||
{
|
||||
return tr("Adjusts shadows, midtones, and highlights separately.");
|
||||
}
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_shadows_color_input;
|
||||
static const QString k_midtones_color_input;
|
||||
static const QString k_highlights_color_input;
|
||||
static const QString k_shadows_amount_input;
|
||||
static const QString k_midtones_amount_input;
|
||||
static const QString k_highlights_amount_input;
|
||||
static const QString k_luma_coefficients_input;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_THREEWAYCOLORNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Oak - Non-Linear Video Editor
|
||||
# Copyright (C) 2026 Oak Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/color/whitebalance/whitebalance.cpp
|
||||
node/color/whitebalance/whitebalance.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,156 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "whitebalance.h"
|
||||
|
||||
#include <QtMath>
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
#include "render/job/shaderjob.h"
|
||||
#include "render/texture.h"
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super Node
|
||||
|
||||
const QString WhiteBalanceNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString WhiteBalanceNode::k_temperature_input =
|
||||
QStringLiteral("temperature_in");
|
||||
const QString WhiteBalanceNode::k_tint_input = QStringLiteral("tint_in");
|
||||
const QString WhiteBalanceNode::k_gain_input = QStringLiteral("wb_gain_in");
|
||||
|
||||
WhiteBalanceNode::WhiteBalanceNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_temperature_input, NodeValue::k_float, 6500.0);
|
||||
set_input_property(k_temperature_input, QStringLiteral("min"), 1000.0);
|
||||
set_input_property(k_temperature_input, QStringLiteral("max"), 40000.0);
|
||||
set_input_property(k_temperature_input, QStringLiteral("view"),
|
||||
slider::k_normal);
|
||||
|
||||
add_input(k_tint_input, NodeValue::k_float, 0.0);
|
||||
set_input_property(k_tint_input, QStringLiteral("min"), -1.0);
|
||||
set_input_property(k_tint_input, QStringLiteral("max"), 1.0);
|
||||
set_input_property(k_tint_input, QStringLiteral("base"), 0.01);
|
||||
|
||||
set_effect_input(k_texture_input);
|
||||
set_flag(k_video_effect);
|
||||
}
|
||||
|
||||
QString WhiteBalanceNode::name() const
|
||||
{
|
||||
return tr("White Balance");
|
||||
}
|
||||
|
||||
QString WhiteBalanceNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.whitebalance");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> WhiteBalanceNode::category() const
|
||||
{
|
||||
return { k_category_color };
|
||||
}
|
||||
|
||||
QString WhiteBalanceNode::description() const
|
||||
{
|
||||
return tr("Adjust white balance by color temperature and tint.");
|
||||
}
|
||||
|
||||
void WhiteBalanceNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_temperature_input, tr("Temperature (K)"));
|
||||
set_input_name(k_tint_input, tr("Tint"));
|
||||
}
|
||||
|
||||
ShaderCode WhiteBalanceNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(
|
||||
FileFunctions::read_file_as_string(":/shaders/whitebalance.frag"));
|
||||
}
|
||||
|
||||
void WhiteBalanceNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
Q_UNUSED(globals)
|
||||
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
ShaderJob job(value);
|
||||
|
||||
const QVector3D gain = get_gain_for_temperature(
|
||||
value[k_temperature_input].to_double(),
|
||||
value[k_tint_input].to_double());
|
||||
job.insert(k_gain_input, NodeValue(NodeValue::k_vec3, gain));
|
||||
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
QVector3D WhiteBalanceNode::get_gain_for_temperature(double kelvin, double tint)
|
||||
{
|
||||
// Tanner Helland blackbody approximation (1000K-40000K), returning
|
||||
// 0-255 per channel
|
||||
kelvin = qBound(1000.0, kelvin, 40000.0);
|
||||
const double t = kelvin / 100.0;
|
||||
|
||||
double red;
|
||||
if (t <= 66.0) {
|
||||
red = 255.0;
|
||||
} else {
|
||||
red = 329.698727446 * qPow(t - 60.0, -0.1332047592);
|
||||
}
|
||||
|
||||
double green;
|
||||
if (t <= 66.0) {
|
||||
green = 99.4708025861 * qLn(t) - 161.1195681661;
|
||||
} else {
|
||||
green = 288.1221695283 * qPow(t - 60.0, -0.0755148492);
|
||||
}
|
||||
|
||||
double blue;
|
||||
if (t >= 66.0) {
|
||||
blue = 255.0;
|
||||
} else if (t <= 19.0) {
|
||||
blue = 0.0;
|
||||
} else {
|
||||
blue = 138.5177312231 * qLn(t - 10.0) - 305.0447927307;
|
||||
}
|
||||
|
||||
// Normalize to the green channel so temperature shifts do not change
|
||||
// exposure, then let tint move along the green-magenta axis
|
||||
red /= green;
|
||||
blue /= green;
|
||||
green = 1.0;
|
||||
|
||||
const double tint_gain = qBound(0.0, 1.0 + tint, 2.0);
|
||||
|
||||
return QVector3D(float(red), float(green * tint_gain), float(blue));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_WHITEBALANCENODE_H
|
||||
#define OAK_WHITEBALANCENODE_H
|
||||
|
||||
#include <QVector3D>
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief White balance correction by color temperature and tint
|
||||
*
|
||||
* Converts a scene illuminant temperature (in Kelvin) into per-channel RGB
|
||||
* gains using the Tanner Helland blackbody approximation, normalized so the
|
||||
* green channel is preserved (no exposure shift). Tint shifts the image
|
||||
* along the green-magenta axis.
|
||||
*/
|
||||
class WhiteBalanceNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
WhiteBalanceNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(WhiteBalanceNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual ShaderCode get_shader_code(const ShaderRequest &request) const override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
/**
|
||||
* @brief RGB gains for a given illuminant temperature and tint
|
||||
*
|
||||
* Extracted for testability. Kelvin is clamped to [1000, 40000]; the
|
||||
* result is normalized so the green channel gain is 1.0 at tint 0.
|
||||
*/
|
||||
static QVector3D get_gain_for_temperature(double kelvin, double tint);
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_temperature_input;
|
||||
static const QString k_tint_input;
|
||||
static const QString k_gain_input;
|
||||
};
|
||||
|
||||
} // olive
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(cornerpin)
|
||||
add_subdirectory(crop)
|
||||
add_subdirectory(flip)
|
||||
add_subdirectory(mask)
|
||||
add_subdirectory(ripple)
|
||||
add_subdirectory(swirl)
|
||||
add_subdirectory(tile)
|
||||
add_subdirectory(transform)
|
||||
add_subdirectory(wave)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/cornerpin/cornerpindistortnode.cpp
|
||||
node/distort/cornerpin/cornerpindistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,222 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "cornerpindistortnode.h"
|
||||
|
||||
#include "common/lerp.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString CornerPinDistortNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString CornerPinDistortNode::k_top_left_input =
|
||||
QStringLiteral("top_left_in");
|
||||
const QString CornerPinDistortNode::k_top_right_input =
|
||||
QStringLiteral("top_right_in");
|
||||
const QString CornerPinDistortNode::k_bottom_right_input =
|
||||
QStringLiteral("bottom_right_in");
|
||||
const QString CornerPinDistortNode::k_bottom_left_input =
|
||||
QStringLiteral("bottom_left_in");
|
||||
const QString CornerPinDistortNode::k_perspective_input =
|
||||
QStringLiteral("perspective_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
CornerPinDistortNode::CornerPinDistortNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
add_input(k_perspective_input, NodeValue::k_boolean, true);
|
||||
add_input(k_top_left_input, NodeValue::k_vec2, QVector2D(0.0, 0.0));
|
||||
add_input(k_top_right_input, NodeValue::k_vec2, QVector2D(0.0, 0.0));
|
||||
add_input(k_bottom_right_input, NodeValue::k_vec2, QVector2D(0.0, 0.0));
|
||||
add_input(k_bottom_left_input, NodeValue::k_vec2, QVector2D(0.0, 0.0));
|
||||
|
||||
// Initiate gizmos
|
||||
gizmo_whole_rect_ = add_draggable_gizmo<PolygonGizmo>();
|
||||
gizmo_resize_handle_[0] = add_draggable_gizmo<PointGizmo>(
|
||||
{ NodeKeyframeTrackReference(NodeInput(this, k_top_left_input), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_top_left_input), 1) });
|
||||
gizmo_resize_handle_[1] = add_draggable_gizmo<PointGizmo>(
|
||||
{ NodeKeyframeTrackReference(NodeInput(this, k_top_right_input), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_top_right_input), 1) });
|
||||
gizmo_resize_handle_[2] = add_draggable_gizmo<PointGizmo>(
|
||||
{ NodeKeyframeTrackReference(NodeInput(this, k_bottom_right_input), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_bottom_right_input), 1) });
|
||||
gizmo_resize_handle_[3] = add_draggable_gizmo<PointGizmo>(
|
||||
{ NodeKeyframeTrackReference(NodeInput(this, k_bottom_left_input), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_bottom_left_input), 1) });
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
}
|
||||
|
||||
void CornerPinDistortNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Texture"));
|
||||
set_input_name(k_perspective_input, tr("Perspective"));
|
||||
set_input_name(k_top_left_input, tr("Top Left"));
|
||||
set_input_name(k_top_right_input, tr("Top Right"));
|
||||
set_input_name(k_bottom_right_input, tr("Bottom Right"));
|
||||
set_input_name(k_bottom_left_input, tr("Bottom Left"));
|
||||
}
|
||||
|
||||
void CornerPinDistortNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// If no texture do nothing
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
// In the special case that all sliders are in their default position just
|
||||
// push the texture.
|
||||
if (!(value[k_top_left_input].to_vec2().isNull() &&
|
||||
value[k_top_right_input].to_vec2().isNull() &&
|
||||
value[k_bottom_right_input].to_vec2().isNull() &&
|
||||
value[k_bottom_left_input].to_vec2().isNull())) {
|
||||
ShaderJob job(value);
|
||||
job.insert(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::k_vec2, tex->virtual_resolution(),
|
||||
this));
|
||||
|
||||
// Convert slider values to their pixel values and then convert to clip space (-1.0 ... 1.0) for overriding the
|
||||
// vertex coordinates.
|
||||
const QVector2D &resolution = tex->virtual_resolution();
|
||||
QVector2D half_resolution = resolution * 0.5;
|
||||
QVector2D top_left = QVector2D(value_to_pixel(0, value, resolution)) /
|
||||
half_resolution -
|
||||
QVector2D(1.0, 1.0);
|
||||
QVector2D top_right =
|
||||
QVector2D(value_to_pixel(1, value, resolution)) /
|
||||
half_resolution -
|
||||
QVector2D(1.0, 1.0);
|
||||
QVector2D bottom_right =
|
||||
QVector2D(value_to_pixel(2, value, resolution)) /
|
||||
half_resolution -
|
||||
QVector2D(1.0, 1.0);
|
||||
QVector2D bottom_left =
|
||||
QVector2D(value_to_pixel(3, value, resolution)) /
|
||||
half_resolution -
|
||||
QVector2D(1.0, 1.0);
|
||||
|
||||
// Override default vertex coordinates.
|
||||
QVector<float> adjusted_vertices = {
|
||||
top_left.x(), top_left.y(), 0.0f,
|
||||
top_right.x(), top_right.y(), 0.0f,
|
||||
bottom_right.x(), bottom_right.y(), 0.0f,
|
||||
|
||||
top_left.x(), top_left.y(), 0.0f,
|
||||
bottom_left.x(), bottom_left.y(), 0.0f,
|
||||
bottom_right.x(), bottom_right.y(), 0.0f
|
||||
};
|
||||
job.set_vertex_coordinates(adjusted_vertices);
|
||||
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
} else {
|
||||
table->push(value[k_texture_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShaderCode
|
||||
CornerPinDistortNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
|
||||
return ShaderCode(FileFunctions::read_file_as_string(
|
||||
QStringLiteral(":/shaders/cornerpin.frag")),
|
||||
FileFunctions::read_file_as_string(
|
||||
QStringLiteral(":/shaders/cornerpin.vert")));
|
||||
}
|
||||
|
||||
QPointF CornerPinDistortNode::value_to_pixel(int value, const NodeValueRow &row,
|
||||
const QVector2D &resolution) const
|
||||
{
|
||||
Q_ASSERT(value >= 0 && value <= 3);
|
||||
|
||||
QVector2D v;
|
||||
|
||||
switch (value) {
|
||||
case 0: // Top left
|
||||
v = row[k_top_left_input].to_vec2();
|
||||
return QPointF(v.x(), v.y());
|
||||
case 1: // Top right
|
||||
v = row[k_top_right_input].to_vec2();
|
||||
return QPointF(resolution.x() + v.x(), v.y());
|
||||
case 2: // Bottom right
|
||||
v = row[k_bottom_right_input].to_vec2();
|
||||
return QPointF(resolution.x() + v.x(), resolution.y() + v.y());
|
||||
case 3: //Bottom left
|
||||
v = row[k_bottom_left_input].to_vec2();
|
||||
return QPointF(v.x(), v.y() + resolution.y());
|
||||
default: // We should never get here
|
||||
return QPointF();
|
||||
}
|
||||
}
|
||||
|
||||
void CornerPinDistortNode::gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo *>(sender());
|
||||
|
||||
if (gizmo != gizmo_whole_rect_) {
|
||||
gizmo->get_draggers()[0].drag(
|
||||
gizmo->get_draggers()[0].get_start_value().toDouble() + x);
|
||||
gizmo->get_draggers()[1].drag(
|
||||
gizmo->get_draggers()[1].get_start_value().toDouble() + y);
|
||||
}
|
||||
}
|
||||
|
||||
void CornerPinDistortNode::update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals)
|
||||
{
|
||||
if (TexturePtr tex = row[k_texture_input].to_texture()) {
|
||||
const QVector2D &resolution = tex->virtual_resolution();
|
||||
|
||||
QPointF top_left = value_to_pixel(0, row, resolution);
|
||||
QPointF top_right = value_to_pixel(1, row, resolution);
|
||||
QPointF bottom_right = value_to_pixel(2, row, resolution);
|
||||
QPointF bottom_left = value_to_pixel(3, row, resolution);
|
||||
|
||||
// Add the correct offset to each slider
|
||||
set_input_property(k_top_left_input, QStringLiteral("offset"),
|
||||
QVector2D(0.0, 0.0));
|
||||
set_input_property(k_top_right_input, QStringLiteral("offset"),
|
||||
QVector2D(resolution.x(), 0.0));
|
||||
set_input_property(k_bottom_right_input, QStringLiteral("offset"),
|
||||
resolution);
|
||||
set_input_property(k_bottom_left_input, QStringLiteral("offset"),
|
||||
QVector2D(0.0, resolution.y()));
|
||||
|
||||
// Draw bounding box
|
||||
gizmo_whole_rect_->set_polygon(QPolygonF(
|
||||
{ top_left, top_right, bottom_right, bottom_left, top_left }));
|
||||
|
||||
// Create handles
|
||||
gizmo_resize_handle_[0]->set_point(top_left);
|
||||
gizmo_resize_handle_[1]->set_point(top_right);
|
||||
gizmo_resize_handle_[2]->set_point(bottom_right);
|
||||
gizmo_resize_handle_[3]->set_point(bottom_left);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CORNERPINDISTORTNODE_H
|
||||
#define OAK_CORNERPINDISTORTNODE_H
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/inputdragger.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
class CornerPinDistortNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CornerPinDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(CornerPinDistortNode)
|
||||
|
||||
virtual QString name() const override
|
||||
{
|
||||
return tr("Corner Pin");
|
||||
}
|
||||
|
||||
virtual QString id() const override
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.cornerpin");
|
||||
}
|
||||
|
||||
virtual QVector<CategoryID> category() const override
|
||||
{
|
||||
return { k_category_distort };
|
||||
}
|
||||
|
||||
virtual QString description() const override
|
||||
{
|
||||
return tr("Distort the image by dragging the corners.");
|
||||
}
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
|
||||
virtual void update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals) override;
|
||||
|
||||
/**
|
||||
* @brief Convenience function - converts the 2D slider values from being
|
||||
* an offset to the actual pixel value.
|
||||
*/
|
||||
QPointF value_to_pixel(int value, const NodeValueRow &row,
|
||||
const QVector2D &resolution) const;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_perspective_input;
|
||||
static const QString k_top_left_input;
|
||||
static const QString k_top_right_input;
|
||||
static const QString k_bottom_right_input;
|
||||
static const QString k_bottom_left_input;
|
||||
|
||||
protected slots:
|
||||
virtual void gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
// Gizmo variables
|
||||
static const int k_gizmo_corner_count = 4;
|
||||
PointGizmo *gizmo_resize_handle_[k_gizmo_corner_count];
|
||||
PolygonGizmo *gizmo_whole_rect_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_CORNERPINDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/crop/cropdistortnode.cpp
|
||||
node/distort/crop/cropdistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,188 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "cropdistortnode.h"
|
||||
|
||||
#include "common/util.h"
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString CropDistortNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString CropDistortNode::k_left_input = QStringLiteral("left_in");
|
||||
const QString CropDistortNode::k_top_input = QStringLiteral("top_in");
|
||||
const QString CropDistortNode::k_right_input = QStringLiteral("right_in");
|
||||
const QString CropDistortNode::k_bottom_input = QStringLiteral("bottom_in");
|
||||
const QString CropDistortNode::k_feather_input = QStringLiteral("feather_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
CropDistortNode::CropDistortNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
create_crop_side_input(k_left_input);
|
||||
create_crop_side_input(k_top_input);
|
||||
create_crop_side_input(k_right_input);
|
||||
create_crop_side_input(k_bottom_input);
|
||||
|
||||
add_input(k_feather_input, NodeValue::k_float, 0.0);
|
||||
set_input_property(k_feather_input, QStringLiteral("min"), 0.0);
|
||||
|
||||
// Initiate gizmos
|
||||
poly_gizmo_ = add_draggable_gizmo<PolygonGizmo>(
|
||||
{ k_left_input, k_top_input, k_right_input, k_bottom_input });
|
||||
|
||||
point_gizmo_[k_gizmo_scale_top_left] =
|
||||
add_draggable_gizmo<PointGizmo>({ k_left_input, k_top_input });
|
||||
point_gizmo_[k_gizmo_scale_top_center] =
|
||||
add_draggable_gizmo<PointGizmo>({ k_top_input });
|
||||
point_gizmo_[k_gizmo_scale_top_right] =
|
||||
add_draggable_gizmo<PointGizmo>({ k_right_input, k_top_input });
|
||||
point_gizmo_[k_gizmo_scale_bottom_left] =
|
||||
add_draggable_gizmo<PointGizmo>({ k_left_input, k_bottom_input });
|
||||
point_gizmo_[k_gizmo_scale_bottom_center] =
|
||||
add_draggable_gizmo<PointGizmo>({ k_bottom_input });
|
||||
point_gizmo_[k_gizmo_scale_bottom_right] =
|
||||
add_draggable_gizmo<PointGizmo>({ k_right_input, k_bottom_input });
|
||||
point_gizmo_[k_gizmo_scale_center_left] =
|
||||
add_draggable_gizmo<PointGizmo>({ k_left_input });
|
||||
point_gizmo_[k_gizmo_scale_center_right] =
|
||||
add_draggable_gizmo<PointGizmo>({ k_right_input });
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
}
|
||||
|
||||
void CropDistortNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Texture"));
|
||||
set_input_name(k_left_input, tr("Left"));
|
||||
set_input_name(k_top_input, tr("Top"));
|
||||
set_input_name(k_right_input, tr("Right"));
|
||||
set_input_name(k_bottom_input, tr("Bottom"));
|
||||
set_input_name(k_feather_input, tr("Feather"));
|
||||
}
|
||||
|
||||
void CropDistortNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
ShaderJob job;
|
||||
job.insert(value);
|
||||
|
||||
if (TexturePtr texture = job.get(k_texture_input).to_texture()) {
|
||||
job.insert(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::k_vec2,
|
||||
QVector2D(texture->params().width(),
|
||||
texture->params().height()),
|
||||
this));
|
||||
|
||||
if (!qIsNull(job.get(k_left_input).to_double()) ||
|
||||
!qIsNull(job.get(k_right_input).to_double()) ||
|
||||
!qIsNull(job.get(k_top_input).to_double()) ||
|
||||
!qIsNull(job.get(k_bottom_input).to_double())) {
|
||||
table->push(NodeValue::k_texture, texture->to_job(job), this);
|
||||
} else {
|
||||
table->push(job.get(k_texture_input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShaderCode CropDistortNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(
|
||||
FileFunctions::read_file_as_string(QStringLiteral(":/shaders/crop.frag")));
|
||||
}
|
||||
|
||||
void CropDistortNode::update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals)
|
||||
{
|
||||
if (TexturePtr tex = row[k_texture_input].to_texture()) {
|
||||
const QVector2D &resolution = tex->virtual_resolution();
|
||||
temp_resolution_ = resolution;
|
||||
|
||||
double left_pt = resolution.x() * row[k_left_input].to_double();
|
||||
double top_pt = resolution.y() * row[k_top_input].to_double();
|
||||
double right_pt = resolution.x() * (1.0 - row[k_right_input].to_double());
|
||||
double bottom_pt =
|
||||
resolution.y() * (1.0 - row[k_bottom_input].to_double());
|
||||
double center_x_pt = mid(left_pt, right_pt);
|
||||
double center_y_pt = mid(top_pt, bottom_pt);
|
||||
|
||||
point_gizmo_[k_gizmo_scale_top_left]->set_point(QPointF(left_pt, top_pt));
|
||||
point_gizmo_[k_gizmo_scale_top_center]->set_point(
|
||||
QPointF(center_x_pt, top_pt));
|
||||
point_gizmo_[k_gizmo_scale_top_right]->set_point(QPointF(right_pt, top_pt));
|
||||
point_gizmo_[k_gizmo_scale_bottom_left]->set_point(
|
||||
QPointF(left_pt, bottom_pt));
|
||||
point_gizmo_[k_gizmo_scale_bottom_center]->set_point(
|
||||
QPointF(center_x_pt, bottom_pt));
|
||||
point_gizmo_[k_gizmo_scale_bottom_right]->set_point(
|
||||
QPointF(right_pt, bottom_pt));
|
||||
point_gizmo_[k_gizmo_scale_center_left]->set_point(
|
||||
QPointF(left_pt, center_y_pt));
|
||||
point_gizmo_[k_gizmo_scale_center_right]->set_point(
|
||||
QPointF(right_pt, center_y_pt));
|
||||
|
||||
poly_gizmo_->set_polygon(
|
||||
QRectF(left_pt, top_pt, right_pt - left_pt, bottom_pt - top_pt));
|
||||
}
|
||||
}
|
||||
|
||||
void CropDistortNode::gizmo_drag_move(double x_diff, double y_diff,
|
||||
const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo *>(sender());
|
||||
|
||||
QVector2D res = temp_resolution_;
|
||||
x_diff /= res.x();
|
||||
y_diff /= res.y();
|
||||
|
||||
for (int j = 0; j < gizmo->get_draggers().size(); j++) {
|
||||
NodeInputDragger &i = gizmo->get_draggers()[j];
|
||||
double s = i.get_start_value().toDouble();
|
||||
if (i.get_input().input().input() == k_left_input) {
|
||||
i.drag(s + x_diff);
|
||||
} else if (i.get_input().input().input() == k_top_input) {
|
||||
i.drag(s + y_diff);
|
||||
} else if (i.get_input().input().input() == k_right_input) {
|
||||
i.drag(s - x_diff);
|
||||
} else if (i.get_input().input().input() == k_bottom_input) {
|
||||
i.drag(s - y_diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CropDistortNode::create_crop_side_input(const QString &id)
|
||||
{
|
||||
add_input(id, NodeValue::k_float, 0.0);
|
||||
set_input_property(id, QStringLiteral("min"), 0.0);
|
||||
set_input_property(id, QStringLiteral("max"), 1.0);
|
||||
set_input_property(id, QStringLiteral("view"), slider::k_percentage);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_CROPDISTORTNODE_H
|
||||
#define OAK_CROPDISTORTNODE_H
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/inputdragger.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class CropDistortNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CropDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(CropDistortNode)
|
||||
|
||||
virtual QString name() const override
|
||||
{
|
||||
return tr("Crop");
|
||||
}
|
||||
|
||||
virtual QString id() const override
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.crop");
|
||||
}
|
||||
|
||||
virtual QVector<CategoryID> category() const override
|
||||
{
|
||||
return { k_category_distort };
|
||||
}
|
||||
|
||||
virtual QString description() const override
|
||||
{
|
||||
return tr("Crop the edges of an image.");
|
||||
}
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
|
||||
virtual void update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals) override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_left_input;
|
||||
static const QString k_top_input;
|
||||
static const QString k_right_input;
|
||||
static const QString k_bottom_input;
|
||||
static const QString k_feather_input;
|
||||
|
||||
protected slots:
|
||||
virtual void gizmo_drag_move(double delta_x, double delta_y,
|
||||
const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
void create_crop_side_input(const QString &id);
|
||||
|
||||
// Gizmo variables
|
||||
PointGizmo *point_gizmo_[k_gizmo_scale_count];
|
||||
PolygonGizmo *poly_gizmo_;
|
||||
QVector2D temp_resolution_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_CROPDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/flip/flipdistortnode.cpp
|
||||
node/distort/flip/flipdistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,99 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "flipdistortnode.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString FlipDistortNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString FlipDistortNode::k_horizontal_input = QStringLiteral("horiz_in");
|
||||
const QString FlipDistortNode::k_vertical_input = QStringLiteral("vert_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
FlipDistortNode::FlipDistortNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_horizontal_input, NodeValue::k_boolean, false);
|
||||
|
||||
add_input(k_vertical_input, NodeValue::k_boolean, false);
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
}
|
||||
|
||||
QString FlipDistortNode::name() const
|
||||
{
|
||||
return tr("Flip");
|
||||
}
|
||||
|
||||
QString FlipDistortNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.flip");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> FlipDistortNode::category() const
|
||||
{
|
||||
return { k_category_distort };
|
||||
}
|
||||
|
||||
QString FlipDistortNode::description() const
|
||||
{
|
||||
return tr("Flips an image horizontally or vertically");
|
||||
}
|
||||
|
||||
void FlipDistortNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_horizontal_input, tr("Horizontal"));
|
||||
set_input_name(k_vertical_input, tr("Vertical"));
|
||||
}
|
||||
|
||||
ShaderCode FlipDistortNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(FileFunctions::read_file_as_string(":/shaders/flip.frag"));
|
||||
}
|
||||
|
||||
void FlipDistortNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (value[k_horizontal_input].to_bool() ||
|
||||
value[k_vertical_input].to_bool()) {
|
||||
table->push(NodeValue::k_texture, tex->to_job(ShaderJob(value)),
|
||||
this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->push(value[k_texture_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_FLIPDISTORTNODE_H
|
||||
#define OAK_FLIPDISTORTNODE_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class FlipDistortNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FlipDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(FlipDistortNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_horizontal_input;
|
||||
static const QString k_vertical_input;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_FLIPDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/mask/mask.cpp
|
||||
node/distort/mask/mask.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,133 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "mask.h"
|
||||
|
||||
#include "node/filter/blur/blur.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super PolygonGenerator
|
||||
|
||||
const QString MaskDistortNode::k_feather_input = QStringLiteral("feather_in");
|
||||
const QString MaskDistortNode::k_invert_input = QStringLiteral("invert_in");
|
||||
|
||||
MaskDistortNode::MaskDistortNode()
|
||||
{
|
||||
// Mask should always be (1.0, 1.0, 1.0) for multiply to work correctly
|
||||
set_input_flag(k_color_input, k_input_flag_hidden);
|
||||
|
||||
add_input(k_invert_input, NodeValue::k_boolean, false);
|
||||
|
||||
add_input(k_feather_input, NodeValue::k_float, 0.0);
|
||||
set_input_property(k_feather_input, QStringLiteral("min"), 0.0);
|
||||
}
|
||||
|
||||
ShaderCode MaskDistortNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
if (request.id == QStringLiteral("mrg")) {
|
||||
return ShaderCode(FileFunctions::read_file_as_string(
|
||||
QStringLiteral(":/shaders/multiply.frag")));
|
||||
} else if (request.id == QStringLiteral("feather")) {
|
||||
return ShaderCode(FileFunctions::read_file_as_string(
|
||||
QStringLiteral(":/shaders/blur.frag")));
|
||||
} else if (request.id == QStringLiteral("invert")) {
|
||||
return ShaderCode(FileFunctions::read_file_as_string(
|
||||
QStringLiteral(":/shaders/invertrgba.frag")));
|
||||
} else {
|
||||
return super::get_shader_code(request);
|
||||
}
|
||||
}
|
||||
|
||||
void MaskDistortNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_base_input, tr("Texture"));
|
||||
set_input_name(k_invert_input, tr("Invert"));
|
||||
set_input_name(k_feather_input, tr("Feather"));
|
||||
}
|
||||
|
||||
void MaskDistortNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
TexturePtr texture = value[k_base_input].to_texture();
|
||||
|
||||
VideoParams job_params = texture ? texture->params() : globals.vparams();
|
||||
NodeValue job(NodeValue::k_texture,
|
||||
Texture::job(job_params, get_generate_job(value, job_params)),
|
||||
this);
|
||||
|
||||
if (value[k_invert_input].to_bool()) {
|
||||
ShaderJob invert;
|
||||
invert.set_shader_id(QStringLiteral("invert"));
|
||||
invert.insert(QStringLiteral("tex_in"), job);
|
||||
job.set_value(Texture::job(job_params, invert));
|
||||
}
|
||||
|
||||
if (texture) {
|
||||
// Push as merge node
|
||||
ShaderJob merge;
|
||||
|
||||
merge.set_shader_id(QStringLiteral("mrg"));
|
||||
merge.insert(QStringLiteral("tex_a"), value[k_base_input]);
|
||||
|
||||
if (value[k_feather_input].to_double() > 0.0) {
|
||||
// Nest a blur shader in there too
|
||||
ShaderJob feather;
|
||||
|
||||
feather.set_shader_id(QStringLiteral("feather"));
|
||||
feather.insert(BlurFilterNode::k_texture_input, job);
|
||||
feather.insert(BlurFilterNode::k_method_input,
|
||||
NodeValue(NodeValue::k_int,
|
||||
int(BlurFilterNode::k_gaussian), this));
|
||||
feather.insert(BlurFilterNode::k_horiz_input,
|
||||
NodeValue(NodeValue::k_boolean, true, this));
|
||||
feather.insert(BlurFilterNode::k_vert_input,
|
||||
NodeValue(NodeValue::k_boolean, true, this));
|
||||
feather.insert(BlurFilterNode::k_repeat_edge_pixels_input,
|
||||
NodeValue(NodeValue::k_boolean, true, this));
|
||||
feather.insert(BlurFilterNode::k_radius_input,
|
||||
NodeValue(NodeValue::k_float,
|
||||
value[k_feather_input].to_double(), this));
|
||||
feather.set_iterations(2, BlurFilterNode::k_texture_input);
|
||||
feather.insert(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::k_vec2,
|
||||
texture ? texture->virtual_resolution() :
|
||||
globals.square_resolution(),
|
||||
this));
|
||||
|
||||
merge.insert(QStringLiteral("tex_b"),
|
||||
NodeValue(NodeValue::k_texture,
|
||||
Texture::job(job_params, feather), this));
|
||||
} else {
|
||||
merge.insert(QStringLiteral("tex_b"), job);
|
||||
}
|
||||
|
||||
table->push(NodeValue::k_texture, Texture::job(job_params, merge), this);
|
||||
} else {
|
||||
table->push(job);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_MASKDISTORTNODE_H
|
||||
#define OAK_MASKDISTORTNODE_H
|
||||
|
||||
#include "node/generator/polygon/polygon.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class MaskDistortNode : public PolygonGenerator {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MaskDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(MaskDistortNode)
|
||||
|
||||
virtual QString name() const override
|
||||
{
|
||||
return tr("Mask");
|
||||
}
|
||||
|
||||
virtual QString id() const override
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.mask");
|
||||
}
|
||||
|
||||
virtual QVector<CategoryID> category() const override
|
||||
{
|
||||
return { k_category_distort };
|
||||
}
|
||||
|
||||
virtual QString description() const override
|
||||
{
|
||||
return tr("Apply a polygonal mask.");
|
||||
}
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
static const QString k_invert_input;
|
||||
static const QString k_feather_input;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_MASKDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/ripple/rippledistortnode.cpp
|
||||
node/distort/ripple/rippledistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,141 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "rippledistortnode.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString RippleDistortNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString RippleDistortNode::k_evolution_input =
|
||||
QStringLiteral("evolution_in");
|
||||
const QString RippleDistortNode::k_intensity_input =
|
||||
QStringLiteral("intensity_in");
|
||||
const QString RippleDistortNode::k_frequency_input =
|
||||
QStringLiteral("frequency_in");
|
||||
const QString RippleDistortNode::k_position_input = QStringLiteral("position_in");
|
||||
const QString RippleDistortNode::k_stretch_input = QStringLiteral("stretch_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
RippleDistortNode::RippleDistortNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_evolution_input, NodeValue::k_float, 0);
|
||||
add_input(k_intensity_input, NodeValue::k_float, 100);
|
||||
|
||||
add_input(k_frequency_input, NodeValue::k_float, 1);
|
||||
set_input_property(k_frequency_input, QStringLiteral("base"), 0.01);
|
||||
|
||||
add_input(k_position_input, NodeValue::k_vec2, QVector2D(0, 0));
|
||||
add_input(k_stretch_input, NodeValue::k_boolean, false);
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
|
||||
gizmo_ = add_draggable_gizmo<PointGizmo>({
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_position_input), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_position_input), 1),
|
||||
});
|
||||
gizmo_->set_shape(PointGizmo::k_anchor_point);
|
||||
}
|
||||
|
||||
QString RippleDistortNode::name() const
|
||||
{
|
||||
return tr("Ripple");
|
||||
}
|
||||
|
||||
QString RippleDistortNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.ripple");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> RippleDistortNode::category() const
|
||||
{
|
||||
return { k_category_distort };
|
||||
}
|
||||
|
||||
QString RippleDistortNode::description() const
|
||||
{
|
||||
return tr("Distorts an image with a ripple effect.");
|
||||
}
|
||||
|
||||
void RippleDistortNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_frequency_input, tr("Frequency"));
|
||||
set_input_name(k_intensity_input, tr("Intensity"));
|
||||
set_input_name(k_evolution_input, tr("Evolution"));
|
||||
set_input_name(k_position_input, tr("Position"));
|
||||
set_input_name(k_stretch_input, tr("Stretch"));
|
||||
}
|
||||
|
||||
ShaderCode RippleDistortNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(FileFunctions::read_file_as_string(":/shaders/ripple.frag"));
|
||||
}
|
||||
|
||||
void RippleDistortNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (!qIsNull(value[k_intensity_input].to_double())) {
|
||||
ShaderJob job(value);
|
||||
job.insert(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::k_vec2, tex->virtual_resolution(),
|
||||
this));
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->push(value[k_texture_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RippleDistortNode::update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals)
|
||||
{
|
||||
if (TexturePtr tex = row[k_texture_input].to_texture()) {
|
||||
QPointF half_res(tex->virtual_resolution().x() / 2,
|
||||
tex->virtual_resolution().y() / 2);
|
||||
gizmo_->set_point(half_res + row[k_position_input].to_vec2().toPointF());
|
||||
}
|
||||
}
|
||||
|
||||
void RippleDistortNode::gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
NodeInputDragger &x_drag = gizmo_->get_draggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo_->get_draggers()[1];
|
||||
|
||||
x_drag.drag(x_drag.get_start_value().toDouble() + x);
|
||||
y_drag.drag(y_drag.get_start_value().toDouble() + y);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_RIPPLEDISTORTNODE_H
|
||||
#define OAK_RIPPLEDISTORTNODE_H
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class RippleDistortNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RippleDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(RippleDistortNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
virtual void update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals) override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_evolution_input;
|
||||
static const QString k_intensity_input;
|
||||
static const QString k_frequency_input;
|
||||
static const QString k_position_input;
|
||||
static const QString k_stretch_input;
|
||||
|
||||
protected slots:
|
||||
virtual void gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
PointGizmo *gizmo_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_RIPPLEDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/swirl/swirldistortnode.cpp
|
||||
node/distort/swirl/swirldistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,133 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "swirldistortnode.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString SwirlDistortNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString SwirlDistortNode::k_radius_input = QStringLiteral("radius_in");
|
||||
const QString SwirlDistortNode::k_angle_input = QStringLiteral("angle_in");
|
||||
const QString SwirlDistortNode::k_position_input = QStringLiteral("pos_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
SwirlDistortNode::SwirlDistortNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_radius_input, NodeValue::k_float, 200);
|
||||
set_input_property(k_radius_input, QStringLiteral("min"), 0);
|
||||
|
||||
add_input(k_angle_input, NodeValue::k_float, 10);
|
||||
set_input_property(k_angle_input, QStringLiteral("base"), 0.1);
|
||||
|
||||
add_input(k_position_input, NodeValue::k_vec2, QVector2D(0, 0));
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
|
||||
gizmo_ = add_draggable_gizmo<PointGizmo>({
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_position_input), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_position_input), 1),
|
||||
});
|
||||
gizmo_->set_shape(PointGizmo::k_anchor_point);
|
||||
}
|
||||
|
||||
QString SwirlDistortNode::name() const
|
||||
{
|
||||
return tr("Swirl");
|
||||
}
|
||||
|
||||
QString SwirlDistortNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.swirl");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> SwirlDistortNode::category() const
|
||||
{
|
||||
return { k_category_distort };
|
||||
}
|
||||
|
||||
QString SwirlDistortNode::description() const
|
||||
{
|
||||
return tr("Distorts an image by swirling it around a center point.");
|
||||
}
|
||||
|
||||
void SwirlDistortNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_radius_input, tr("Radius"));
|
||||
set_input_name(k_angle_input, tr("Angle"));
|
||||
set_input_name(k_position_input, tr("Position"));
|
||||
}
|
||||
|
||||
ShaderCode SwirlDistortNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(FileFunctions::read_file_as_string(":/shaders/swirl.frag"));
|
||||
}
|
||||
|
||||
void SwirlDistortNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (!qIsNull(value[k_angle_input].to_double()) &&
|
||||
!qIsNull(value[k_radius_input].to_double())) {
|
||||
ShaderJob job(value);
|
||||
job.insert(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::k_vec2, tex->virtual_resolution(),
|
||||
this));
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->push(value[k_texture_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SwirlDistortNode::update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals)
|
||||
{
|
||||
QPointF half_res(globals.square_resolution().x() / 2,
|
||||
globals.square_resolution().y() / 2);
|
||||
|
||||
gizmo_->set_point(half_res + row[k_position_input].to_vec2().toPointF());
|
||||
}
|
||||
|
||||
void SwirlDistortNode::gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
NodeInputDragger &x_drag = gizmo_->get_draggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo_->get_draggers()[1];
|
||||
|
||||
x_drag.drag(x_drag.get_start_value().toDouble() + x);
|
||||
y_drag.drag(y_drag.get_start_value().toDouble() + y);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_SWIRLDISTORTNODE_H
|
||||
#define OAK_SWIRLDISTORTNODE_H
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class SwirlDistortNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SwirlDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(SwirlDistortNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
virtual void update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals) override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_radius_input;
|
||||
static const QString k_angle_input;
|
||||
static const QString k_position_input;
|
||||
|
||||
protected slots:
|
||||
virtual void gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
PointGizmo *gizmo_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_SWIRLDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/tile/tiledistortnode.cpp
|
||||
node/distort/tile/tiledistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,177 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "tiledistortnode.h"
|
||||
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString TileDistortNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString TileDistortNode::k_scale_input = QStringLiteral("scale_in");
|
||||
const QString TileDistortNode::k_position_input = QStringLiteral("position_in");
|
||||
const QString TileDistortNode::k_anchor_input = QStringLiteral("anchor_in");
|
||||
const QString TileDistortNode::k_mirror_x_input = QStringLiteral("mirrorx_in");
|
||||
const QString TileDistortNode::k_mirror_y_input = QStringLiteral("mirrory_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
TileDistortNode::TileDistortNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_scale_input, NodeValue::k_float, 0.5);
|
||||
set_input_property(k_scale_input, QStringLiteral("min"), 0);
|
||||
set_input_property(k_scale_input, QStringLiteral("view"),
|
||||
slider::k_percentage);
|
||||
|
||||
add_input(k_position_input, NodeValue::k_vec2, QVector2D(0, 0));
|
||||
|
||||
add_input(k_anchor_input, NodeValue::k_combo, k_middle_center);
|
||||
|
||||
add_input(k_mirror_x_input, NodeValue::k_boolean, false);
|
||||
add_input(k_mirror_y_input, NodeValue::k_boolean, false);
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
|
||||
gizmo_ = add_draggable_gizmo<PointGizmo>({
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_position_input), 0),
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_position_input), 1),
|
||||
});
|
||||
gizmo_->set_shape(PointGizmo::k_anchor_point);
|
||||
}
|
||||
|
||||
QString TileDistortNode::name() const
|
||||
{
|
||||
return tr("Tile");
|
||||
}
|
||||
|
||||
QString TileDistortNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.tile");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> TileDistortNode::category() const
|
||||
{
|
||||
return { k_category_distort };
|
||||
}
|
||||
|
||||
QString TileDistortNode::description() const
|
||||
{
|
||||
return tr("Infinitely tile an image horizontally and vertically.");
|
||||
}
|
||||
|
||||
void TileDistortNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_scale_input, tr("Scale"));
|
||||
set_input_name(k_position_input, tr("Position"));
|
||||
set_input_name(k_mirror_x_input, tr("Mirror Horizontally"));
|
||||
set_input_name(k_mirror_y_input, tr("Mirror Vertically"));
|
||||
|
||||
set_input_name(k_anchor_input, tr("Anchor"));
|
||||
set_combo_box_strings(k_anchor_input, {
|
||||
tr("Top-Left"),
|
||||
tr("Top-Center"),
|
||||
tr("Top-Right"),
|
||||
tr("Middle-Left"),
|
||||
tr("Middle-Center"),
|
||||
tr("Middle-Right"),
|
||||
tr("Bottom-Left"),
|
||||
tr("Bottom-Center"),
|
||||
tr("Bottom-Right"),
|
||||
});
|
||||
}
|
||||
|
||||
ShaderCode TileDistortNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(FileFunctions::read_file_as_string(":/shaders/tile.frag"));
|
||||
}
|
||||
|
||||
void TileDistortNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (!qFuzzyCompare(value[k_scale_input].to_double(), 1.0)) {
|
||||
ShaderJob job(value);
|
||||
job.insert(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::k_vec2, tex->virtual_resolution(),
|
||||
this));
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->push(value[k_texture_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileDistortNode::update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals)
|
||||
{
|
||||
if (TexturePtr tex = row[k_texture_input].to_texture()) {
|
||||
QPointF res = tex->virtual_resolution().toPointF();
|
||||
QPointF pos = row[k_position_input].to_vec2().toPointF();
|
||||
qreal x = pos.x();
|
||||
qreal y = pos.y();
|
||||
|
||||
Anchor a = static_cast<Anchor>(row[k_anchor_input].to_int());
|
||||
if (a == k_top_left || a == k_top_center || a == k_top_right) {
|
||||
// Do nothing
|
||||
} else if (a == k_middle_left || a == k_middle_center ||
|
||||
a == k_middle_right) {
|
||||
y += res.y() / 2;
|
||||
} else if (a == k_bottom_left || a == k_bottom_center ||
|
||||
a == k_bottom_right) {
|
||||
y += res.y();
|
||||
}
|
||||
if (a == k_top_left || a == k_middle_left || a == k_bottom_left) {
|
||||
// Do nothing
|
||||
} else if (a == k_top_center || a == k_middle_center ||
|
||||
a == k_bottom_center) {
|
||||
x += res.x() / 2;
|
||||
} else if (a == k_top_right || a == k_middle_right || a == k_bottom_right) {
|
||||
x += res.x();
|
||||
}
|
||||
|
||||
gizmo_->set_point(QPointF(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
void TileDistortNode::gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
NodeInputDragger &x_drag = gizmo_->get_draggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo_->get_draggers()[1];
|
||||
|
||||
x_drag.drag(x_drag.get_start_value().toDouble() + x);
|
||||
y_drag.drag(y_drag.get_start_value().toDouble() + y);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_TILEDISTORTNODE_H
|
||||
#define OAK_TILEDISTORTNODE_H
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class TileDistortNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TileDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(TileDistortNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
virtual void update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals) override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_scale_input;
|
||||
static const QString k_position_input;
|
||||
static const QString k_anchor_input;
|
||||
static const QString k_mirror_x_input;
|
||||
static const QString k_mirror_y_input;
|
||||
|
||||
protected slots:
|
||||
virtual void gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
enum Anchor {
|
||||
k_top_left,
|
||||
k_top_center,
|
||||
k_top_right,
|
||||
k_middle_left,
|
||||
k_middle_center,
|
||||
k_middle_right,
|
||||
k_bottom_left,
|
||||
k_bottom_center,
|
||||
k_bottom_right
|
||||
};
|
||||
|
||||
PointGizmo *gizmo_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_TILEDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/transform/transformdistortnode.cpp
|
||||
node/distort/transform/transformdistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,513 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "transformdistortnode.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString TransformDistortNode::k_parent_input = QStringLiteral("parent_in");
|
||||
const QString TransformDistortNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString TransformDistortNode::k_autoscale_input =
|
||||
QStringLiteral("autoscale_in");
|
||||
const QString TransformDistortNode::k_interpolation_input =
|
||||
QStringLiteral("interpolation_in");
|
||||
|
||||
#define super MatrixGenerator
|
||||
|
||||
TransformDistortNode::TransformDistortNode()
|
||||
{
|
||||
add_input(k_parent_input, NodeValue::k_matrix);
|
||||
|
||||
add_input(k_autoscale_input, NodeValue::k_combo, 0);
|
||||
|
||||
add_input(k_interpolation_input, NodeValue::k_combo, 2);
|
||||
|
||||
prepend_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
// Initiate gizmos
|
||||
rotation_gizmo_ = add_draggable_gizmo<ScreenGizmo>();
|
||||
rotation_gizmo_->add_input(NodeInput(this, k_rotation_input));
|
||||
rotation_gizmo_->set_drag_value_behavior(ScreenGizmo::k_absolute);
|
||||
|
||||
poly_gizmo_ = add_draggable_gizmo<PolygonGizmo>();
|
||||
poly_gizmo_->add_input(
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_position_input), 0));
|
||||
poly_gizmo_->add_input(
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_position_input), 1));
|
||||
|
||||
anchor_gizmo_ = add_draggable_gizmo<PointGizmo>();
|
||||
anchor_gizmo_->set_shape(PointGizmo::k_anchor_point);
|
||||
anchor_gizmo_->add_input(
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_anchor_input), 0));
|
||||
anchor_gizmo_->add_input(
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_anchor_input), 1));
|
||||
anchor_gizmo_->add_input(
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_position_input), 0));
|
||||
anchor_gizmo_->add_input(
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_position_input), 1));
|
||||
|
||||
for (int i = 0; i < k_gizmo_scale_count; i++) {
|
||||
point_gizmo_[i] = add_draggable_gizmo<PointGizmo>();
|
||||
point_gizmo_[i]->add_input(
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_scale_input), 0));
|
||||
point_gizmo_[i]->add_input(
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_scale_input), 1));
|
||||
point_gizmo_[i]->set_drag_value_behavior(PointGizmo::k_absolute);
|
||||
}
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
}
|
||||
|
||||
void TransformDistortNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_parent_input, tr("Parent"));
|
||||
set_input_name(k_autoscale_input, tr("Auto-Scale"));
|
||||
set_input_name(k_texture_input, tr("Texture"));
|
||||
set_input_name(k_interpolation_input, tr("Interpolation"));
|
||||
|
||||
set_combo_box_strings(k_autoscale_input,
|
||||
{ tr("None"), tr("Fit"), tr("Fill"), tr("Stretch") });
|
||||
set_combo_box_strings(k_interpolation_input,
|
||||
{ tr("Nearest Neighbor"), tr("Bilinear"),
|
||||
tr("Mipmapped Bilinear") });
|
||||
}
|
||||
|
||||
void TransformDistortNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// Generate matrix
|
||||
QMatrix4x4 generated_matrix = generate_matrix(
|
||||
value, false, false, false, value[k_parent_input].to_matrix());
|
||||
|
||||
// Pop texture
|
||||
NodeValue texture_meta = value[k_texture_input];
|
||||
|
||||
TexturePtr job_to_push = nullptr;
|
||||
|
||||
// If we have a texture, generate a matrix and make it happen
|
||||
if (TexturePtr texture = texture_meta.to_texture()) {
|
||||
// Adjust our matrix by the resolutions involved
|
||||
QMatrix4x4 real_matrix = generate_auto_scaled_matrix(
|
||||
generated_matrix, value, globals, texture->params());
|
||||
|
||||
if (!real_matrix.isIdentity()) {
|
||||
// The matrix will transform things
|
||||
ShaderJob job;
|
||||
job.insert(QStringLiteral("ove_maintex"), texture_meta);
|
||||
job.insert(QStringLiteral("ove_mvpmat"),
|
||||
NodeValue(NodeValue::k_matrix, real_matrix, this));
|
||||
job.set_interpolation(QStringLiteral("ove_maintex"),
|
||||
static_cast<Texture::Interpolation>(
|
||||
value[k_interpolation_input].to_int()));
|
||||
|
||||
// Use global resolution rather than texture resolution because this may result in a size change
|
||||
job_to_push = Texture::job(globals.vparams(), job);
|
||||
}
|
||||
}
|
||||
|
||||
table->push(NodeValue::k_matrix, QVariant::fromValue(generated_matrix),
|
||||
this);
|
||||
|
||||
if (!job_to_push) {
|
||||
// Re-push whatever value we received
|
||||
table->push(texture_meta);
|
||||
} else {
|
||||
table->push(NodeValue::k_texture, job_to_push, this);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderCode
|
||||
TransformDistortNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request);
|
||||
|
||||
// Returns default frag and vert shader
|
||||
return ShaderCode();
|
||||
}
|
||||
|
||||
void TransformDistortNode::gizmo_drag_start(const NodeValueRow &row, double x,
|
||||
double y, const Rational &time)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo *>(sender());
|
||||
|
||||
if (gizmo == anchor_gizmo_) {
|
||||
gizmo_inverted_transform_ =
|
||||
generate_matrix(row, true, true, false, row[k_parent_input].to_matrix())
|
||||
.toTransform()
|
||||
.inverted();
|
||||
|
||||
} else if (is_a_scale_gizmo(gizmo)) {
|
||||
// Dragging scale handle
|
||||
TexturePtr tex = row[k_texture_input].to_texture();
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
gizmo_scale_uniform_ = row[k_uniform_scale_input].to_bool();
|
||||
gizmo_anchor_pt_ = (row[k_anchor_input].to_vec2() +
|
||||
gizmo->get_globals().nonsquare_resolution() / 2)
|
||||
.toPointF();
|
||||
|
||||
if (gizmo == point_gizmo_[k_gizmo_scale_top_left] ||
|
||||
gizmo == point_gizmo_[k_gizmo_scale_top_right] ||
|
||||
gizmo == point_gizmo_[k_gizmo_scale_bottom_left] ||
|
||||
gizmo == point_gizmo_[k_gizmo_scale_bottom_right]) {
|
||||
gizmo_scale_axes_ = k_gizmo_scale_both;
|
||||
} else if (gizmo == point_gizmo_[k_gizmo_scale_center_left] ||
|
||||
gizmo == point_gizmo_[k_gizmo_scale_center_right]) {
|
||||
gizmo_scale_axes_ = k_gizmo_scale_x_only;
|
||||
} else {
|
||||
gizmo_scale_axes_ = k_gizmo_scale_y_only;
|
||||
}
|
||||
|
||||
// Store texture size
|
||||
VideoParams texture_params = tex->params();
|
||||
QVector2D texture_sz(texture_params.square_pixel_width(),
|
||||
texture_params.height());
|
||||
gizmo_scale_anchor_ = row[k_anchor_input].to_vec2() + texture_sz / 2;
|
||||
|
||||
if (gizmo == point_gizmo_[k_gizmo_scale_top_right] ||
|
||||
gizmo == point_gizmo_[k_gizmo_scale_bottom_right] ||
|
||||
gizmo == point_gizmo_[k_gizmo_scale_center_right]) {
|
||||
// Right handles, flip X axis
|
||||
gizmo_scale_anchor_.setX(texture_sz.x() - gizmo_scale_anchor_.x());
|
||||
}
|
||||
|
||||
if (gizmo == point_gizmo_[k_gizmo_scale_bottom_left] ||
|
||||
gizmo == point_gizmo_[k_gizmo_scale_bottom_right] ||
|
||||
gizmo == point_gizmo_[k_gizmo_scale_bottom_center]) {
|
||||
// Bottom handles, flip Y axis
|
||||
gizmo_scale_anchor_.setY(texture_sz.y() - gizmo_scale_anchor_.y());
|
||||
}
|
||||
|
||||
// Store current matrix
|
||||
gizmo_inverted_transform_ =
|
||||
generate_matrix(row, true, true, true, row[k_parent_input].to_matrix())
|
||||
.toTransform()
|
||||
.inverted();
|
||||
|
||||
} else if (gizmo == rotation_gizmo_) {
|
||||
gizmo_anchor_pt_ = (row[k_anchor_input].to_vec2() +
|
||||
gizmo->get_globals().nonsquare_resolution() / 2)
|
||||
.toPointF();
|
||||
gizmo_start_angle_ =
|
||||
std::atan2(y - gizmo_anchor_pt_.y(), x - gizmo_anchor_pt_.x());
|
||||
gizmo_last_angle_ = gizmo_start_angle_;
|
||||
gizmo_last_alt_angle_ =
|
||||
std::atan2(x - gizmo_anchor_pt_.x(), y - gizmo_anchor_pt_.y());
|
||||
gizmo_rotate_wrap_ = 0;
|
||||
gizmo_rotate_last_dir_ = k_direction_none;
|
||||
}
|
||||
}
|
||||
|
||||
void TransformDistortNode::gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo *>(sender());
|
||||
|
||||
if (gizmo == poly_gizmo_) {
|
||||
NodeInputDragger &x_drag = gizmo->get_draggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo->get_draggers()[1];
|
||||
|
||||
x_drag.drag(x_drag.get_start_value().toDouble() + x);
|
||||
y_drag.drag(y_drag.get_start_value().toDouble() + y);
|
||||
|
||||
} else if (gizmo == anchor_gizmo_) {
|
||||
NodeInputDragger &x_anchor_drag = gizmo->get_draggers()[0];
|
||||
NodeInputDragger &y_anchor_drag = gizmo->get_draggers()[1];
|
||||
NodeInputDragger &x_pos_drag = gizmo->get_draggers()[2];
|
||||
NodeInputDragger &y_pos_drag = gizmo->get_draggers()[3];
|
||||
|
||||
QPointF inverted_movement(gizmo_inverted_transform_.map(QPointF(x, y)));
|
||||
|
||||
x_anchor_drag.drag(x_anchor_drag.get_start_value().toDouble() +
|
||||
inverted_movement.x());
|
||||
y_anchor_drag.drag(y_anchor_drag.get_start_value().toDouble() +
|
||||
inverted_movement.y());
|
||||
x_pos_drag.drag(x_pos_drag.get_start_value().toDouble() + x);
|
||||
y_pos_drag.drag(y_pos_drag.get_start_value().toDouble() + y);
|
||||
|
||||
} else if (gizmo == rotation_gizmo_) {
|
||||
double raw_angle =
|
||||
std::atan2(y - gizmo_anchor_pt_.y(), x - gizmo_anchor_pt_.x());
|
||||
double alt_angle =
|
||||
std::atan2(x - gizmo_anchor_pt_.x(), y - gizmo_anchor_pt_.y());
|
||||
|
||||
double current_angle = raw_angle;
|
||||
|
||||
// Detect rotation wrap around
|
||||
RotationDirection this_dir =
|
||||
get_direction_from_angles(gizmo_last_angle_, raw_angle);
|
||||
RotationDirection alt_dir =
|
||||
get_direction_from_angles(gizmo_last_alt_angle_, alt_angle);
|
||||
|
||||
if (gizmo_rotate_last_dir_ != k_direction_none &&
|
||||
this_dir != gizmo_rotate_last_dir_) {
|
||||
if (alt_dir == gizmo_rotate_last_alt_dir_) {
|
||||
if ((raw_angle - gizmo_last_angle_) < 0) {
|
||||
gizmo_rotate_wrap_++;
|
||||
} else {
|
||||
gizmo_rotate_wrap_--;
|
||||
}
|
||||
|
||||
this_dir = gizmo_rotate_last_dir_;
|
||||
alt_dir = gizmo_rotate_last_alt_dir_;
|
||||
}
|
||||
}
|
||||
|
||||
gizmo_rotate_last_dir_ = this_dir;
|
||||
gizmo_rotate_last_alt_dir_ = alt_dir;
|
||||
gizmo_last_angle_ = raw_angle;
|
||||
gizmo_last_alt_angle_ = alt_angle;
|
||||
|
||||
current_angle += M_PI * 2 * gizmo_rotate_wrap_;
|
||||
|
||||
// Convert radians to degrees
|
||||
double rotation_difference =
|
||||
(current_angle - gizmo_start_angle_) * 57.2958;
|
||||
|
||||
NodeInputDragger &d = gizmo->get_draggers()[0];
|
||||
d.drag(d.get_start_value().toDouble() + rotation_difference);
|
||||
|
||||
} else if (is_a_scale_gizmo(gizmo)) {
|
||||
QPointF mouse_relative =
|
||||
gizmo_inverted_transform_.map(QPointF(x, y) - gizmo_anchor_pt_);
|
||||
|
||||
double x_scaled_movement =
|
||||
qAbs(mouse_relative.x() / gizmo_scale_anchor_.x());
|
||||
double y_scaled_movement =
|
||||
qAbs(mouse_relative.y() / gizmo_scale_anchor_.y());
|
||||
|
||||
NodeInputDragger &x_drag = gizmo->get_draggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo->get_draggers()[1];
|
||||
|
||||
switch (gizmo_scale_axes_) {
|
||||
case k_gizmo_scale_x_only:
|
||||
x_drag.drag(x_scaled_movement);
|
||||
break;
|
||||
case k_gizmo_scale_y_only:
|
||||
if (gizmo_scale_uniform_) {
|
||||
x_drag.drag(y_scaled_movement);
|
||||
} else {
|
||||
y_drag.drag(y_scaled_movement);
|
||||
}
|
||||
break;
|
||||
case k_gizmo_scale_both:
|
||||
if (gizmo_scale_uniform_) {
|
||||
double distance =
|
||||
std::hypot(mouse_relative.x(), mouse_relative.y());
|
||||
double texture_diag = std::hypot(gizmo_scale_anchor_.x(),
|
||||
gizmo_scale_anchor_.y());
|
||||
|
||||
x_drag.drag(qAbs(distance / texture_diag));
|
||||
} else {
|
||||
x_drag.drag(x_scaled_movement);
|
||||
y_drag.drag(y_scaled_movement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QMatrix4x4 TransformDistortNode::adjust_matrix_by_resolutions(
|
||||
const QMatrix4x4 &mat, const QVector2D &sequence_res,
|
||||
const QVector2D &texture_res, const QVector2D &offset,
|
||||
AutoScaleType autoscale_type)
|
||||
{
|
||||
// First, create an identity matrix
|
||||
QMatrix4x4 adjusted_matrix;
|
||||
|
||||
// Scale it to a square based on the sequence's resolution
|
||||
adjusted_matrix.scale(2.0 / sequence_res.x(), 2.0 / sequence_res.y(), 1.0);
|
||||
|
||||
// Apply offset if applicable
|
||||
adjusted_matrix.translate(offset.x(), offset.y());
|
||||
|
||||
// Adjust by the matrix we generated earlier
|
||||
adjusted_matrix *= mat;
|
||||
|
||||
// Scale back out to texture size (adjusted by pixel aspect)
|
||||
adjusted_matrix.scale(texture_res.x() * 0.5, texture_res.y() * 0.5, 1.0);
|
||||
|
||||
// If auto-scale is enabled, fit the texture to the sequence (without cropping)
|
||||
if (autoscale_type != k_auto_scale_none) {
|
||||
if (autoscale_type == k_auto_scale_stretch) {
|
||||
adjusted_matrix.scale(sequence_res.x() / texture_res.x(),
|
||||
sequence_res.y() / texture_res.y(), 1.0);
|
||||
} else {
|
||||
double footage_real_ar = texture_res.x() / texture_res.y();
|
||||
double sequence_real_ar = sequence_res.x() / sequence_res.y();
|
||||
|
||||
double scale_by_x = sequence_res.x() / texture_res.x();
|
||||
double scale_by_y = sequence_res.y() / texture_res.y();
|
||||
double autoscale_val;
|
||||
|
||||
if ((autoscale_type == k_auto_scale_fit) ==
|
||||
(sequence_real_ar > footage_real_ar)) {
|
||||
// Scale by height. Either the sequence is wider than the footage or we're using fill and
|
||||
// cutting off the sides
|
||||
autoscale_val = scale_by_y;
|
||||
} else {
|
||||
// Scale by width. Either the footage is wider than the sequence or we're using fill and
|
||||
// cutting off the top and bottom
|
||||
autoscale_val = scale_by_x;
|
||||
}
|
||||
|
||||
adjusted_matrix.scale(autoscale_val, autoscale_val, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
return adjusted_matrix;
|
||||
}
|
||||
|
||||
void TransformDistortNode::update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals)
|
||||
{
|
||||
TexturePtr tex = row[k_texture_input].to_texture();
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the sequence resolution
|
||||
const QVector2D &sequence_res = globals.nonsquare_resolution();
|
||||
QVector2D sequence_half_res = sequence_res * 0.5;
|
||||
QPointF sequence_half_res_pt = sequence_half_res.toPointF();
|
||||
|
||||
// GizmoTraverser just returns the sizes of the textures and no other data
|
||||
VideoParams tex_params = tex->params();
|
||||
QVector2D tex_sz(tex_params.square_pixel_width(), tex_params.height());
|
||||
QVector2D tex_offset = tex_params.offset();
|
||||
|
||||
// Retrieve autoscale value
|
||||
AutoScaleType autoscale =
|
||||
static_cast<AutoScaleType>(row[k_autoscale_input].to_int());
|
||||
|
||||
// Fold values into a matrix for the rectangle
|
||||
QMatrix4x4 rectangle_matrix;
|
||||
rectangle_matrix.scale(sequence_half_res.x(), sequence_half_res.y());
|
||||
rectangle_matrix *= adjust_matrix_by_resolutions(
|
||||
generate_matrix(row, false, false, false, row[k_parent_input].to_matrix()),
|
||||
sequence_res, tex_sz, tex_offset, autoscale);
|
||||
|
||||
// Create rect and transform it
|
||||
const QVector<QPointF> points = { QPointF(-1, -1), QPointF(1, -1),
|
||||
QPointF(1, 1), QPointF(-1, 1),
|
||||
QPointF(-1, -1) };
|
||||
QTransform rectangle_transform = rectangle_matrix.toTransform();
|
||||
QPolygonF r = rectangle_transform.map(points);
|
||||
r.translate(sequence_half_res_pt);
|
||||
poly_gizmo_->set_polygon(r);
|
||||
|
||||
// Draw anchor point
|
||||
QMatrix4x4 anchor_matrix;
|
||||
anchor_matrix.scale(sequence_half_res.x(), sequence_half_res.y());
|
||||
anchor_matrix *= adjust_matrix_by_resolutions(
|
||||
generate_matrix(row, true, false, false, row[k_parent_input].to_matrix()),
|
||||
sequence_res, tex_sz, tex_offset, autoscale);
|
||||
anchor_gizmo_->set_point(anchor_matrix.toTransform().map(QPointF(0, 0)) +
|
||||
sequence_half_res_pt);
|
||||
|
||||
// Draw scale handles
|
||||
point_gizmo_[k_gizmo_scale_top_left]->set_point(
|
||||
create_scale_point(-1, -1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[k_gizmo_scale_top_center]->set_point(
|
||||
create_scale_point(0, -1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[k_gizmo_scale_top_right]->set_point(
|
||||
create_scale_point(1, -1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[k_gizmo_scale_bottom_left]->set_point(
|
||||
create_scale_point(-1, 1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[k_gizmo_scale_bottom_center]->set_point(
|
||||
create_scale_point(0, 1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[k_gizmo_scale_bottom_right]->set_point(
|
||||
create_scale_point(1, 1, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[k_gizmo_scale_center_left]->set_point(
|
||||
create_scale_point(-1, 0, sequence_half_res_pt, rectangle_matrix));
|
||||
point_gizmo_[k_gizmo_scale_center_right]->set_point(
|
||||
create_scale_point(1, 0, sequence_half_res_pt, rectangle_matrix));
|
||||
|
||||
// Use offsets to make the appearance of values that start in the top left, even though we
|
||||
// really anchor around the center
|
||||
set_input_property(k_position_input, QStringLiteral("offset"),
|
||||
sequence_half_res + tex_offset);
|
||||
set_input_property(k_anchor_input, QStringLiteral("offset"), tex_sz * 0.5);
|
||||
}
|
||||
|
||||
QTransform
|
||||
TransformDistortNode::gizmo_transformation(const NodeValueRow &row,
|
||||
const NodeGlobals &globals) const
|
||||
{
|
||||
if (TexturePtr texture = row[k_texture_input].to_texture()) {
|
||||
//auto m = GenerateMatrix(row, false, false, false, row[kParentInput].toMatrix());
|
||||
auto m = generate_matrix(row, false, false, false, QMatrix4x4());
|
||||
return generate_auto_scaled_matrix(m, row, globals, texture->params())
|
||||
.toTransform();
|
||||
}
|
||||
return super::gizmo_transformation(row, globals);
|
||||
}
|
||||
|
||||
QPointF TransformDistortNode::create_scale_point(double x, double y,
|
||||
const QPointF &half_res,
|
||||
const QMatrix4x4 &mat)
|
||||
{
|
||||
return mat.map(QPointF(x, y)) + half_res;
|
||||
}
|
||||
|
||||
QMatrix4x4 TransformDistortNode::generate_auto_scaled_matrix(
|
||||
const QMatrix4x4 &generated_matrix, const NodeValueRow &value,
|
||||
const NodeGlobals &globals, const VideoParams &texture_params) const
|
||||
{
|
||||
const QVector2D &sequence_res = globals.nonsquare_resolution();
|
||||
QVector2D texture_res(texture_params.square_pixel_width(),
|
||||
texture_params.height());
|
||||
AutoScaleType autoscale =
|
||||
static_cast<AutoScaleType>(value[k_autoscale_input].to_int());
|
||||
|
||||
return adjust_matrix_by_resolutions(generated_matrix, sequence_res,
|
||||
texture_res, texture_params.offset(),
|
||||
autoscale);
|
||||
}
|
||||
|
||||
bool TransformDistortNode::is_a_scale_gizmo(NodeGizmo *g) const
|
||||
{
|
||||
for (int i = 0; i < k_gizmo_scale_count; i++) {
|
||||
if (point_gizmo_[i] == g) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TransformDistortNode::RotationDirection
|
||||
TransformDistortNode::get_direction_from_angles(double last, double current)
|
||||
{
|
||||
return (current > last) ? k_direction_positive : k_direction_negative;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_TRANSFORMDISTORTNODE_H
|
||||
#define OAK_TRANSFORMDISTORTNODE_H
|
||||
|
||||
#include "node/generator/matrix/matrix.h"
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/gizmo/screen.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class TransformDistortNode : public MatrixGenerator {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TransformDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(TransformDistortNode)
|
||||
|
||||
virtual QString name() const override
|
||||
{
|
||||
return tr("Transform");
|
||||
}
|
||||
|
||||
virtual QString short_name() const override
|
||||
{
|
||||
// Override MatrixGenerator's short name "Ortho"
|
||||
return name();
|
||||
}
|
||||
|
||||
virtual QString id() const override
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.transform");
|
||||
}
|
||||
|
||||
virtual QVector<CategoryID> category() const override
|
||||
{
|
||||
return { k_category_distort };
|
||||
}
|
||||
|
||||
virtual QString description() const override
|
||||
{
|
||||
return tr(
|
||||
"Transform an image in 2D space. Equivalent to multiplying by an orthographic matrix.");
|
||||
}
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
|
||||
enum AutoScaleType {
|
||||
k_auto_scale_none,
|
||||
k_auto_scale_fit,
|
||||
k_auto_scale_fill,
|
||||
k_auto_scale_stretch
|
||||
};
|
||||
|
||||
static QMatrix4x4 adjust_matrix_by_resolutions(
|
||||
const QMatrix4x4 &mat, const QVector2D &sequence_res,
|
||||
const QVector2D &texture_res, const QVector2D &offset,
|
||||
AutoScaleType autoscale_type = k_auto_scale_none);
|
||||
|
||||
virtual void update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals) override;
|
||||
virtual QTransform
|
||||
gizmo_transformation(const NodeValueRow &row,
|
||||
const NodeGlobals &globals) const override;
|
||||
|
||||
static const QString k_parent_input;
|
||||
static const QString k_texture_input;
|
||||
static const QString k_autoscale_input;
|
||||
static const QString k_interpolation_input;
|
||||
|
||||
protected slots:
|
||||
virtual void gizmo_drag_start(const olive::NodeValueRow &row, double x,
|
||||
double y, const olive::Rational &time) override;
|
||||
|
||||
virtual void gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
private:
|
||||
static QPointF create_scale_point(double x, double y, const QPointF &half_res,
|
||||
const QMatrix4x4 &mat);
|
||||
|
||||
QMatrix4x4
|
||||
generate_auto_scaled_matrix(const QMatrix4x4 &generated_matrix,
|
||||
const NodeValueRow &db, const NodeGlobals &globals,
|
||||
const VideoParams &texture_params) const;
|
||||
|
||||
bool is_a_scale_gizmo(NodeGizmo *g) const;
|
||||
|
||||
// Gizmo variables
|
||||
double gizmo_start_angle_;
|
||||
QTransform gizmo_inverted_transform_;
|
||||
QPointF gizmo_anchor_pt_;
|
||||
bool gizmo_scale_uniform_;
|
||||
double gizmo_last_angle_;
|
||||
double gizmo_last_alt_angle_;
|
||||
int gizmo_rotate_wrap_;
|
||||
|
||||
enum RotationDirection {
|
||||
k_direction_none,
|
||||
k_direction_positive, // Clockwise
|
||||
k_direction_negative // Counter-clockwise
|
||||
};
|
||||
|
||||
static RotationDirection get_direction_from_angles(double last,
|
||||
double current);
|
||||
RotationDirection gizmo_rotate_last_dir_;
|
||||
RotationDirection gizmo_rotate_last_alt_dir_;
|
||||
|
||||
enum GizmoScaleType { k_gizmo_scale_x_only, k_gizmo_scale_y_only, k_gizmo_scale_both };
|
||||
|
||||
GizmoScaleType gizmo_scale_axes_;
|
||||
QVector2D gizmo_scale_anchor_;
|
||||
|
||||
// Gizmo on screen object storage
|
||||
PointGizmo *point_gizmo_[k_gizmo_scale_count];
|
||||
PointGizmo *anchor_gizmo_;
|
||||
PolygonGizmo *poly_gizmo_;
|
||||
ScreenGizmo *rotation_gizmo_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_TRANSFORMDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/wave/wavedistortnode.cpp
|
||||
node/distort/wave/wavedistortnode.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,106 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "wavedistortnode.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString WaveDistortNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString WaveDistortNode::k_frequency_input = QStringLiteral("frequency_in");
|
||||
const QString WaveDistortNode::k_intensity_input = QStringLiteral("intensity_in");
|
||||
const QString WaveDistortNode::k_evolution_input = QStringLiteral("evolution_in");
|
||||
const QString WaveDistortNode::k_vertical_input = QStringLiteral("vertical_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
WaveDistortNode::WaveDistortNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_frequency_input, NodeValue::k_float, 10);
|
||||
add_input(k_intensity_input, NodeValue::k_float, 10);
|
||||
add_input(k_evolution_input, NodeValue::k_float, 0);
|
||||
|
||||
add_input(k_vertical_input, NodeValue::k_combo, false);
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
}
|
||||
|
||||
QString WaveDistortNode::name() const
|
||||
{
|
||||
return tr("Wave");
|
||||
}
|
||||
|
||||
QString WaveDistortNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.wave");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> WaveDistortNode::category() const
|
||||
{
|
||||
return { k_category_distort };
|
||||
}
|
||||
|
||||
QString WaveDistortNode::description() const
|
||||
{
|
||||
return tr("Distorts an image along a sine wave.");
|
||||
}
|
||||
|
||||
void WaveDistortNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_frequency_input, tr("Frequency"));
|
||||
set_input_name(k_intensity_input, tr("Intensity"));
|
||||
set_input_name(k_evolution_input, tr("Evolution"));
|
||||
set_input_name(k_vertical_input, tr("Direction"));
|
||||
set_combo_box_strings(k_vertical_input, { tr("Horizontal"), tr("Vertical") });
|
||||
}
|
||||
|
||||
ShaderCode WaveDistortNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(FileFunctions::read_file_as_string(":/shaders/wave.frag"));
|
||||
}
|
||||
|
||||
void WaveDistortNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr texture = value[k_texture_input].to_texture()) {
|
||||
// Only run shader if at least one of flip or flop are selected
|
||||
if (!qIsNull(value[k_intensity_input].to_double())) {
|
||||
table->push(NodeValue::k_texture,
|
||||
Texture::job(texture->params(), ShaderJob(value)),
|
||||
this);
|
||||
} else {
|
||||
// If we're not flipping or flopping just push the texture
|
||||
table->push(value[k_texture_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_WAVEDISTORTNODE_H
|
||||
#define OAK_WAVEDISTORTNODE_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class WaveDistortNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaveDistortNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(WaveDistortNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_frequency_input;
|
||||
static const QString k_intensity_input;
|
||||
static const QString k_evolution_input;
|
||||
static const QString k_vertical_input;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_WAVEDISTORTNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(opacity)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/effect/opacity/opacityeffect.cpp
|
||||
node/effect/opacity/opacityeffect.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Oak Video Editor - Non-Linear Video Editor
|
||||
* Copyright (C) 2025 Olive CE Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "opacityeffect.h"
|
||||
|
||||
#include "node/math/math/math.h"
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super Node
|
||||
|
||||
const QString OpacityEffect::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString OpacityEffect::k_value_input = QStringLiteral("opacity_in");
|
||||
|
||||
OpacityEffect::OpacityEffect()
|
||||
{
|
||||
MathNode *math = new MathNode();
|
||||
math->setParent(this);
|
||||
|
||||
math->set_operation(MathNode::k_op_multiply);
|
||||
|
||||
set_node_position_in_context(math, QPointF(0, 0));
|
||||
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_value_input, NodeValue::k_float, 1.0);
|
||||
set_input_property(k_value_input, QStringLiteral("view"),
|
||||
slider::k_percentage);
|
||||
set_input_property(k_value_input, QStringLiteral("min"), 0.0);
|
||||
set_input_property(k_value_input, QStringLiteral("max"), 1.0);
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
}
|
||||
|
||||
void OpacityEffect::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Texture"));
|
||||
set_input_name(k_value_input, tr("Opacity"));
|
||||
}
|
||||
|
||||
ShaderCode OpacityEffect::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
if (request.id == QStringLiteral("rgbmult")) {
|
||||
return ShaderCode(
|
||||
FileFunctions::read_file_as_string(":/shaders/opacity_rgb.frag"));
|
||||
} else {
|
||||
return ShaderCode(
|
||||
FileFunctions::read_file_as_string(":/shaders/opacity.frag"));
|
||||
}
|
||||
}
|
||||
|
||||
void OpacityEffect::value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
if (TexturePtr opacity_tex = value[k_value_input].to_texture()) {
|
||||
ShaderJob job(value);
|
||||
job.set_shader_id(QStringLiteral("rgbmult"));
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
} else if (!qFuzzyCompare(value[k_value_input].to_double(), 1.0)) {
|
||||
table->push(NodeValue::k_texture, tex->to_job(ShaderJob(value)),
|
||||
this);
|
||||
} else {
|
||||
// 1.0 float is a no-op, so just push the texture
|
||||
table->push(value[k_texture_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Oak Video Editor - Non-Linear Video Editor
|
||||
* Copyright (C) 2025 Olive CE Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OAK_OPACITYEFFECT_H
|
||||
#define OAK_OPACITYEFFECT_H
|
||||
|
||||
#include "node/group/group.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class OpacityEffect : public Node {
|
||||
public:
|
||||
OpacityEffect();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(OpacityEffect)
|
||||
|
||||
virtual QString name() const override
|
||||
{
|
||||
return tr("Opacity");
|
||||
}
|
||||
|
||||
virtual QString id() const override
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.opacity");
|
||||
}
|
||||
|
||||
virtual QVector<CategoryID> category() const override
|
||||
{
|
||||
return { k_category_filter };
|
||||
}
|
||||
|
||||
virtual QString description() const override
|
||||
{
|
||||
return tr(
|
||||
"Alter a video's opacity.\n\nThis is equivalent to multiplying a video by a number between 0.0 and 1.0.");
|
||||
}
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_value_input;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_OPACITYEFFECT_H
|
||||
@@ -0,0 +1,306 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "factory.h"
|
||||
|
||||
#include <QHash>
|
||||
|
||||
#include "audio/pan/pan.h"
|
||||
#include "audio/volume/volume.h"
|
||||
#include "block/clip/clip.h"
|
||||
#include "block/gap/gap.h"
|
||||
#include "block/subtitle/subtitle.h"
|
||||
#include "block/transition/crossdissolve/crossdissolvetransition.h"
|
||||
#include "block/transition/diptocolor/diptocolortransition.h"
|
||||
#include "color/displaytransform/displaytransform.h"
|
||||
#include "color/ociogradingtransformlinear/ociogradingtransformlinear.h"
|
||||
#include "color/ociogradingtransformlog/ociogradingtransformlog.h"
|
||||
#include "color/ociolut/ociolut.h"
|
||||
#include "color/threewaycolor/threewaycolor.h"
|
||||
#include "color/whitebalance/whitebalance.h"
|
||||
#include "common/current.h"
|
||||
#include "distort/cornerpin/cornerpindistortnode.h"
|
||||
#include "distort/crop/cropdistortnode.h"
|
||||
#include "distort/flip/flipdistortnode.h"
|
||||
#include "distort/mask/mask.h"
|
||||
#include "distort/ripple/rippledistortnode.h"
|
||||
#include "distort/swirl/swirldistortnode.h"
|
||||
#include "distort/tile/tiledistortnode.h"
|
||||
#include "distort/transform/transformdistortnode.h"
|
||||
#include "distort/wave/wavedistortnode.h"
|
||||
#include "effect/opacity/opacityeffect.h"
|
||||
#include "filter/blur/blur.h"
|
||||
#include "filter/dropshadow/dropshadowfilter.h"
|
||||
#include "filter/mosaic/mosaicfilternode.h"
|
||||
#include "filter/stroke/stroke.h"
|
||||
#include "generator/matrix/matrix.h"
|
||||
#include "generator/noise/noise.h"
|
||||
#include "generator/polygon/polygon.h"
|
||||
#include "generator/shape/shapenode.h"
|
||||
#include "generator/solid/solid.h"
|
||||
#include "generator/text/textv1.h"
|
||||
#include "generator/text/textv2.h"
|
||||
#include "generator/text/textv3.h"
|
||||
#include "input/multicam/multicamnode.h"
|
||||
#include "input/time/timeinput.h"
|
||||
#include "input/value/valuenode.h"
|
||||
#include "keying/chromakey/chromakey.h"
|
||||
#include "keying/colordifferencekey/colordifferencekey.h"
|
||||
#include "keying/despill/despill.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 "pluginSupport/olivehost.h"
|
||||
#include "plugins/plugin.h"
|
||||
#include "project/folder/folder.h"
|
||||
#include "project/footage/footage.h"
|
||||
#include "project/sequence/sequence.h"
|
||||
#include "time/timeformat/timeformat.h"
|
||||
#include "time/timeoffset/timeoffsetnode.h"
|
||||
#include "time/timeremap/timeremap.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
QList<Node *> NodeFactory::library;
|
||||
|
||||
void NodeFactory::initialize()
|
||||
{
|
||||
destroy();
|
||||
|
||||
// Add internal types
|
||||
for (int i = 0; i < k_internal_node_count; i++) {
|
||||
Node *created_node = create_from_factory_index(static_cast<InternalID>(i));
|
||||
|
||||
library.append(created_node);
|
||||
}
|
||||
|
||||
register_plugin_nodes();
|
||||
}
|
||||
|
||||
void NodeFactory::destroy()
|
||||
{
|
||||
qDeleteAll(library);
|
||||
library.clear();
|
||||
}
|
||||
|
||||
QString NodeFactory::get_name_from_id(const QString &id)
|
||||
{
|
||||
if (!id.isEmpty()) {
|
||||
foreach (Node *n, library) {
|
||||
if (n->id() == id) {
|
||||
return n->name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
Node *NodeFactory::create_from_id(const QString &id)
|
||||
{
|
||||
QString resolved_id = id;
|
||||
|
||||
// Node IDs renamed after older project files were written
|
||||
static const QHash<QString, QString> k_legacy_i_ds = {
|
||||
{ QStringLiteral("org.oliveeditor.Olive.flip"),
|
||||
QStringLiteral("org.olivevideoeditor.Olive.flip") },
|
||||
{ QStringLiteral("org.oliveeditor.Olive.ripple"),
|
||||
QStringLiteral("org.olivevideoeditor.Olive.ripple") },
|
||||
{ QStringLiteral("org.oliveeditor.Olive.swirl"),
|
||||
QStringLiteral("org.olivevideoeditor.Olive.swirl") },
|
||||
{ QStringLiteral("org.oliveeditor.Olive.tile"),
|
||||
QStringLiteral("org.olivevideoeditor.Olive.tile") },
|
||||
{ QStringLiteral("org.oliveeditor.Olive.wave"),
|
||||
QStringLiteral("org.olivevideoeditor.Olive.wave") },
|
||||
};
|
||||
resolved_id = k_legacy_i_ds.value(id, id);
|
||||
|
||||
foreach (Node *n, library) {
|
||||
if (n->id() == resolved_id) {
|
||||
return n->copy();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NodeFactory::register_plugin_nodes()
|
||||
{
|
||||
QSet<QString> existing_ids;
|
||||
for (Node *node : library) {
|
||||
existing_ids.insert(node->id());
|
||||
}
|
||||
|
||||
for (auto plugin : OFX::Host::PluginCache::getPluginCache()->getPlugins()) {
|
||||
auto *image_effect =
|
||||
dynamic_cast<OFX::Host::ImageEffect::ImageEffectPlugin *>(plugin);
|
||||
if (!image_effect) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QString plugin_id =
|
||||
QString::fromStdString(image_effect->getIdentifier());
|
||||
if (existing_ids.contains(plugin_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &contexts = image_effect->getContexts();
|
||||
if (contexts.empty()) {
|
||||
qWarning() << "Skipping OFX plugin with no contexts:" << plugin_id;
|
||||
continue;
|
||||
}
|
||||
std::string context = kOfxImageEffectContextFilter;
|
||||
if (contexts.find(kOfxImageEffectContextFilter) == contexts.end()) {
|
||||
context = *contexts.begin();
|
||||
}
|
||||
|
||||
auto *instance = image_effect->createInstance(context, nullptr);
|
||||
if (!instance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
plugin::PluginNode *plugin_node = new plugin::PluginNode(instance);
|
||||
library.append(plugin_node);
|
||||
existing_ids.insert(plugin_id);
|
||||
}
|
||||
}
|
||||
|
||||
Node *NodeFactory::create_from_factory_index(const NodeFactory::InternalID &id)
|
||||
{
|
||||
switch (id) {
|
||||
case k_clip_block:
|
||||
return new ClipBlock();
|
||||
case k_gap_block:
|
||||
return new GapBlock();
|
||||
case k_polygon_generator:
|
||||
return new PolygonGenerator();
|
||||
case k_matrix_generator:
|
||||
return new MatrixGenerator();
|
||||
case k_transform_distort:
|
||||
return new TransformDistortNode();
|
||||
case k_track_output:
|
||||
return new Track();
|
||||
case k_viewer_output:
|
||||
return new ViewerOutput();
|
||||
case k_audio_volume:
|
||||
return new VolumeNode();
|
||||
case k_audio_panning:
|
||||
return new PanNode();
|
||||
case k_math:
|
||||
return new MathNode();
|
||||
case k_trigonometry:
|
||||
return new TrigonometryNode();
|
||||
case k_time:
|
||||
return new TimeInput();
|
||||
case k_blur_filter:
|
||||
return new BlurFilterNode();
|
||||
case k_solid_generator:
|
||||
return new SolidGenerator();
|
||||
case k_merge:
|
||||
return new MergeNode();
|
||||
case k_stroke_filter:
|
||||
return new StrokeFilterNode();
|
||||
case k_text_generator_v1:
|
||||
return new TextGeneratorV1();
|
||||
case k_text_generator_v2:
|
||||
return new TextGeneratorV2();
|
||||
case k_text_generator_v3:
|
||||
return new TextGeneratorV3();
|
||||
case k_cross_dissolve_transition:
|
||||
return new CrossDissolveTransition();
|
||||
case k_dip_to_color_transition:
|
||||
return new DipToColorTransition();
|
||||
case k_mosaic_filter:
|
||||
return new MosaicFilterNode();
|
||||
case k_crop_distort:
|
||||
return new CropDistortNode();
|
||||
case k_project_footage:
|
||||
return new Footage();
|
||||
case k_project_folder:
|
||||
return new Folder();
|
||||
case k_project_sequence:
|
||||
return new Sequence();
|
||||
case k_value_node:
|
||||
return new ValueNode();
|
||||
case k_time_remap_node:
|
||||
return new TimeRemapNode();
|
||||
case k_subtitle_block:
|
||||
return new SubtitleBlock();
|
||||
case k_shape_generator:
|
||||
return new ShapeNode();
|
||||
case k_color_difference_key_keying:
|
||||
return new ColorDifferenceKeyNode();
|
||||
case k_despill_keying:
|
||||
return new DespillNode();
|
||||
case k_group_node:
|
||||
return new NodeGroup();
|
||||
case k_opacity_effect:
|
||||
return new OpacityEffect();
|
||||
case k_flip_distort:
|
||||
return new FlipDistortNode();
|
||||
case k_noise_generator:
|
||||
return new NoiseGeneratorNode();
|
||||
case k_time_offset_node:
|
||||
return new TimeOffsetNode();
|
||||
case k_corner_pin_distort:
|
||||
return new CornerPinDistortNode();
|
||||
case k_display_transform:
|
||||
return new DisplayTransformNode();
|
||||
case k_ocio_grading_transform_linear:
|
||||
return new OCIOGradingTransformLinearNode();
|
||||
case k_ocio_grading_transform_log:
|
||||
return new OCIOGradingTransformLogNode();
|
||||
case k_white_balance:
|
||||
return new WhiteBalanceNode();
|
||||
case k_ocio_lut:
|
||||
return new OCIOLutNode();
|
||||
case k_three_way_color:
|
||||
return new ThreeWayColorNode();
|
||||
case k_chroma_key:
|
||||
return new ChromaKeyNode();
|
||||
case k_mask_distort:
|
||||
return new MaskDistortNode();
|
||||
case k_drop_shadow_filter:
|
||||
return new DropShadowFilter();
|
||||
case k_time_format:
|
||||
return new TimeFormatNode();
|
||||
case k_wave_distort:
|
||||
return new WaveDistortNode();
|
||||
case k_tile_distort:
|
||||
return new TileDistortNode();
|
||||
case k_swirl_distort:
|
||||
return new SwirlDistortNode();
|
||||
case k_ripple_distort:
|
||||
return new RippleDistortNode();
|
||||
case k_multicam_node:
|
||||
return new MultiCamNode();
|
||||
|
||||
case k_internal_node_count:
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_NODEFACTORY_H
|
||||
#define OAK_NODEFACTORY_H
|
||||
|
||||
#include <QList>
|
||||
|
||||
#include "node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class NodeFactory {
|
||||
public:
|
||||
enum InternalID {
|
||||
k_viewer_output,
|
||||
k_clip_block,
|
||||
k_gap_block,
|
||||
k_polygon_generator,
|
||||
k_matrix_generator,
|
||||
k_transform_distort,
|
||||
k_track_output,
|
||||
k_audio_volume,
|
||||
k_audio_panning,
|
||||
k_math,
|
||||
k_time,
|
||||
k_trigonometry,
|
||||
k_blur_filter,
|
||||
k_solid_generator,
|
||||
k_merge,
|
||||
k_stroke_filter,
|
||||
k_text_generator_v1,
|
||||
k_text_generator_v2,
|
||||
k_text_generator_v3,
|
||||
k_cross_dissolve_transition,
|
||||
k_dip_to_color_transition,
|
||||
k_mosaic_filter,
|
||||
k_crop_distort,
|
||||
k_project_footage,
|
||||
k_project_folder,
|
||||
k_project_sequence,
|
||||
k_value_node,
|
||||
k_time_remap_node,
|
||||
k_subtitle_block,
|
||||
k_shape_generator,
|
||||
k_color_difference_key_keying,
|
||||
k_despill_keying,
|
||||
k_group_node,
|
||||
k_opacity_effect,
|
||||
k_flip_distort,
|
||||
k_noise_generator,
|
||||
k_time_offset_node,
|
||||
k_corner_pin_distort,
|
||||
k_display_transform,
|
||||
k_ocio_grading_transform_linear,
|
||||
k_ocio_lut,
|
||||
k_three_way_color,
|
||||
k_chroma_key,
|
||||
k_mask_distort,
|
||||
k_drop_shadow_filter,
|
||||
k_time_format,
|
||||
k_wave_distort,
|
||||
k_ripple_distort,
|
||||
k_tile_distort,
|
||||
k_swirl_distort,
|
||||
k_multicam_node,
|
||||
k_ocio_grading_transform_log,
|
||||
k_white_balance,
|
||||
|
||||
// Count value
|
||||
k_internal_node_count
|
||||
};
|
||||
|
||||
NodeFactory() = default;
|
||||
|
||||
static void initialize();
|
||||
|
||||
static void destroy();
|
||||
|
||||
static QString get_name_from_id(const QString &id);
|
||||
|
||||
static Node *create_from_id(const QString &id);
|
||||
static void register_plugin_nodes();
|
||||
|
||||
static Node *create_from_factory_index(const InternalID &id);
|
||||
|
||||
/**
|
||||
* @brief Access the internal node library
|
||||
*
|
||||
* Exposed for the UI layer (e.g. widget/menu/factorymenu), which builds
|
||||
* node creation menus from the library. The library itself stays
|
||||
* UI-independent.
|
||||
*/
|
||||
static const QList<Node *> &get_library()
|
||||
{
|
||||
return library;
|
||||
}
|
||||
|
||||
private:
|
||||
static QList<Node *> library;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_NODEFACTORY_H
|
||||
@@ -0,0 +1,25 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(blur)
|
||||
add_subdirectory(dropshadow)
|
||||
add_subdirectory(mosaic)
|
||||
add_subdirectory(stroke)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/filter/blur/blur.h
|
||||
node/filter/blur/blur.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,236 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "blur.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString BlurFilterNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString BlurFilterNode::k_method_input = QStringLiteral("method_in");
|
||||
const QString BlurFilterNode::k_radius_input = QStringLiteral("radius_in");
|
||||
const QString BlurFilterNode::k_horiz_input = QStringLiteral("horiz_in");
|
||||
const QString BlurFilterNode::k_vert_input = QStringLiteral("vert_in");
|
||||
const QString BlurFilterNode::k_repeat_edge_pixels_input =
|
||||
QStringLiteral("repeat_edge_pixels_in");
|
||||
|
||||
const QString BlurFilterNode::k_directional_degrees_input =
|
||||
QStringLiteral("directional_degrees_in");
|
||||
|
||||
const QString BlurFilterNode::k_radial_center_input =
|
||||
QStringLiteral("radial_center_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
BlurFilterNode::BlurFilterNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
Method default_method = k_gaussian;
|
||||
|
||||
add_input(k_method_input, NodeValue::k_combo, default_method,
|
||||
InputFlags(k_input_flag_not_keyframable | k_input_flag_not_connectable));
|
||||
|
||||
add_input(k_radius_input, NodeValue::k_float, 10.0);
|
||||
set_input_property(k_radius_input, QStringLiteral("min"), 0.0);
|
||||
|
||||
{
|
||||
// Box and gaussian only
|
||||
add_input(k_horiz_input, NodeValue::k_boolean, true);
|
||||
add_input(k_vert_input, NodeValue::k_boolean, true);
|
||||
}
|
||||
|
||||
{
|
||||
// Directional only
|
||||
add_input(k_directional_degrees_input, NodeValue::k_float, 0.0);
|
||||
}
|
||||
|
||||
{
|
||||
// Radial only
|
||||
add_input(k_radial_center_input, NodeValue::k_vec2, QVector2D(0, 0));
|
||||
}
|
||||
|
||||
update_inputs(default_method);
|
||||
|
||||
add_input(k_repeat_edge_pixels_input, NodeValue::k_boolean, true);
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
|
||||
radial_center_gizmo_ = add_draggable_gizmo<PointGizmo>();
|
||||
radial_center_gizmo_->set_shape(PointGizmo::k_anchor_point);
|
||||
radial_center_gizmo_->add_input(
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_radial_center_input), 0));
|
||||
radial_center_gizmo_->add_input(
|
||||
NodeKeyframeTrackReference(NodeInput(this, k_radial_center_input), 1));
|
||||
}
|
||||
|
||||
QString BlurFilterNode::name() const
|
||||
{
|
||||
return tr("Blur");
|
||||
}
|
||||
|
||||
QString BlurFilterNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.blur");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> BlurFilterNode::category() const
|
||||
{
|
||||
return { k_category_filter };
|
||||
}
|
||||
|
||||
QString BlurFilterNode::description() const
|
||||
{
|
||||
return tr("Blurs an image.");
|
||||
}
|
||||
|
||||
void BlurFilterNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_method_input, tr("Method"));
|
||||
set_combo_box_strings(k_method_input, { tr("Box"), tr("Gaussian"),
|
||||
tr("Directional"), tr("Radial") });
|
||||
set_input_name(k_radius_input, tr("Radius"));
|
||||
set_input_name(k_horiz_input, tr("Horizontal"));
|
||||
set_input_name(k_vert_input, tr("Vertical"));
|
||||
set_input_name(k_repeat_edge_pixels_input, tr("Repeat Edge Pixels"));
|
||||
|
||||
set_input_name(k_directional_degrees_input, tr("Direction"));
|
||||
set_input_name(k_radial_center_input, tr("Center"));
|
||||
}
|
||||
|
||||
ShaderCode BlurFilterNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(FileFunctions::read_file_as_string(":/shaders/blur.frag"));
|
||||
}
|
||||
|
||||
void BlurFilterNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
// If there's no texture, no need to run an operation
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
Method method = static_cast<Method>(value[k_method_input].to_int());
|
||||
|
||||
bool can_push_job = true;
|
||||
int iterations = 1;
|
||||
|
||||
// Check if radius is > 0
|
||||
if (value[k_radius_input].to_double() > 0.0) {
|
||||
// Method-specific considerations
|
||||
switch (method) {
|
||||
case k_box:
|
||||
case k_gaussian: {
|
||||
bool horiz = value[k_horiz_input].to_bool();
|
||||
bool vert = value[k_vert_input].to_bool();
|
||||
|
||||
if (!horiz && !vert) {
|
||||
// Disable job if horiz and vert are unchecked
|
||||
can_push_job = false;
|
||||
} else if (horiz && vert) {
|
||||
// Set iteration count to 2 if we're blurring both horizontally and vertically
|
||||
iterations = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case k_directional:
|
||||
case k_radial:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
can_push_job = false;
|
||||
}
|
||||
|
||||
if (can_push_job) {
|
||||
ShaderJob job(value);
|
||||
job.insert(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::k_vec2, tex->virtual_resolution(),
|
||||
this));
|
||||
job.set_iterations(iterations, k_texture_input);
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
} else {
|
||||
// If we're not performing the blur job, just push the texture
|
||||
table->push(value[k_texture_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlurFilterNode::update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals)
|
||||
{
|
||||
if (TexturePtr tex = row[k_texture_input].to_texture()) {
|
||||
if (row[k_method_input].to_int() == k_radial) {
|
||||
const QVector2D &sequence_res = tex->virtual_resolution();
|
||||
QVector2D sequence_half_res = sequence_res * 0.5;
|
||||
|
||||
radial_center_gizmo_->set_visible(true);
|
||||
radial_center_gizmo_->set_point(
|
||||
sequence_half_res.toPointF() +
|
||||
row[k_radial_center_input].to_vec2().toPointF());
|
||||
|
||||
set_input_property(k_radial_center_input, QStringLiteral("offset"),
|
||||
sequence_half_res);
|
||||
} else {
|
||||
radial_center_gizmo_->set_visible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlurFilterNode::gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo *>(sender());
|
||||
|
||||
if (gizmo == radial_center_gizmo_) {
|
||||
NodeInputDragger &x_drag = gizmo->get_draggers()[0];
|
||||
NodeInputDragger &y_drag = gizmo->get_draggers()[1];
|
||||
|
||||
x_drag.drag(x_drag.get_start_value().toDouble() + x);
|
||||
y_drag.drag(y_drag.get_start_value().toDouble() + y);
|
||||
}
|
||||
}
|
||||
|
||||
void BlurFilterNode::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
if (input == k_method_input) {
|
||||
update_inputs(get_method());
|
||||
}
|
||||
|
||||
super::InputValueChangedEvent(input, element);
|
||||
}
|
||||
|
||||
void BlurFilterNode::update_inputs(Method method)
|
||||
{
|
||||
set_input_flag(k_horiz_input, k_input_flag_hidden,
|
||||
!(method == k_box || method == k_gaussian));
|
||||
set_input_flag(k_vert_input, k_input_flag_hidden,
|
||||
!(method == k_box || method == k_gaussian));
|
||||
set_input_flag(k_directional_degrees_input, k_input_flag_hidden,
|
||||
!(method == k_directional));
|
||||
set_input_flag(k_radial_center_input, k_input_flag_hidden, !(method == k_radial));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_BLURFILTERNODE_H
|
||||
#define OAK_BLURFILTERNODE_H
|
||||
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class BlurFilterNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
BlurFilterNode();
|
||||
|
||||
enum Method { k_box, k_gaussian, k_directional, k_radial };
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(BlurFilterNode)
|
||||
|
||||
virtual QString name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> category() const override;
|
||||
virtual QString description() const override;
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
Method get_method() const
|
||||
{
|
||||
return static_cast<Method>(get_standard_value(k_method_input).toInt());
|
||||
}
|
||||
|
||||
virtual void update_gizmo_positions(const NodeValueRow &row,
|
||||
const NodeGlobals &globals) override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_method_input;
|
||||
static const QString k_radius_input;
|
||||
static const QString k_horiz_input;
|
||||
static const QString k_vert_input;
|
||||
static const QString k_repeat_edge_pixels_input;
|
||||
|
||||
static const QString k_directional_degrees_input;
|
||||
|
||||
static const QString k_radial_center_input;
|
||||
|
||||
protected slots:
|
||||
virtual void gizmo_drag_move(double x, double y,
|
||||
const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
protected:
|
||||
virtual void InputValueChangedEvent(const QString &input,
|
||||
int element) override;
|
||||
|
||||
private:
|
||||
void update_inputs(Method method);
|
||||
|
||||
PointGizmo *radial_center_gizmo_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_BLURFILTERNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/filter/dropshadow/dropshadowfilter.h
|
||||
node/filter/dropshadow/dropshadowfilter.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,107 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "dropshadowfilter.h"
|
||||
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super Node
|
||||
|
||||
const QString DropShadowFilter::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString DropShadowFilter::k_color_input = QStringLiteral("color_in");
|
||||
const QString DropShadowFilter::k_distance_input = QStringLiteral("distance_in");
|
||||
const QString DropShadowFilter::k_angle_input = QStringLiteral("angle_in");
|
||||
const QString DropShadowFilter::k_softness_input = QStringLiteral("radius_in");
|
||||
const QString DropShadowFilter::k_opacity_input = QStringLiteral("opacity_in");
|
||||
const QString DropShadowFilter::k_fast_input = QStringLiteral("fast_in");
|
||||
|
||||
DropShadowFilter::DropShadowFilter()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_color_input, NodeValue::k_color,
|
||||
QVariant::fromValue(Color(0.0, 0.0, 0.0)));
|
||||
|
||||
add_input(k_distance_input, NodeValue::k_float, 10.0);
|
||||
|
||||
add_input(k_angle_input, NodeValue::k_float, 135.0);
|
||||
|
||||
add_input(k_softness_input, NodeValue::k_float, 10.0);
|
||||
set_input_property(k_softness_input, QStringLiteral("min"), 0.0);
|
||||
|
||||
add_input(k_opacity_input, NodeValue::k_float, 1.0);
|
||||
set_input_property(k_opacity_input, QStringLiteral("min"), 0.0);
|
||||
set_input_property(k_opacity_input, QStringLiteral("view"),
|
||||
slider::k_percentage);
|
||||
|
||||
add_input(k_fast_input, NodeValue::k_boolean, false);
|
||||
|
||||
set_effect_input(k_texture_input);
|
||||
set_flag(k_video_effect);
|
||||
}
|
||||
|
||||
void DropShadowFilter::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Texture"));
|
||||
set_input_name(k_color_input, tr("Color"));
|
||||
set_input_name(k_distance_input, tr("Distance"));
|
||||
set_input_name(k_angle_input, tr("Angle"));
|
||||
set_input_name(k_softness_input, tr("Softness"));
|
||||
set_input_name(k_opacity_input, tr("Opacity"));
|
||||
set_input_name(k_fast_input, tr("Faster (Lower Quality)"));
|
||||
}
|
||||
|
||||
ShaderCode DropShadowFilter::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return ShaderCode(
|
||||
FileFunctions::read_file_as_string(":/shaders/dropshadow.frag"));
|
||||
}
|
||||
|
||||
void DropShadowFilter::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
ShaderJob job(value);
|
||||
|
||||
QString iterative = QStringLiteral("previous_iteration_in");
|
||||
|
||||
job.insert(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::k_vec2, tex->virtual_resolution(),
|
||||
this));
|
||||
job.insert(iterative, value[k_texture_input]);
|
||||
|
||||
if (!qIsNull(value[k_softness_input].to_double())) {
|
||||
job.set_iterations(3, iterative);
|
||||
}
|
||||
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_DROPSHADOWFILTER_H
|
||||
#define OAK_DROPSHADOWFILTER_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class DropShadowFilter : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DropShadowFilter();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(DropShadowFilter)
|
||||
|
||||
virtual QString name() const override
|
||||
{
|
||||
return tr("Drop Shadow");
|
||||
}
|
||||
virtual QString id() const override
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.dropshadow");
|
||||
}
|
||||
virtual QVector<CategoryID> category() const override
|
||||
{
|
||||
return { k_category_filter };
|
||||
}
|
||||
virtual QString description() const override
|
||||
{
|
||||
return tr("Adds a drop shadow to an image.");
|
||||
}
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_color_input;
|
||||
static const QString k_distance_input;
|
||||
static const QString k_angle_input;
|
||||
static const QString k_softness_input;
|
||||
static const QString k_opacity_input;
|
||||
static const QString k_fast_input;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_DROPSHADOWFILTER_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/filter/mosaic/mosaicfilternode.h
|
||||
node/filter/mosaic/mosaicfilternode.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,83 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "mosaicfilternode.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString MosaicFilterNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString MosaicFilterNode::k_horiz_input = QStringLiteral("horiz_in");
|
||||
const QString MosaicFilterNode::k_vert_input = QStringLiteral("vert_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
MosaicFilterNode::MosaicFilterNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_horiz_input, NodeValue::k_float, 32.0);
|
||||
set_input_property(k_horiz_input, QStringLiteral("min"), 1.0);
|
||||
|
||||
add_input(k_vert_input, NodeValue::k_float, 18.0);
|
||||
set_input_property(k_vert_input, QStringLiteral("min"), 1.0);
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
}
|
||||
|
||||
void MosaicFilterNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Texture"));
|
||||
set_input_name(k_horiz_input, tr("Horizontal"));
|
||||
set_input_name(k_vert_input, tr("Vertical"));
|
||||
}
|
||||
|
||||
void MosaicFilterNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
if (TexturePtr texture = value[k_texture_input].to_texture()) {
|
||||
if (texture && (value[k_horiz_input].to_int() != texture->width() ||
|
||||
value[k_vert_input].to_int() != texture->height())) {
|
||||
ShaderJob job(value);
|
||||
|
||||
// Mipmapping makes this look weird, so we just use bilinear for finding the color of each block
|
||||
job.set_interpolation(k_texture_input, Texture::k_linear);
|
||||
|
||||
table->push(NodeValue::k_texture, texture->to_job(job), this);
|
||||
} else {
|
||||
table->push(value[k_texture_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShaderCode MosaicFilterNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
|
||||
return ShaderCode(FileFunctions::read_file_as_string(":/shaders/mosaic.frag"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_MOSAICFILTERNODE_H
|
||||
#define OAK_MOSAICFILTERNODE_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class MosaicFilterNode : public Node {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MosaicFilterNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(MosaicFilterNode)
|
||||
|
||||
virtual QString name() const override
|
||||
{
|
||||
return tr("Mosaic");
|
||||
}
|
||||
|
||||
virtual QString id() const override
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.mosaicfilter");
|
||||
}
|
||||
|
||||
virtual QVector<CategoryID> category() const override
|
||||
{
|
||||
return { k_category_filter };
|
||||
}
|
||||
|
||||
virtual QString description() const override
|
||||
{
|
||||
return tr("Apply a pixelated mosaic filter to video.");
|
||||
}
|
||||
|
||||
virtual void retranslate() override;
|
||||
|
||||
virtual void value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const override;
|
||||
virtual ShaderCode
|
||||
get_shader_code(const ShaderRequest &request) const override;
|
||||
|
||||
static const QString k_texture_input;
|
||||
static const QString k_horiz_input;
|
||||
static const QString k_vert_input;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_MOSAICFILTERNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/filter/stroke/stroke.h
|
||||
node/filter/stroke/stroke.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,116 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "stroke.h"
|
||||
|
||||
#include "node/sliderdisplaytype.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QString StrokeFilterNode::k_texture_input = QStringLiteral("tex_in");
|
||||
const QString StrokeFilterNode::k_color_input = QStringLiteral("color_in");
|
||||
const QString StrokeFilterNode::k_radius_input = QStringLiteral("radius_in");
|
||||
const QString StrokeFilterNode::k_opacity_input = QStringLiteral("opacity_in");
|
||||
const QString StrokeFilterNode::k_inner_input = QStringLiteral("inner_in");
|
||||
|
||||
#define super Node
|
||||
|
||||
StrokeFilterNode::StrokeFilterNode()
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
|
||||
add_input(k_color_input, NodeValue::k_color,
|
||||
QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f)));
|
||||
|
||||
add_input(k_radius_input, NodeValue::k_float, 10.0);
|
||||
set_input_property(k_radius_input, QStringLiteral("min"), 0.0);
|
||||
|
||||
add_input(k_opacity_input, NodeValue::k_float, 1.0f);
|
||||
set_input_property(k_opacity_input, QStringLiteral("view"),
|
||||
slider::k_percentage);
|
||||
set_input_property(k_opacity_input, QStringLiteral("min"), 0.0f);
|
||||
set_input_property(k_opacity_input, QStringLiteral("max"), 1.0f);
|
||||
|
||||
add_input(k_inner_input, NodeValue::k_boolean, false);
|
||||
|
||||
set_flag(k_video_effect);
|
||||
set_effect_input(k_texture_input);
|
||||
}
|
||||
|
||||
QString StrokeFilterNode::name() const
|
||||
{
|
||||
return tr("Stroke");
|
||||
}
|
||||
|
||||
QString StrokeFilterNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.stroke");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> StrokeFilterNode::category() const
|
||||
{
|
||||
return { k_category_filter };
|
||||
}
|
||||
|
||||
QString StrokeFilterNode::description() const
|
||||
{
|
||||
return tr("Creates a stroke outline around an image.");
|
||||
}
|
||||
|
||||
void StrokeFilterNode::retranslate()
|
||||
{
|
||||
super::retranslate();
|
||||
|
||||
set_input_name(k_texture_input, tr("Input"));
|
||||
set_input_name(k_color_input, tr("Color"));
|
||||
set_input_name(k_radius_input, tr("Radius"));
|
||||
set_input_name(k_opacity_input, tr("Opacity"));
|
||||
set_input_name(k_inner_input, tr("Inner"));
|
||||
}
|
||||
|
||||
void StrokeFilterNode::value(const NodeValueRow &value,
|
||||
const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
if (TexturePtr tex = value[k_texture_input].to_texture()) {
|
||||
if (value[k_radius_input].to_double() > 0.0 &&
|
||||
value[k_opacity_input].to_double() > 0.0) {
|
||||
ShaderJob job(value);
|
||||
job.insert(QStringLiteral("resolution_in"),
|
||||
NodeValue(NodeValue::k_vec2, tex->virtual_resolution(),
|
||||
this));
|
||||
table->push(NodeValue::k_texture, tex->to_job(job), this);
|
||||
} else {
|
||||
table->push(value[k_texture_input]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShaderCode StrokeFilterNode::get_shader_code(const ShaderRequest &request) const
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
|
||||
return ShaderCode(FileFunctions::read_file_as_string(":/shaders/stroke.frag"));
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user