Merge pull request #1875 from ThomasWilshaw/ocio_node
Initial OCIO node implementation
This commit is contained in:
@@ -15,6 +15,9 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(colormanager)
|
||||
add_subdirectory(displaytransform)
|
||||
add_subdirectory(ociobase)
|
||||
add_subdirectory(ociogradingtransformlinear)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
|
||||
@@ -273,6 +273,7 @@ void ColorManager::InputValueChangedEvent(const QString &input, int element)
|
||||
|
||||
try {
|
||||
SetConfig(OCIO::Config::CreateFromFile(GetConfigFilename().toUtf8()));
|
||||
emit ConfigChanged();
|
||||
} catch (OCIO::Exception&) {}
|
||||
|
||||
}
|
||||
|
||||
@@ -121,6 +121,9 @@ public:
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
signals:
|
||||
void ConfigChanged();
|
||||
|
||||
protected:
|
||||
virtual void InputValueChangedEvent(const QString &input, int element) override;
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 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,144 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "displaytransform.h"
|
||||
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString DisplayTransformNode::kDisplayInput = QStringLiteral("display_in");
|
||||
const QString DisplayTransformNode::kViewInput = QStringLiteral("view_in");
|
||||
const QString DisplayTransformNode::kDirectionInput = QStringLiteral("dir_in");
|
||||
|
||||
#define super OCIOBaseNode
|
||||
|
||||
DisplayTransformNode::DisplayTransformNode()
|
||||
{
|
||||
AddInput(kDisplayInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
|
||||
|
||||
AddInput(kViewInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
|
||||
|
||||
AddInput(kDirectionInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
|
||||
}
|
||||
|
||||
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 {kCategoryColor};
|
||||
}
|
||||
|
||||
QString DisplayTransformNode::Description() const
|
||||
{
|
||||
return tr("Converts an image to or from a display color space.");
|
||||
}
|
||||
|
||||
void DisplayTransformNode::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
SetInputName(kDisplayInput, tr("Display"));
|
||||
SetInputName(kViewInput, tr("View"));
|
||||
SetInputName(kDirectionInput, tr("Direction"));
|
||||
SetComboBoxStrings(kDirectionInput, {tr("Forward"), tr("Inverse")});
|
||||
}
|
||||
|
||||
void DisplayTransformNode::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
Q_UNUSED(element);
|
||||
if (input == kDisplayInput || input == kDirectionInput || input == kViewInput) {
|
||||
if (input == kDisplayInput) {
|
||||
UpdateViews();
|
||||
}
|
||||
GenerateProcessor();
|
||||
}
|
||||
}
|
||||
|
||||
QString DisplayTransformNode::GetDisplay() const
|
||||
{
|
||||
if (manager()) {
|
||||
int index = GetStandardValue(kDisplayInput).toInt();
|
||||
if (index < manager()->ListAvailableDisplays().size()) {
|
||||
return manager()->ListAvailableDisplays().at(index);
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString DisplayTransformNode::GetView() const
|
||||
{
|
||||
if (manager()) {
|
||||
QString display = GetDisplay();
|
||||
if (!display.isEmpty()) {
|
||||
int index = GetStandardValue(kViewInput).toInt();
|
||||
QStringList views = manager()->ListAvailableViews(display);
|
||||
if (index < views.size()) {
|
||||
return views.at(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
ColorProcessor::Direction DisplayTransformNode::GetDirection() const
|
||||
{
|
||||
return static_cast<ColorProcessor::Direction>(GetStandardValue(kDirectionInput).toInt());;
|
||||
}
|
||||
|
||||
void DisplayTransformNode::UpdateDisplays()
|
||||
{
|
||||
if (manager()) {
|
||||
SetComboBoxStrings(kDisplayInput, manager()->ListAvailableDisplays());
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayTransformNode::UpdateViews()
|
||||
{
|
||||
if (manager()) {
|
||||
SetComboBoxStrings(kViewInput, manager()->ListAvailableViews(GetDisplay()));
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayTransformNode::ConfigChanged()
|
||||
{
|
||||
UpdateDisplays();
|
||||
UpdateViews();
|
||||
GenerateProcessor();
|
||||
}
|
||||
|
||||
void DisplayTransformNode::GenerateProcessor()
|
||||
{
|
||||
if (manager()) {
|
||||
ColorTransform transform(GetDisplay(), GetView(), QString());
|
||||
set_processor(ColorProcessor::Create(manager(), manager()->GetReferenceColorSpace(), transform, GetDirection()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef DISPLAYTRANSFORMNODE_H
|
||||
#define 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 GetDisplay() const;
|
||||
QString GetView() const;
|
||||
ColorProcessor::Direction GetDirection() const;
|
||||
|
||||
static const QString kDisplayInput;
|
||||
static const QString kViewInput;
|
||||
static const QString kDirectionInput;
|
||||
|
||||
protected slots:
|
||||
virtual void ConfigChanged() override;
|
||||
|
||||
private:
|
||||
void GenerateProcessor();
|
||||
|
||||
void UpdateDisplays();
|
||||
|
||||
void UpdateViews();
|
||||
|
||||
};
|
||||
|
||||
} // olive
|
||||
|
||||
#endif // DISPLAYTRANSFORMNODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 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,69 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "ociobase.h"
|
||||
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "node/project/project.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString OCIOBaseNode::kTextureInput = QStringLiteral("tex_in");
|
||||
|
||||
OCIOBaseNode::OCIOBaseNode() :
|
||||
manager_(nullptr),
|
||||
processor_(nullptr)
|
||||
{
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
SetEffectInput(kTextureInput);
|
||||
|
||||
connect(this, &Node::AddedToGraph, this, &OCIOBaseNode::ParentChanged);
|
||||
|
||||
SetFlags(kVideoEffect);
|
||||
}
|
||||
|
||||
void OCIOBaseNode::ParentChanged(NodeGraph *graph)
|
||||
{
|
||||
if (manager_) {
|
||||
disconnect(manager_, &ColorManager::ConfigChanged, this, &OCIOBaseNode::ConfigChanged);
|
||||
manager_ = nullptr;
|
||||
}
|
||||
|
||||
if (Project *p = dynamic_cast<Project*>(graph)) {
|
||||
manager_ = p->color_manager();
|
||||
connect(manager_, &ColorManager::ConfigChanged, this, &OCIOBaseNode::ConfigChanged);
|
||||
ConfigChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOBaseNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
if (!value[kTextureInput].data().isNull() && processor_) {
|
||||
ColorTransformJob job;
|
||||
|
||||
job.SetColorProcessor(processor_);
|
||||
job.SetInputTexture(value[kTextureInput].data().value<TexturePtr>());
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OCIOBASENODE_H
|
||||
#define OCIOBASENODE_H
|
||||
|
||||
#include "node/node.h"
|
||||
#include "render/job/colortransformjob.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class OCIOBaseNode : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OCIOBaseNode();
|
||||
|
||||
virtual void Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
|
||||
protected slots:
|
||||
virtual void ConfigChanged() = 0;
|
||||
|
||||
protected:
|
||||
ColorManager *manager() const { return manager_; }
|
||||
|
||||
ColorProcessorPtr processor() const { return processor_; }
|
||||
void set_processor(ColorProcessorPtr p) { processor_ = p; }
|
||||
|
||||
private:
|
||||
ColorManager *manager_;
|
||||
|
||||
ColorProcessorPtr processor_;
|
||||
|
||||
private slots:
|
||||
void ParentChanged(olive::NodeGraph *graph);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OCIOBASENODE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 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,218 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "ociogradingtransformlinear.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "common/ocioutils.h"
|
||||
#include "node/project/project.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString OCIOGradingTransformLinearNode::kContrastInput = QStringLiteral("ocio_grading_primary_contrast");
|
||||
const QString OCIOGradingTransformLinearNode::kOffsetInput = QStringLiteral("ocio_grading_primary_offset");
|
||||
const QString OCIOGradingTransformLinearNode::kExposureInput = QStringLiteral("ocio_grading_primary_exposure");
|
||||
const QString OCIOGradingTransformLinearNode::kSaturationInput = QStringLiteral("ocio_grading_primary_saturation");
|
||||
const QString OCIOGradingTransformLinearNode::kPivotInput = QStringLiteral("ocio_grading_primary_pivot");
|
||||
const QString OCIOGradingTransformLinearNode::kClampBlackEnableInput = QStringLiteral("clamp_black_enable_in");
|
||||
const QString OCIOGradingTransformLinearNode::kClampBlackInput = QStringLiteral("ocio_grading_primary_clampBlack");
|
||||
const QString OCIOGradingTransformLinearNode::kClampWhiteEnableInput = QStringLiteral("clamp_white_enable_in");
|
||||
const QString OCIOGradingTransformLinearNode::kClampWhiteInput = QStringLiteral("ocio_grading_primary_clampWhite");
|
||||
|
||||
#define super OCIOBaseNode
|
||||
|
||||
OCIOGradingTransformLinearNode::OCIOGradingTransformLinearNode()
|
||||
{
|
||||
AddInput(kContrastInput, NodeValue::kVec4, QVector4D{1.0, 1.0, 1.0, 1.0});
|
||||
// Minimum based on OCIO::GradingPrimary::validate
|
||||
SetInputProperty(kContrastInput, QStringLiteral("min"), QVector4D{0.01f, 0.01f, 0.01f, 0.01f});
|
||||
SetInputProperty(kContrastInput, QStringLiteral("base"), 0.01);
|
||||
SetVec4InputColors(kContrastInput);
|
||||
|
||||
AddInput(kOffsetInput, NodeValue::kVec4, QVector4D{0.0, 0.0, 0.0, 0.0});
|
||||
SetInputProperty(kOffsetInput, QStringLiteral("base"), 0.01);
|
||||
SetVec4InputColors(kOffsetInput);
|
||||
|
||||
AddInput(kExposureInput, NodeValue::kVec4, QVector4D{0.0, 0.0, 0.0, 0.0});
|
||||
SetInputProperty(kExposureInput, QStringLiteral("base"), 0.01);
|
||||
SetVec4InputColors(kExposureInput);
|
||||
|
||||
AddInput(kSaturationInput, NodeValue::kFloat, 1.0);
|
||||
SetInputProperty(kSaturationInput, QStringLiteral("view"), FloatSlider::kPercentage);
|
||||
SetInputProperty(kSaturationInput, QStringLiteral("min"), 0.0);
|
||||
|
||||
AddInput(kPivotInput, NodeValue::kFloat, 0.18); // Default listed in OCIO::GradingPrimary
|
||||
SetInputProperty(kPivotInput, QStringLiteral("base"), 0.01);
|
||||
|
||||
AddInput(kClampBlackEnableInput, NodeValue::kBoolean, false);
|
||||
|
||||
AddInput(kClampBlackInput, NodeValue::kFloat, 0.0);
|
||||
SetInputProperty(kClampBlackInput, QStringLiteral("enabled"), GetStandardValue(kClampBlackEnableInput).toBool());
|
||||
SetInputProperty(kClampBlackInput, QStringLiteral("base"), 0.01);
|
||||
|
||||
AddInput(kClampWhiteEnableInput, NodeValue::kBoolean, false);
|
||||
|
||||
AddInput(kClampWhiteInput, NodeValue::kFloat, 1.0);
|
||||
SetInputProperty(kClampWhiteInput, QStringLiteral("enabled"), GetStandardValue(kClampWhiteEnableInput).toBool());
|
||||
SetInputProperty(kClampWhiteInput, QStringLiteral("base"), 0.01);
|
||||
|
||||
// FIXME: Temporarily disabled. This will break if "clamp black" is keyframed or connected to
|
||||
// something and there's currently no solution to remedy that. If there is in the future,
|
||||
// we can look into re-enabling this.
|
||||
//SetInputProperty(kClampWhiteInput, QStringLiteral("min"), GetStandardValue(kClampBlackInput).toDouble() + 0.000001);
|
||||
}
|
||||
|
||||
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 {kCategoryColor};
|
||||
}
|
||||
|
||||
QString OCIOGradingTransformLinearNode::Description() const
|
||||
{
|
||||
return tr("Simple linear color grading using OpenColorIO.");
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
SetInputName(kContrastInput, tr("Contrast"));
|
||||
SetInputName(kOffsetInput, tr("Offset"));
|
||||
SetInputName(kExposureInput, tr("Exposure"));
|
||||
SetInputProperty(kExposureInput, QStringLiteral("tooltip"), tr("Exposure increments in stops."));
|
||||
SetInputName(kSaturationInput, tr("Saturation"));
|
||||
SetInputName(kPivotInput, tr("Pivot"));
|
||||
SetInputName(kClampBlackEnableInput, tr("Enable Black Clamp"));
|
||||
SetInputName(kClampBlackInput, tr("Black Clamp"));
|
||||
SetInputName(kClampWhiteEnableInput, tr("Enable White Clamp"));
|
||||
SetInputName(kClampWhiteInput, tr("White Clamp"));
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
Q_UNUSED(element);
|
||||
|
||||
if (input == kClampWhiteEnableInput) {
|
||||
SetInputProperty(kClampWhiteInput, QStringLiteral("enabled"), GetStandardValue(kClampWhiteEnableInput).toBool());
|
||||
} else if (input == kClampBlackEnableInput) {
|
||||
SetInputProperty(kClampBlackInput, QStringLiteral("enabled"), GetStandardValue(kClampBlackEnableInput).toBool());
|
||||
} else if (input == kClampBlackInput) {
|
||||
// Ensure the white clamp is always greater than the black clamp as per OCIO::GradingPrimary::validate
|
||||
// FIXME: Temporarily disabled. This will break if "clamp black" is keyframed or connected to
|
||||
// something and there's currently no solution to remedy that. If there is in the future,
|
||||
// we can look into re-enabling this.
|
||||
//SetInputProperty(kClampWhiteInput, QStringLiteral("min"), GetStandardValue(kClampBlackInput).toDouble() + 0.000001);
|
||||
}
|
||||
|
||||
GenerateProcessor();
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::GenerateProcessor()
|
||||
{
|
||||
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()->GetConfig()->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 (!value[kTextureInput].data().isNull() && processor()) {
|
||||
ColorTransformJob job;
|
||||
|
||||
job.SetColorProcessor(processor());
|
||||
job.SetInputTexture(value[kTextureInput].data().value<TexturePtr>());
|
||||
|
||||
job.InsertValue(value);
|
||||
|
||||
const int MASTER_CHANNEL = 0;
|
||||
const int RED_CHANNEL = 1;
|
||||
const int GREEN_CHANNEL = 2;
|
||||
const int BLUE_CHANNEL = 3;
|
||||
|
||||
// Oddly, OCIO uses RGBMs when setting the GradingPrimary on the CPU, but uses vec3s on the GPU.
|
||||
// Even more oddly, the conversion from RGBM to vec3 does not appear to have a public API.
|
||||
// Therefore, this code has been duplicated from OCIO here:
|
||||
// https://github.com/AcademySoftwareFoundation/OpenColorIO/blob/3abbe5b20521169580fcfe3692aca81859859953/src/OpenColorIO/ops/gradingprimary/GradingPrimary.cpp#L157
|
||||
QVector4D offset = value[kOffsetInput].value<QVector4D>();
|
||||
offset[RED_CHANNEL] += offset[MASTER_CHANNEL];
|
||||
offset[GREEN_CHANNEL] += offset[MASTER_CHANNEL];
|
||||
offset[BLUE_CHANNEL] += offset[MASTER_CHANNEL];
|
||||
job.InsertValue(kOffsetInput, NodeValue(NodeValue::kVec3, QVector3D(offset[RED_CHANNEL], offset[GREEN_CHANNEL], offset[BLUE_CHANNEL])));
|
||||
|
||||
QVector4D exposure = value[kExposureInput].value<QVector4D>();
|
||||
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.InsertValue(kExposureInput, NodeValue(NodeValue::kVec3, QVector3D(exposure[RED_CHANNEL], exposure[GREEN_CHANNEL], exposure[BLUE_CHANNEL])));
|
||||
|
||||
QVector4D contrast = value[kContrastInput].value<QVector4D>();
|
||||
contrast[RED_CHANNEL] *= contrast[MASTER_CHANNEL];
|
||||
contrast[GREEN_CHANNEL] *= contrast[MASTER_CHANNEL];
|
||||
contrast[BLUE_CHANNEL] *= contrast[MASTER_CHANNEL];
|
||||
job.InsertValue(kContrastInput, NodeValue(NodeValue::kVec3, QVector3D(contrast[RED_CHANNEL], contrast[GREEN_CHANNEL], contrast[BLUE_CHANNEL])));
|
||||
|
||||
if (!value[kClampBlackEnableInput].data().toBool()) {
|
||||
job.InsertValue(kClampBlackInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampBlack()));
|
||||
}
|
||||
|
||||
if (!value[kClampWhiteEnableInput].data().toBool()) {
|
||||
job.InsertValue(kClampWhiteInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampWhite()));
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::ConfigChanged()
|
||||
{
|
||||
GenerateProcessor();
|
||||
}
|
||||
|
||||
void OCIOGradingTransformLinearNode::SetVec4InputColors(const QString &input)
|
||||
{
|
||||
SetInputProperty(input, QStringLiteral("color0"), QColor(192, 192, 192).name());
|
||||
SetInputProperty(input, QStringLiteral("color1"), QColor(255, 0, 0).name());
|
||||
SetInputProperty(input, QStringLiteral("color2"), QColor(0, 255, 0).name());
|
||||
SetInputProperty(input, QStringLiteral("color3"), QColor(0, 0, 255).name());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OCIOGRADINGTRANSFORMLINEARNODE_H
|
||||
#define 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;
|
||||
void GenerateProcessor();
|
||||
|
||||
virtual void Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
static const QString kContrastInput;
|
||||
static const QString kOffsetInput;
|
||||
static const QString kExposureInput;
|
||||
static const QString kSaturationInput;
|
||||
static const QString kPivotInput;
|
||||
static const QString kClampBlackEnableInput;
|
||||
static const QString kClampBlackInput;
|
||||
static const QString kClampWhiteEnableInput;
|
||||
static const QString kClampWhiteInput;
|
||||
|
||||
protected slots:
|
||||
virtual void ConfigChanged() override;
|
||||
|
||||
private:
|
||||
void SetVec4InputColors(const QString &input);
|
||||
|
||||
};
|
||||
|
||||
} // olive
|
||||
|
||||
#endif
|
||||
@@ -29,6 +29,8 @@
|
||||
#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 "distort/cornerpin/cornerpindistortnode.h"
|
||||
#include "distort/crop/cropdistortnode.h"
|
||||
#include "distort/flip/flipdistortnode.h"
|
||||
@@ -52,6 +54,7 @@
|
||||
#include "math/trigonometry/trigonometry.h"
|
||||
#include "keying/colordifferencekey/colordifferencekey.h"
|
||||
#include "keying/despill/despill.h"
|
||||
#include "keying/chromakey/chromakey.h"
|
||||
#include "output/track/track.h"
|
||||
#include "output/viewer/viewer.h"
|
||||
#include "project/folder/folder.h"
|
||||
@@ -281,6 +284,12 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
|
||||
return new TimeOffsetNode();
|
||||
case kCornerPinDistort:
|
||||
return new CornerPinDistortNode();
|
||||
case kDisplayTransform:
|
||||
return new DisplayTransformNode();
|
||||
case kOCIOGradingTransformLinear:
|
||||
return new OCIOGradingTransformLinearNode();
|
||||
case kChromaKey:
|
||||
return new ChromaKeyNode();
|
||||
|
||||
case kInternalNodeCount:
|
||||
break;
|
||||
|
||||
@@ -70,6 +70,9 @@ public:
|
||||
kNoiseGenerator,
|
||||
kTimeOffsetNode,
|
||||
kCornerPinDistort,
|
||||
kDisplayTransform,
|
||||
kOCIOGradingTransformLinear,
|
||||
kChromaKey,
|
||||
|
||||
// Count value
|
||||
kInternalNodeCount
|
||||
|
||||
@@ -183,7 +183,7 @@ void MatrixGenerator::InputValueChangedEvent(const QString &input, int element)
|
||||
Q_UNUSED(element)
|
||||
|
||||
if (input == kUniformScaleInput) {
|
||||
SetInputProperty(kScaleInput, QStringLiteral("disabley"), GetStandardValue(kUniformScaleInput).toBool());
|
||||
SetInputProperty(kScaleInput, QStringLiteral("disable1"), GetStandardValue(kUniformScaleInput).toBool());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,12 @@ void HashTraverser::ProcessShader(TexturePtr destination, const Node *node, cons
|
||||
texture_ids_.insert(destination.get(), hash_.result());
|
||||
}
|
||||
|
||||
void HashTraverser::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob &job)
|
||||
{
|
||||
Hash(job.GetColorProcessor()->id());
|
||||
texture_ids_.insert(destination.get(), hash_.result());
|
||||
}
|
||||
|
||||
void HashTraverser::ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job)
|
||||
{
|
||||
texture_ids_.insert(destination.get(), hash_.result());
|
||||
|
||||
@@ -39,6 +39,8 @@ protected:
|
||||
|
||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
||||
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
|
||||
|
||||
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job) override;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# 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(chromakey)
|
||||
add_subdirectory(colordifferencekey)
|
||||
add_subdirectory(despill)
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 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/keying/chromakey/chromakey.h
|
||||
node/keying/chromakey/chromakey.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,150 @@
|
||||
/***
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "chromakey.h"
|
||||
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "render/colorprocessor.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super OCIOBaseNode
|
||||
|
||||
const QString ChromaKeyNode::kColorInput = QStringLiteral("color_key");
|
||||
const QString ChromaKeyNode::kMaskOnlyInput = QStringLiteral("mask_only_in");
|
||||
const QString ChromaKeyNode::kUpperToleranceInput = QStringLiteral("upper_tolerence_in");
|
||||
const QString ChromaKeyNode::kLowerToleranceInput = QStringLiteral("lower_tolerence_in");
|
||||
const QString ChromaKeyNode::kGarbageMatteInput = QStringLiteral("garbage_in");
|
||||
const QString ChromaKeyNode::kCoreMatteInput = QStringLiteral("core_in");
|
||||
const QString ChromaKeyNode::kShadowsInput = QStringLiteral("shadows_in");
|
||||
const QString ChromaKeyNode::kHighlightsInput = QStringLiteral("highlights_in");
|
||||
|
||||
ChromaKeyNode::ChromaKeyNode()
|
||||
{
|
||||
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(0.0f, 1.0f, 0.0f, 1.0f)));
|
||||
|
||||
AddInput(kLowerToleranceInput, NodeValue::kFloat, 5.0);
|
||||
SetInputProperty(kLowerToleranceInput, QStringLiteral("min"), 0.0);
|
||||
SetInputProperty(kLowerToleranceInput, QStringLiteral("base"), 0.1);
|
||||
|
||||
AddInput(kUpperToleranceInput, NodeValue::kFloat, 25.0);
|
||||
SetInputProperty(kUpperToleranceInput, QStringLiteral("base"), 0.1);
|
||||
|
||||
// FIXME: Temporarily disabled. This will break if "lower tolerance" is keyframed or connected to
|
||||
// something and there's currently no solution to remedy that. If there is in the future,
|
||||
// we can look into re-enabling this.
|
||||
//SetInputProperty(kUpperToleranceInput, QStringLiteral("min"), GetStandardValue(kLowerToleranceInput).toDouble());
|
||||
|
||||
AddInput(kGarbageMatteInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kCoreMatteInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kHighlightsInput, NodeValue::kFloat, 100.0f);
|
||||
SetInputProperty(kHighlightsInput, QStringLiteral("min"), 0.0);
|
||||
SetInputProperty(kHighlightsInput, QStringLiteral("base"), 0.1);
|
||||
|
||||
AddInput(kShadowsInput, NodeValue::kFloat, 100.0f);
|
||||
SetInputProperty(kShadowsInput, QStringLiteral("min"), 0.0);
|
||||
SetInputProperty(kShadowsInput, QStringLiteral("base"), 0.1);
|
||||
|
||||
AddInput(kMaskOnlyInput, NodeValue::kBoolean, false);
|
||||
}
|
||||
|
||||
QString ChromaKeyNode::Name() const
|
||||
{
|
||||
return tr("Chroma Key");
|
||||
}
|
||||
|
||||
QString ChromaKeyNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.chromakey");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> ChromaKeyNode::Category() const
|
||||
{
|
||||
return {kCategoryKeying};
|
||||
}
|
||||
|
||||
QString ChromaKeyNode::Description() const
|
||||
{
|
||||
return tr("A simple color key based on the distance from the chroma of a selected color.");
|
||||
}
|
||||
|
||||
void ChromaKeyNode::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
SetInputName(kTextureInput, tr("Input"));
|
||||
SetInputName(kGarbageMatteInput, tr("Garbage Matte"));
|
||||
SetInputName(kCoreMatteInput, tr("Core Matte"));
|
||||
SetInputName(kColorInput, tr("Key Color"));
|
||||
SetInputName(kShadowsInput, tr("Shadows"));
|
||||
SetInputName(kHighlightsInput, tr("Highlights"));
|
||||
SetInputName(kUpperToleranceInput, tr("Upper Tolerance"));
|
||||
SetInputName(kLowerToleranceInput, tr("Lower Tolerance"));
|
||||
SetInputName(kMaskOnlyInput, tr("Show Mask Only"));
|
||||
}
|
||||
|
||||
void ChromaKeyNode::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
Q_UNUSED(element);
|
||||
if (input == kLowerToleranceInput) {
|
||||
// FIXME: Temporarily disabled. This will break if "lower tolerance" is keyframed or connected to
|
||||
// something and there's currently no solution to remedy that. If there is in the future,
|
||||
// we can look into re-enabling this.
|
||||
//SetInputProperty(kUpperToleranceInput, QStringLiteral("min"), GetStandardValue(kLowerToleranceInput).toDouble());
|
||||
}
|
||||
|
||||
GenerateProcessor();
|
||||
}
|
||||
|
||||
ShaderCode ChromaKeyNode::GetShaderCode(const ShaderRequest &request) const
|
||||
{
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/chromakey.frag")).arg(request.stub));
|
||||
}
|
||||
|
||||
void ChromaKeyNode::GenerateProcessor()
|
||||
{
|
||||
if (manager()){
|
||||
try {
|
||||
ColorTransform transform("cie_xyz_d65_interchange");
|
||||
set_processor(ColorProcessor::Create(manager(), manager()->GetReferenceColorSpace(), transform));
|
||||
} catch (const OCIO::Exception &e) {
|
||||
std::cerr << std::endl << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChromaKeyNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
if (!value[kTextureInput].data().isNull() && processor()) {
|
||||
ColorTransformJob job;
|
||||
|
||||
job.InsertValue(value);
|
||||
job.SetAlphaChannelRequired(ColorTransformJob::kAlphaForceOn);
|
||||
job.SetColorProcessor(processor());
|
||||
job.SetInputTexture(value[kTextureInput].data().value<TexturePtr>());
|
||||
job.SetNeedsCustomShader(this);
|
||||
job.SetFunctionName(QStringLiteral("SceneLinearToCIEXYZ_d65"));
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
void ChromaKeyNode::ConfigChanged()
|
||||
{
|
||||
GenerateProcessor();
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
@@ -0,0 +1,62 @@
|
||||
/***
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#ifndef CHROMAKEYNODE_H
|
||||
#define CHROMAKEYNODE_H
|
||||
|
||||
#include "node/color/ociobase/ociobase.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ChromaKeyNode : public OCIOBaseNode {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ChromaKeyNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(ChromaKeyNode)
|
||||
|
||||
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 ShaderCode GetShaderCode(const ShaderRequest &request) const override;
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals& globals, NodeValueTable* table) const override;
|
||||
|
||||
virtual void ConfigChanged() override;
|
||||
|
||||
static const QString kColorInput;
|
||||
static const QString kMaskOnlyInput;
|
||||
static const QString kUpperToleranceInput;
|
||||
static const QString kLowerToleranceInput;
|
||||
static const QString kGarbageMatteInput;
|
||||
static const QString kCoreMatteInput;
|
||||
static const QString kShadowsInput;
|
||||
static const QString kHighlightsInput;
|
||||
|
||||
private:
|
||||
void GenerateProcessor();
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // CHROMAKEYNODE_H
|
||||
+1
-2
@@ -50,8 +50,7 @@ Node::Node() :
|
||||
folder_(nullptr),
|
||||
operation_stack_(0),
|
||||
cache_result_(false),
|
||||
flags_(kNone),
|
||||
effect_element_(-1)
|
||||
flags_(kNone)
|
||||
{
|
||||
AddInput(kEnabledInput, NodeValue::kBoolean, true);
|
||||
}
|
||||
|
||||
+13
-4
@@ -552,7 +552,12 @@ public:
|
||||
|
||||
NodeInput GetEffectInput()
|
||||
{
|
||||
return effect_input_.isEmpty() ? NodeInput() : NodeInput(this, effect_input_, effect_element_);
|
||||
return effect_input_.isEmpty() ? NodeInput() : NodeInput(this, effect_input_);
|
||||
}
|
||||
|
||||
const QString &GetEffectInputID() const
|
||||
{
|
||||
return effect_input_;
|
||||
}
|
||||
|
||||
class ValueHint {
|
||||
@@ -667,6 +672,12 @@ public:
|
||||
id = shader_id;
|
||||
}
|
||||
|
||||
ShaderRequest(const QString &shader_id, const QString &shader_stub)
|
||||
{
|
||||
id = shader_id;
|
||||
stub = shader_stub;
|
||||
}
|
||||
|
||||
QString id;
|
||||
QString stub;
|
||||
};
|
||||
@@ -1058,10 +1069,9 @@ protected:
|
||||
|
||||
virtual void childEvent(QChildEvent *event) override;
|
||||
|
||||
void SetEffectInput(const QString &input, int element = -1)
|
||||
void SetEffectInput(const QString &input)
|
||||
{
|
||||
effect_input_ = input;
|
||||
effect_element_ = element;
|
||||
}
|
||||
|
||||
void SetToolTip(const QString& s)
|
||||
@@ -1380,7 +1390,6 @@ private:
|
||||
QVector<NodeGizmo*> gizmos_;
|
||||
|
||||
QString effect_input_;
|
||||
int effect_element_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
|
||||
+23
-1
@@ -269,7 +269,15 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const Node::ValueHint
|
||||
|
||||
return table;
|
||||
} else {
|
||||
return database.Merge();
|
||||
// If this node has an effect input, ensure that is pushed last
|
||||
NodeValueTable primary;
|
||||
if (!n->GetEffectInputID().isEmpty()) {
|
||||
primary = database.Take(n->GetEffectInputID());
|
||||
}
|
||||
|
||||
NodeValueTable m = database.Merge();
|
||||
m.Push(primary);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,6 +295,7 @@ NodeValueTable NodeTraverser::GenerateBlockTable(const Track *track, const TimeR
|
||||
return table;
|
||||
}
|
||||
|
||||
|
||||
QVector2D NodeTraverser::GenerateResolution() const
|
||||
{
|
||||
return QVector2D(video_params_.square_pixel_width(), video_params_.height());
|
||||
@@ -339,6 +348,19 @@ void NodeTraverser::ResolveJobs(NodeValue &val, const TimeRange &range)
|
||||
|
||||
val.set_data(QVariant::fromValue(tex));
|
||||
|
||||
} else if (v.canConvert<ColorTransformJob>()) {
|
||||
|
||||
ColorTransformJob job = v.value<ColorTransformJob>();
|
||||
|
||||
VideoParams src_params = job.GetInputTexture()->params();
|
||||
src_params.set_channel_count(GetChannelCountFromJob(job));
|
||||
|
||||
TexturePtr dest = CreateTexture(src_params);
|
||||
|
||||
ProcessColorTransform(dest, val.source(), job);
|
||||
|
||||
val.set_data(QVariant::fromValue(dest));
|
||||
|
||||
} else if (v.canConvert<FootageJob>()) {
|
||||
|
||||
FootageJob job = v.value<FootageJob>();
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "common/cancelableobject.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "render/job/footagejob.h"
|
||||
#include "render/job/colortransformjob.h"
|
||||
#include "value.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -88,6 +89,8 @@ protected:
|
||||
|
||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job){}
|
||||
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job){}
|
||||
|
||||
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job){}
|
||||
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job){}
|
||||
|
||||
@@ -203,6 +203,12 @@ public:
|
||||
return type_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T value() const
|
||||
{
|
||||
return data_.value<T>();
|
||||
}
|
||||
|
||||
const QVariant& data() const
|
||||
{
|
||||
return data_;
|
||||
@@ -340,6 +346,11 @@ public:
|
||||
values_.append(value);
|
||||
}
|
||||
|
||||
void Push(const NodeValueTable& value)
|
||||
{
|
||||
values_.append(value.values_);
|
||||
}
|
||||
|
||||
void Push(NodeValue::Type type, const QVariant& data, const Node *from, bool array = false, const QString& tag = QString())
|
||||
{
|
||||
Push(NodeValue(type, data, from, array, tag));
|
||||
|
||||
@@ -41,6 +41,11 @@ public:
|
||||
tables_.insert(key, value);
|
||||
}
|
||||
|
||||
NodeValueTable Take(const QString &key)
|
||||
{
|
||||
return tables_.take(key);
|
||||
}
|
||||
|
||||
NodeValueTable Merge() const;
|
||||
|
||||
using Tables = QHash<QString, NodeValueTable>;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef ALPHAASSOC_H
|
||||
#define ALPHAASSOC_H
|
||||
|
||||
namespace olive {
|
||||
|
||||
enum AlphaAssociated {
|
||||
kAlphaNone,
|
||||
kAlphaUnassociated,
|
||||
kAlphaAssociated
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ALPHAASSOC_H
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const ColorTransform &transform)
|
||||
ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const ColorTransform &transform, Direction direction)
|
||||
{
|
||||
QMutexLocker locker(config->mutex());
|
||||
|
||||
@@ -41,6 +41,7 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const
|
||||
display_transform->setSrc(input.toUtf8());
|
||||
display_transform->setDisplay(output.toUtf8());
|
||||
display_transform->setView(view.toUtf8());
|
||||
display_transform->setDirection(direction == kNormal ? OCIO::TRANSFORM_DIR_FORWARD : OCIO::TRANSFORM_DIR_INVERSE);
|
||||
|
||||
OCIO_SET_C_LOCALE_FOR_SCOPE;
|
||||
|
||||
@@ -69,13 +70,25 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const
|
||||
} else {
|
||||
|
||||
OCIO_SET_C_LOCALE_FOR_SCOPE;
|
||||
processor_ = config->GetConfig()->getProcessor(input.toUtf8(),
|
||||
output.toUtf8());
|
||||
try {
|
||||
if (direction == kNormal) {
|
||||
processor_ = config->GetConfig()->getProcessor(input.toUtf8(), output.toUtf8());
|
||||
} else {
|
||||
processor_ = config->GetConfig()->getProcessor(output.toUtf8(), input.toUtf8());
|
||||
}
|
||||
} catch (OCIO::Exception &e) {
|
||||
qWarning() << "ColorProcessor exception:" << e.what();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cpu_processor_ = processor_->getDefaultCPUProcessor();
|
||||
id_ = GenerateID(config, input, transform);
|
||||
}
|
||||
|
||||
ColorProcessor::ColorProcessor(OCIO::ConstProcessorRcPtr processor)
|
||||
{
|
||||
processor_ = processor;
|
||||
cpu_processor_ = processor_->getDefaultCPUProcessor();
|
||||
}
|
||||
|
||||
void ColorProcessor::ConvertFrame(Frame *f)
|
||||
@@ -109,18 +122,14 @@ Color ColorProcessor::ConvertColor(const Color& in)
|
||||
return Color(c[0], c[1], c[2], c[3]);
|
||||
}
|
||||
|
||||
QString ColorProcessor::GenerateID(ColorManager *config, const QString &input, const ColorTransform &transform)
|
||||
ColorProcessorPtr ColorProcessor::Create(ColorManager *config, const QString& input, const ColorTransform &transform, Direction direction)
|
||||
{
|
||||
return QStringLiteral("%1:%2:%3:%4:%5").arg(config->GetConfigFilename(),
|
||||
input,
|
||||
transform.display(),
|
||||
transform.view(),
|
||||
transform.look());
|
||||
return std::make_shared<ColorProcessor>(config, input, transform, direction);
|
||||
}
|
||||
|
||||
ColorProcessorPtr ColorProcessor::Create(ColorManager *config, const QString& input, const ColorTransform &transform)
|
||||
ColorProcessorPtr ColorProcessor::Create(OCIO::ConstProcessorRcPtr processor)
|
||||
{
|
||||
return std::make_shared<ColorProcessor>(config, input, transform);
|
||||
return std::make_shared<ColorProcessor>(processor);
|
||||
}
|
||||
|
||||
OCIO::ConstProcessorRcPtr ColorProcessor::GetProcessor()
|
||||
|
||||
@@ -41,11 +41,13 @@ public:
|
||||
kInverse
|
||||
};
|
||||
|
||||
ColorProcessor(ColorManager* config, const QString& input, const ColorTransform& dest_space);
|
||||
ColorProcessor(ColorManager* config, const QString& input, const ColorTransform& dest_space, Direction direction = kNormal);
|
||||
ColorProcessor(OCIO::ConstProcessorRcPtr processor);
|
||||
|
||||
DISABLE_COPY_MOVE(ColorProcessor)
|
||||
|
||||
static ColorProcessorPtr Create(ColorManager* config, const QString& input, const ColorTransform& dest_space);
|
||||
static ColorProcessorPtr Create(ColorManager* config, const QString& input, const ColorTransform& dest_space, Direction direction = kNormal);
|
||||
static ColorProcessorPtr Create(OCIO::ConstProcessorRcPtr processor);
|
||||
|
||||
OCIO::ConstProcessorRcPtr GetProcessor();
|
||||
|
||||
@@ -54,20 +56,16 @@ public:
|
||||
|
||||
Color ConvertColor(const Color &in);
|
||||
|
||||
const QString& id() const
|
||||
const char *id() const
|
||||
{
|
||||
return id_;
|
||||
return processor_->getCacheID();
|
||||
}
|
||||
|
||||
static QString GenerateID(ColorManager* config, const QString& input, const ColorTransform& dest_space);
|
||||
|
||||
private:
|
||||
OCIO::ConstProcessorRcPtr processor_;
|
||||
|
||||
OCIO::ConstCPUProcessorRcPtr cpu_processor_;
|
||||
|
||||
QString id_;
|
||||
|
||||
};
|
||||
|
||||
using ColorProcessorChain = QVector<ColorProcessorPtr>;
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef COLORTRANSFORMJOB_H
|
||||
#define COLORTRANSFORMJOB_H
|
||||
|
||||
#include <QMatrix4x4>
|
||||
#include <QString>
|
||||
|
||||
#include "render/job/generatejob.h"
|
||||
#include "render/alphaassoc.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "render/texture.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class Node;
|
||||
|
||||
class ColorTransformJob : public GenerateJob
|
||||
{
|
||||
public:
|
||||
ColorTransformJob()
|
||||
{
|
||||
processor_ = nullptr;
|
||||
input_texture_ = nullptr;
|
||||
custom_shader_src_ = nullptr;
|
||||
input_alpha_association_ = kAlphaNone;
|
||||
clear_destination_ = true;
|
||||
}
|
||||
|
||||
QString id() const
|
||||
{
|
||||
if (id_.isEmpty()) {
|
||||
return processor_->id();
|
||||
} else {
|
||||
return id_;
|
||||
}
|
||||
}
|
||||
|
||||
void SetOverrideID(const QString &id) { id_ = id; }
|
||||
|
||||
TexturePtr GetInputTexture() const { return input_texture_; }
|
||||
void SetInputTexture(TexturePtr tex) { input_texture_ = tex; }
|
||||
|
||||
ColorProcessorPtr GetColorProcessor() const { return processor_; }
|
||||
void SetColorProcessor(ColorProcessorPtr p) { processor_ = p; }
|
||||
|
||||
const AlphaAssociated &GetInputAlphaAssociation() const { return input_alpha_association_; }
|
||||
void SetInputAlphaAssociation(const AlphaAssociated &e) { input_alpha_association_ = e; }
|
||||
|
||||
const Node *CustomShaderSource() const { return custom_shader_src_; }
|
||||
const QString &CustomShaderID() const { return custom_shader_id_; }
|
||||
void SetNeedsCustomShader(const Node *node, const QString &id = QString())
|
||||
{
|
||||
custom_shader_src_ = node;
|
||||
custom_shader_id_ = id;
|
||||
}
|
||||
|
||||
bool IsClearDestinationEnabled() const { return clear_destination_; }
|
||||
void SetClearDestinationEnabled(bool e) { clear_destination_ = e; }
|
||||
|
||||
const QMatrix4x4 &GetTransformMatrix() const { return matrix_; }
|
||||
void SetTransformMatrix(const QMatrix4x4 &m) { matrix_ = m; }
|
||||
|
||||
const QMatrix4x4 &GetCropMatrix() const { return crop_matrix_; }
|
||||
void SetCropMatrix(const QMatrix4x4 &m) { crop_matrix_ = m; }
|
||||
|
||||
const QString &GetFunctionName() const { return function_name_; }
|
||||
void SetFunctionName(const QString &function_name = QString()) { function_name_ = function_name; };
|
||||
|
||||
private:
|
||||
ColorProcessorPtr processor_;
|
||||
QString id_;
|
||||
|
||||
TexturePtr input_texture_;
|
||||
|
||||
const Node *custom_shader_src_;
|
||||
QString custom_shader_id_;
|
||||
|
||||
AlphaAssociated input_alpha_association_;
|
||||
|
||||
bool clear_destination_;
|
||||
|
||||
QMatrix4x4 matrix_;
|
||||
|
||||
QMatrix4x4 crop_matrix_;
|
||||
|
||||
QString function_name_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::ColorTransformJob)
|
||||
|
||||
#endif // COLORTRANSFORMJOB_H
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <QVector>
|
||||
|
||||
#include "generatejob.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "render/texture.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
+34
-36
@@ -52,16 +52,6 @@ TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, const void *data,
|
||||
return CreateTexture(params, Texture::k2D, data, linesize);
|
||||
}
|
||||
|
||||
void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, Texture *destination, bool clear_destination, const QMatrix4x4 &matrix, const QMatrix4x4 &crop_matrix)
|
||||
{
|
||||
BlitColorManagedInternal(color_processor, source, source_alpha_association, destination, destination->params(), clear_destination, matrix, crop_matrix);
|
||||
}
|
||||
|
||||
void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, VideoParams params, bool clear_destination, const QMatrix4x4& matrix, const QMatrix4x4 &crop_matrix)
|
||||
{
|
||||
BlitColorManagedInternal(color_processor, source, source_alpha_association, nullptr, params, clear_destination, matrix, crop_matrix);
|
||||
}
|
||||
|
||||
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms)
|
||||
{
|
||||
color_cache_mutex_.lock();
|
||||
@@ -103,35 +93,45 @@ TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const Vide
|
||||
return std::make_shared<Texture>(this, v, params, type);
|
||||
}
|
||||
|
||||
bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::ColorContext *ctx)
|
||||
bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::ColorContext *ctx)
|
||||
{
|
||||
QMutexLocker locker(&color_cache_mutex_);
|
||||
|
||||
ColorContext& color_ctx = *ctx;
|
||||
|
||||
if (color_cache_.contains(color_processor->id())) {
|
||||
color_ctx = color_cache_.value(color_processor->id());
|
||||
QString proc_id = color_job.id();
|
||||
|
||||
if (color_cache_.contains(proc_id)) {
|
||||
color_ctx = color_cache_.value(proc_id);
|
||||
return true;
|
||||
} else {
|
||||
// Create shader description
|
||||
const char* ocio_func_name = "OCIODisplay";
|
||||
QString ocio_func_name;
|
||||
if (color_job.GetFunctionName().isEmpty()) {
|
||||
ocio_func_name = "OCIODisplay";
|
||||
} else {
|
||||
ocio_func_name = color_job.GetFunctionName();
|
||||
}
|
||||
auto shader_desc = OCIO::GpuShaderDesc::CreateShaderDesc();
|
||||
shader_desc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_ES_3_0);
|
||||
shader_desc->setFunctionName(ocio_func_name);
|
||||
shader_desc->setFunctionName(ocio_func_name.toUtf8());
|
||||
shader_desc->setResourcePrefix("ocio_");
|
||||
|
||||
// Generate shader
|
||||
color_processor->GetProcessor()->getDefaultGPUProcessor()->extractGpuShaderInfo(shader_desc);
|
||||
color_job.GetColorProcessor()->GetProcessor()->getDefaultGPUProcessor()->extractGpuShaderInfo(shader_desc);
|
||||
|
||||
// Generate shader code using OCIO stub and our auto-generated name
|
||||
QString shader_frag = FileFunctions::ReadFileAsString(QStringLiteral(":shaders/colormanage.frag")).arg(
|
||||
shader_desc->getShaderText(),
|
||||
ocio_func_name
|
||||
);
|
||||
ShaderCode code;
|
||||
if (const Node *shader_src = color_job.CustomShaderSource()) {
|
||||
// Use shader code from associated node
|
||||
code = shader_src->GetShaderCode({color_job.CustomShaderID(), shader_desc->getShaderText()});
|
||||
} else {
|
||||
// Generate shader code using OCIO stub and our auto-generated name
|
||||
code = FileFunctions::ReadFileAsString(QStringLiteral(":shaders/colormanage.frag"));
|
||||
code.set_frag_code(code.frag_code().arg(shader_desc->getShaderText()));
|
||||
}
|
||||
|
||||
// Try to compile shader
|
||||
color_ctx.compiled_shader = CreateNativeShader(ShaderCode(shader_frag,
|
||||
FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/default.vert"))));
|
||||
color_ctx.compiled_shader = CreateNativeShader(code);
|
||||
|
||||
if (color_ctx.compiled_shader.isNull()) {
|
||||
return false;
|
||||
@@ -199,28 +199,26 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo
|
||||
color_ctx.lut1d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear;
|
||||
}
|
||||
|
||||
color_cache_.insert(color_processor->id(), color_ctx);
|
||||
color_cache_.insert(proc_id, color_ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, TexturePtr source,
|
||||
AlphaAssociated source_alpha_association, Texture *destination,
|
||||
VideoParams params, bool clear_destination, const QMatrix4x4& matrix,
|
||||
const QMatrix4x4& crop_matrix)
|
||||
void Renderer::BlitColorManaged(const ColorTransformJob &color_job, Texture *destination, const VideoParams ¶ms)
|
||||
{
|
||||
ColorContext color_ctx;
|
||||
if (!GetColorContext(color_processor, &color_ctx)) {
|
||||
if (!GetColorContext(color_job, &color_ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ShaderJob job;
|
||||
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(source)));
|
||||
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix));
|
||||
job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix.inverted()));
|
||||
job.InsertValue(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, source_alpha_association));
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(color_job.GetInputTexture())));
|
||||
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
|
||||
job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, color_job.GetCropMatrix().inverted()));
|
||||
job.InsertValue(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, color_job.GetInputAlphaAssociation()));
|
||||
job.InsertValue(color_job.GetValues());
|
||||
job.SetAlphaChannelRequired(color_job.GetAlphaChannelRequired());
|
||||
|
||||
foreach (const ColorContext::LUT& l, color_ctx.lut3d_textures) {
|
||||
job.InsertValue(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture)));
|
||||
@@ -232,9 +230,9 @@ void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, Textu
|
||||
}
|
||||
|
||||
if (destination) {
|
||||
BlitToTexture(color_ctx.compiled_shader, job, destination, clear_destination);
|
||||
BlitToTexture(color_ctx.compiled_shader, job, destination, color_job.IsClearDestinationEnabled());
|
||||
} else {
|
||||
Blit(color_ctx.compiled_shader, job, params, clear_destination);
|
||||
Blit(color_ctx.compiled_shader, job, params, color_job.IsClearDestinationEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-14
@@ -29,6 +29,7 @@
|
||||
#include "common/timerange.h"
|
||||
#include "node/node.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "render/job/colortransformjob.h"
|
||||
#include "render/videoparams.h"
|
||||
#include "texture.h"
|
||||
|
||||
@@ -63,14 +64,15 @@ public:
|
||||
Blit(shader, job, nullptr, params, clear_destination);
|
||||
}
|
||||
|
||||
enum AlphaAssociated {
|
||||
kAlphaNone,
|
||||
kAlphaUnassociated,
|
||||
kAlphaAssociated
|
||||
};
|
||||
|
||||
void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, Texture* destination, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4());
|
||||
void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, VideoParams params, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4());
|
||||
void BlitColorManaged(const ColorTransformJob &color_job, Texture* destination, const VideoParams ¶ms);
|
||||
void BlitColorManaged(const ColorTransformJob &job, Texture* destination)
|
||||
{
|
||||
BlitColorManaged(job, destination, destination->params());
|
||||
}
|
||||
void BlitColorManaged(const ColorTransformJob &job, const VideoParams ¶ms)
|
||||
{
|
||||
BlitColorManaged(job, nullptr, params);
|
||||
}
|
||||
|
||||
TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms);
|
||||
|
||||
@@ -126,12 +128,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
bool GetColorContext(ColorProcessorPtr color_processor, ColorContext* ctx);
|
||||
|
||||
void BlitColorManagedInternal(ColorProcessorPtr color_processor, TexturePtr source,
|
||||
AlphaAssociated source_alpha_association,
|
||||
Texture* destination, VideoParams params, bool clear_destination,
|
||||
const QMatrix4x4 &matrix, const QMatrix4x4 &crop_matrix);
|
||||
bool GetColorContext(const ColorTransformJob &color_job, ColorContext* ctx);
|
||||
|
||||
QHash<QString, ColorContext> color_cache_;
|
||||
|
||||
|
||||
@@ -103,9 +103,14 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time
|
||||
|
||||
if (output_color_transform) {
|
||||
// Yes color transform, blit color managed
|
||||
render_ctx_->BlitColorManaged(output_color_transform, texture,
|
||||
OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? Renderer::kAlphaAssociated : Renderer::kAlphaNone,
|
||||
blit_tex.get(), true, matrix);
|
||||
ColorTransformJob job;
|
||||
|
||||
job.SetColorProcessor(output_color_transform);
|
||||
job.SetInputTexture(texture);
|
||||
job.SetInputAlphaAssociation(OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? kAlphaAssociated : kAlphaNone);
|
||||
job.SetTransformMatrix(matrix);
|
||||
|
||||
render_ctx_->BlitColorManaged(job, blit_tex.get());
|
||||
} else {
|
||||
// No color transform, just blit
|
||||
ShaderJob job;
|
||||
@@ -459,19 +464,21 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
using_colorspace,
|
||||
color_manager->GetReferenceColorSpace());
|
||||
|
||||
Renderer::AlphaAssociated alpha_assoc;
|
||||
ColorTransformJob job;
|
||||
|
||||
job.SetColorProcessor(processor);
|
||||
job.SetInputTexture(unmanaged_texture);
|
||||
|
||||
if (stream_data.channel_count() != VideoParams::kRGBAChannelCount
|
||||
|| stream_data.colorspace() == color_manager->GetReferenceColorSpace()) {
|
||||
alpha_assoc = Renderer::kAlphaNone;
|
||||
job.SetInputAlphaAssociation(kAlphaNone);
|
||||
} else if (stream_data.premultiplied_alpha()) {
|
||||
alpha_assoc = Renderer::kAlphaAssociated;
|
||||
job.SetInputAlphaAssociation(kAlphaAssociated);
|
||||
} else {
|
||||
alpha_assoc = Renderer::kAlphaUnassociated;
|
||||
job.SetInputAlphaAssociation(kAlphaUnassociated);
|
||||
}
|
||||
|
||||
render_ctx_->BlitColorManaged(processor, unmanaged_texture,
|
||||
alpha_assoc,
|
||||
destination.get());
|
||||
render_ctx_->BlitColorManaged(job, destination.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -550,6 +557,11 @@ void RenderProcessor::ProcessSamples(SampleBufferPtr destination, const Node *no
|
||||
}
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob &job)
|
||||
{
|
||||
render_ctx_->BlitColorManaged(job, destination.get());
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob &job)
|
||||
{
|
||||
FramePtr frame = Frame::Create();
|
||||
@@ -571,7 +583,14 @@ void RenderProcessor::ConvertToReferenceSpace(TexturePtr destination, TexturePtr
|
||||
{
|
||||
ColorManager* color_manager = Node::ValueToPtr<ColorManager>(ticket_->property("colormanager"));
|
||||
ColorProcessorPtr cp = ColorProcessor::Create(color_manager, input_cs, color_manager->GetReferenceColorSpace());
|
||||
render_ctx_->BlitColorManaged(cp, source, Renderer::kAlphaAssociated, destination.get());
|
||||
|
||||
ColorTransformJob ctj;
|
||||
|
||||
ctj.SetColorProcessor(cp);
|
||||
ctj.SetInputTexture(source);
|
||||
ctj.SetInputAlphaAssociation(kAlphaAssociated);
|
||||
|
||||
render_ctx_->BlitColorManaged(ctj, destination.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ protected:
|
||||
|
||||
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
|
||||
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job) override;
|
||||
|
||||
virtual bool CanCacheFrames() override;
|
||||
|
||||
@@ -33,15 +33,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
const QString& frag_code() const
|
||||
{
|
||||
return frag_code_;
|
||||
}
|
||||
const QString& frag_code() const { return frag_code_; }
|
||||
void set_frag_code(const QString &f) { frag_code_ = f; }
|
||||
|
||||
const QString& vert_code() const
|
||||
{
|
||||
return vert_code_;
|
||||
}
|
||||
const QString& vert_code() const { return vert_code_; }
|
||||
void set_vert_code(const QString &v) { vert_code_ = v; }
|
||||
|
||||
private:
|
||||
QString frag_code_;
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// Main texture input
|
||||
uniform sampler2D tex_in;
|
||||
uniform vec4 color_key;
|
||||
uniform bool mask_only_in;
|
||||
uniform float upper_tolerence_in;
|
||||
uniform float lower_tolerence_in;
|
||||
|
||||
uniform sampler2D garbage_in;
|
||||
uniform sampler2D core_in;
|
||||
uniform bool garbage_in_enabled;
|
||||
uniform bool core_in_enabled;
|
||||
|
||||
uniform float highlights_in;
|
||||
uniform float shadows_in;
|
||||
|
||||
|
||||
// Main texture coordinate
|
||||
in vec2 ove_texcoord;
|
||||
out vec4 frag_color;
|
||||
|
||||
// Program will replace this with OCIO's auto-generated shader code
|
||||
%1
|
||||
|
||||
// Assume D65 white point
|
||||
float Xn = 95.0489;
|
||||
float Yn = 100.0;
|
||||
float Zn = 108.8840;
|
||||
float delta = 0.20689655172; // 6/29
|
||||
|
||||
float func(float t) {
|
||||
if (t > pow(delta, 3.0)){
|
||||
return pow(t, 1.0/3.0);
|
||||
} else{
|
||||
return (t / (3.0 * pow(delta, 2))) + 4.0/29.0;
|
||||
}
|
||||
}
|
||||
|
||||
vec4 CIExyz_to_Lab(vec4 CIE) {
|
||||
vec4 lab;
|
||||
lab.r = 116.0 * func(CIE.g / Yn) - 16.0;
|
||||
lab.g = 500.0 * (func(CIE.r / Xn) - func(CIE.g / Yn));
|
||||
lab.b = 200.0 * (func(CIE.g / Yn) - func(CIE.b / Zn));
|
||||
lab.w = CIE.w;
|
||||
|
||||
return lab;
|
||||
}
|
||||
|
||||
float colorclose(vec4 col, vec4 key, float tola,float tolb) {
|
||||
// Decides if a color is close to the specified hue
|
||||
float temp = sqrt(((key.g-col.g)*(key.g-col.g))+((key.b-col.b)*(key.b-col.b)));
|
||||
if (temp < tola) {return (0.0);}
|
||||
if (temp < tolb) {return ((temp-tola)/(tolb-tola));}
|
||||
return (1.0);
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 col = texture(tex_in, ove_texcoord);
|
||||
|
||||
vec4 unassoc = col;
|
||||
if (unassoc.a > 0) {
|
||||
unassoc.rgb /= unassoc.a;
|
||||
}
|
||||
|
||||
// Perform color conversion
|
||||
vec4 cie_xyz = SceneLinearToCIEXYZ_d65(unassoc);
|
||||
vec4 lab = CIExyz_to_Lab(cie_xyz);
|
||||
|
||||
vec4 cie_xyz_key = SceneLinearToCIEXYZ_d65(color_key);
|
||||
vec4 lab_key = CIExyz_to_Lab(cie_xyz_key);
|
||||
|
||||
float mask = colorclose(lab, lab_key, lower_tolerence_in, upper_tolerence_in);
|
||||
|
||||
mask = clamp(mask, 0.0, 1.0);
|
||||
|
||||
if (garbage_in_enabled) {
|
||||
// Force anything we want to remove to be 0.0
|
||||
vec4 garbage = texture(garbage_in, ove_texcoord);
|
||||
// Assumes garbage is achromatic
|
||||
mask -= garbage.r;
|
||||
mask = clamp(mask, 0.0, 1.0);
|
||||
}
|
||||
|
||||
if (core_in_enabled) {
|
||||
// Force anything we want to keep to be 1.0
|
||||
vec3 core = texture(core_in, ove_texcoord).rgb;
|
||||
// Assumes core is achromatic
|
||||
mask += core.r;
|
||||
mask = clamp(mask, 0.0, 1.0);
|
||||
}
|
||||
|
||||
// Crush blacks and push whites
|
||||
mask = shadows_in * 0.01 * (highlights_in * 0.01 * mask - 1.0) + 1.0;
|
||||
mask = clamp(mask, 0.0, 1.0);
|
||||
|
||||
col.rgb *= mask;
|
||||
col.w = mask;
|
||||
|
||||
if (!mask_only_in) {
|
||||
frag_color = col;
|
||||
} else {
|
||||
frag_color = vec4(vec3(mask), 1.0);
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ void main() {
|
||||
}
|
||||
|
||||
// Perform color conversion
|
||||
col = %2(col);
|
||||
col = OCIODisplay(col);
|
||||
|
||||
// Associate or re-associate here
|
||||
if (ove_maintex_alpha == ALPHA_ASSOC) {
|
||||
|
||||
@@ -547,22 +547,33 @@ void NodeParamViewWidgetBridge::SetProperty(const QString &key, const QVariant &
|
||||
NodeValue::Type data_type = GetDataType();
|
||||
|
||||
// Parameters for all types
|
||||
if (key == QStringLiteral("enabled")) {
|
||||
foreach (QWidget* w, widgets_) {
|
||||
w->setEnabled(value.toBool());
|
||||
bool key_is_disable = key.startsWith(QStringLiteral("disable"));
|
||||
if (key_is_disable || key.startsWith(QStringLiteral("enabled"))) {
|
||||
|
||||
bool e = value.toBool();
|
||||
if (key_is_disable) {
|
||||
e = !e;
|
||||
}
|
||||
|
||||
if (key.size() == 7) { // just the word "disable" or "enabled"
|
||||
for (int i=0; i<widgets_.size(); i++) {
|
||||
widgets_.at(i)->setEnabled(e);
|
||||
}
|
||||
} else { // set specific track/widget
|
||||
bool ok;
|
||||
int element = key.midRef(7).toInt(&ok);
|
||||
int tracks = NodeValue::get_number_of_keyframe_tracks(data_type);
|
||||
|
||||
if (ok && element >= 0 && element < tracks) {
|
||||
widgets_.at(element)->setEnabled(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Parameters for vectors only
|
||||
if (NodeValue::type_is_vector(data_type)) {
|
||||
if (key == QStringLiteral("disablex")) {
|
||||
static_cast<FloatSlider*>(widgets_.at(0))->setEnabled(!value.toBool());
|
||||
} else if (key == QStringLiteral("disabley")) {
|
||||
static_cast<FloatSlider*>(widgets_.at(1))->setEnabled(!value.toBool());
|
||||
} else if (widgets_.size() > 2 && key == QStringLiteral("disablez")) {
|
||||
static_cast<FloatSlider*>(widgets_.at(2))->setEnabled(!value.toBool());
|
||||
} else if (widgets_.size() > 3 && key == QStringLiteral("disablew")) {
|
||||
static_cast<FloatSlider*>(widgets_.at(3))->setEnabled(!value.toBool());
|
||||
if (key == QStringLiteral("tooltip")) {
|
||||
for (int i = 0; i < widgets_.size(); i++) {
|
||||
widgets_.at(i)->setToolTip(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,6 +656,7 @@ void NodeParamViewWidgetBridge::SetProperty(const QString &key, const QVariant &
|
||||
break;
|
||||
}
|
||||
} else if (key == QStringLiteral("offset")) {
|
||||
|
||||
int tracks = NodeValue::get_number_of_keyframe_tracks(data_type);
|
||||
|
||||
QVector<QVariant> offsets = NodeValue::split_normal_value_into_track_values(data_type, value);
|
||||
@@ -654,6 +666,33 @@ void NodeParamViewWidgetBridge::SetProperty(const QString &key, const QVariant &
|
||||
}
|
||||
|
||||
UpdateWidgetValues();
|
||||
|
||||
} else if (key.startsWith(QStringLiteral("color"))) {
|
||||
|
||||
QColor c(value.toString());
|
||||
|
||||
int tracks = NodeValue::get_number_of_keyframe_tracks(data_type);
|
||||
|
||||
if (key.size() == 5) {
|
||||
// Set for all tracks
|
||||
for (int i=0; i<tracks; i++) {
|
||||
static_cast<SliderBase*>(widgets_.at(i))->SetColor(c);
|
||||
}
|
||||
} else {
|
||||
bool ok;
|
||||
int element = key.midRef(5).toInt(&ok);
|
||||
if (ok && element >= 0 && element < tracks) {
|
||||
static_cast<SliderBase*>(widgets_.at(element))->SetColor(c);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (key == QStringLiteral("base")) {
|
||||
|
||||
double d = value.toDouble();
|
||||
for (int i=0; i<widgets_.size(); i++) {
|
||||
static_cast<NumericSliderBase*>(widgets_.at(i))->SetDragMultiplier(d);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,13 @@ void ScopeBase::OnPaint()
|
||||
if (!managed_tex_ || !managed_tex_up_to_date_
|
||||
|| managed_tex_->params() != texture_->params()) {
|
||||
managed_tex_ = renderer()->CreateTexture(texture_->params());
|
||||
renderer()->BlitColorManaged(color_service(), texture_, Renderer::kAlphaNone, managed_tex_.get());
|
||||
|
||||
ColorTransformJob job;
|
||||
job.SetColorProcessor(color_service());
|
||||
job.SetInputTexture(texture_);
|
||||
job.SetInputAlphaAssociation(kAlphaNone);
|
||||
|
||||
renderer()->BlitColorManaged(job, managed_tex_.get());
|
||||
}
|
||||
|
||||
DrawScope(managed_tex_, pipeline_);
|
||||
|
||||
@@ -69,7 +69,7 @@ void NumericSliderBase::LadderDragged(int value, double multiplier)
|
||||
{
|
||||
dragged_ = true;
|
||||
|
||||
dragged_diff_ += value * drag_multiplier_ * multiplier;
|
||||
dragged_diff_ += value * multiplier;
|
||||
|
||||
// Store current value to try and prevent any unnecessary signalling if the value doesn't change
|
||||
QVariant pre_set_value = GetValueInternal();
|
||||
|
||||
@@ -55,6 +55,11 @@ public:
|
||||
UpdateLabel();
|
||||
}
|
||||
|
||||
void SetColor(const QColor &c)
|
||||
{
|
||||
label_->SetColor(c);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void ShowEditor();
|
||||
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
namespace olive {
|
||||
|
||||
SliderLabel::SliderLabel(QWidget *parent) :
|
||||
QLabel(parent)
|
||||
QLabel(parent),
|
||||
override_color_enabled_(false)
|
||||
{
|
||||
QPalette p = palette();
|
||||
|
||||
@@ -52,6 +53,26 @@ SliderLabel::SliderLabel(QWidget *parent) :
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
}
|
||||
|
||||
void SliderLabel::SetColor(const QColor &c)
|
||||
{
|
||||
// Prevent infinite loop in changeEvent when we set the stylesheet
|
||||
override_color_enabled_ = false;
|
||||
override_color_ = c;
|
||||
|
||||
// Different colors will look different depending on the theme (light/dark mode). We abstract
|
||||
// that away here so that other classes can simply choose a color and we will handle making it
|
||||
// more legible based on the background
|
||||
QColor adjusted;
|
||||
if (palette().window().color().lightness() < 128) {
|
||||
adjusted = override_color_.lighter(150);
|
||||
} else {
|
||||
adjusted = override_color_.darker(150);
|
||||
}
|
||||
|
||||
setStyleSheet(QStringLiteral("color: %1").arg(adjusted.name()));
|
||||
override_color_enabled_ = true;
|
||||
}
|
||||
|
||||
void SliderLabel::mousePressEvent(QMouseEvent *e)
|
||||
{
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
@@ -81,4 +102,13 @@ void SliderLabel::focusInEvent(QFocusEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
void SliderLabel::changeEvent(QEvent *event)
|
||||
{
|
||||
QWidget::changeEvent(event);
|
||||
|
||||
if (override_color_enabled_ && event->type() == QEvent::StyleChange) {
|
||||
SetColor(override_color_);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ class SliderLabel : public QLabel
|
||||
public:
|
||||
SliderLabel(QWidget* parent);
|
||||
|
||||
void SetColor(const QColor &c);
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QMouseEvent *e) override;
|
||||
|
||||
@@ -40,6 +42,8 @@ protected:
|
||||
|
||||
virtual void focusInEvent(QFocusEvent *event) override;
|
||||
|
||||
virtual void changeEvent(QEvent *event) override;
|
||||
|
||||
signals:
|
||||
void LabelPressed();
|
||||
|
||||
@@ -51,6 +55,10 @@ signals:
|
||||
|
||||
void ChangeSliderType();
|
||||
|
||||
private:
|
||||
bool override_color_enabled_;
|
||||
QColor override_color_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -485,10 +485,15 @@ void ViewerDisplayWidget::OnPaint()
|
||||
texture_to_draw = deinterlace_texture_;
|
||||
}
|
||||
|
||||
renderer()->BlitColorManaged(color_service(), texture_to_draw,
|
||||
OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? Renderer::kAlphaAssociated : Renderer::kAlphaNone,
|
||||
device_params, false,
|
||||
combined_matrix_flipped_, crop_matrix_);
|
||||
ColorTransformJob ctj;
|
||||
ctj.SetColorProcessor(color_service());
|
||||
ctj.SetInputTexture(texture_to_draw);
|
||||
ctj.SetInputAlphaAssociation(OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? kAlphaAssociated : kAlphaNone);
|
||||
ctj.SetClearDestinationEnabled(false);
|
||||
ctj.SetTransformMatrix(combined_matrix_flipped_);
|
||||
ctj.SetCropMatrix(crop_matrix_);
|
||||
|
||||
renderer()->BlitColorManaged(ctj, device_params);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user