diff --git a/app/common/ocioutils.cpp b/app/common/ocioutils.cpp index b296f0305..950632c3e 100644 --- a/app/common/ocioutils.cpp +++ b/app/common/ocioutils.cpp @@ -44,4 +44,16 @@ OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(VideoParams::Format for return OCIO::BIT_DEPTH_UNKNOWN; } +OCIO::GradingRGBM OCIOUtils::QVec4ToRGBM(QVector4D vector) +{ + OCIO::GradingRGBM rgbm; + + rgbm.m_red = vector.x(); + rgbm.m_green = vector.y(); + rgbm.m_blue = vector.z(); + rgbm.m_master = vector.w(); + + return rgbm; +} + } diff --git a/app/common/ocioutils.h b/app/common/ocioutils.h index 6fa294de7..511d7730b 100644 --- a/app/common/ocioutils.h +++ b/app/common/ocioutils.h @@ -24,6 +24,8 @@ #include namespace OCIO = OCIO_NAMESPACE; +#include + #include "render/videoparams.h" namespace olive { @@ -32,6 +34,8 @@ class OCIOUtils { public: static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(VideoParams::Format format); + + static OCIO::GradingRGBM QVec4ToRGBM(QVector4D vector); }; } diff --git a/app/node/color/CMakeLists.txt b/app/node/color/CMakeLists.txt index 43b005216..6f616da89 100644 --- a/app/node/color/CMakeLists.txt +++ b/app/node/color/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(colormanager) add_subdirectory(displaytransform) +add_subdirectory(ociogradingtransform) set(OLIVE_SOURCES ${OLIVE_SOURCES} diff --git a/app/node/color/ociogradingtransform/CMakeLists.txt b/app/node/color/ociogradingtransform/CMakeLists.txt new file mode 100644 index 000000000..ee382c689 --- /dev/null +++ b/app/node/color/ociogradingtransform/CMakeLists.txt @@ -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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/color/ociogradingtransform/ociogradingtransform.cpp + node/color/ociogradingtransform/ociogradingtransform.h + PARENT_SCOPE +) diff --git a/app/node/color/ociogradingtransform/ociogradingtransform.cpp b/app/node/color/ociogradingtransform/ociogradingtransform.cpp new file mode 100644 index 000000000..10b63da54 --- /dev/null +++ b/app/node/color/ociogradingtransform/ociogradingtransform.cpp @@ -0,0 +1,194 @@ +/*** + 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 . +***/ + +#include "ociogradingtransform.h" + +#include "common/ocioutils.h" +#include "node/project/project.h" +#include "render/colorprocessor.h" + +namespace olive { + +const QString OCIOGradingTransformNode::kTextureInput = QStringLiteral("tex_in"); +const QString OCIOGradingTransformNode::kTypeInput = QStringLiteral("type_in"); +const QString OCIOGradingTransformNode::kBrightnessInput = QStringLiteral("brightness_in"); +const QString OCIOGradingTransformNode::kContrastInput = QStringLiteral("contrast_in"); +const QString OCIOGradingTransformNode::kGammaInput = QStringLiteral("gamma_in"); +const QString OCIOGradingTransformNode::kOffsetInput = QStringLiteral("offset_in"); +const QString OCIOGradingTransformNode::kExposureInput = QStringLiteral("exposure_in"); +const QString OCIOGradingTransformNode::kLiftInput = QStringLiteral("lift_in"); +const QString OCIOGradingTransformNode::kGainInput = QStringLiteral("gain_in"); +const QString OCIOGradingTransformNode::kSaturationInput = QStringLiteral("saturation_in"); +const QString OCIOGradingTransformNode::kPivotInput = QStringLiteral("pivot_in"); +const QString OCIOGradingTransformNode::kPivotBlackInput = QStringLiteral("pivot_black_in"); +const QString OCIOGradingTransformNode::kPivotWhiteInput = QStringLiteral("pivot_white_in"); +const QString OCIOGradingTransformNode::kClampBlackInput = QStringLiteral("clamp_black_in"); +const QString OCIOGradingTransformNode::kClampWhiteInput = QStringLiteral("clamp_white_in"); + +OCIOGradingTransformNode::OCIOGradingTransformNode() +{ + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + + AddInput(kTypeInput, NodeValue::kCombo, 0); + + //AddInput(kBrightnessInput, NodeValue::kVec4, QVector4D{0.0, 0.0, 0.0, 0.0}); + + AddInput(kContrastInput, NodeValue::kVec4, QVector4D{1.0, 1.0, 1.0, 1.0}); + SetInputProperty(kContrastInput, QStringLiteral("min"), QVector4D{0.001f, 0.001f, 0.001f, 0.001f}); + + //AddInput(kGammaInput, NodeValue::kVec4, QVector4D{1.0, 1.0, 1.0, 1.0}); + + AddInput(kOffsetInput, NodeValue::kVec4, QVector4D{0.0, 0.0, 0.0, 0.0}); + + AddInput(kExposureInput, NodeValue::kVec4, QVector4D{0.0, 0.0, 0.0, 0.0}); + + //AddInput(kLiftInput, NodeValue::kVec4, QVector4D{0.0, 0.0, 0.0, 0.0}); + + //AddInput(kGainInput, NodeValue::kVec4, QVector4D{1.0, 1.0, 1.0, 1.0}); + + AddInput(kSaturationInput, NodeValue::kFloat, 1.0); + SetInputProperty(kSaturationInput, QStringLiteral("min"), 0.0); + + AddInput(kPivotInput, NodeValue::kFloat, 0.203919098); + + // AddInput(kPivotBlackInput, NodeValue::kFloat, 0.0); + + //AddInput(kPivotWhiteInput, NodeValue::kFloat, 1.0); + + AddInput(kClampBlackInput, NodeValue::kFloat, 0.0); + + AddInput(kClampWhiteInput, NodeValue::kFloat, 200.0); + + GenerateProcessor(); +} + +void OCIOGradingTransformNode::GenerateProcessor() +{ + if (!project()) { + return; + } + + // Create shader description + shader_desc_ = OCIO::GpuShaderDesc::CreateShaderDesc(); + shader_desc_->setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_3); + shader_desc_->setFunctionName("OCIOGradingTransform"); + shader_desc_->setResourcePrefix("ocio_"); + + OCIO::GradingPrimaryTransformRcPtr gp = OCIO::GradingPrimaryTransform::Create(OCIO::GRADING_LIN); + //auto gp = OCIO::GradingPrimaryTransform::Create(OCIO::GRADING_LIN); + gp->setDirection(OCIO::TransformDirection::TRANSFORM_DIR_FORWARD); + //gp->makeDynamic(); + + OCIO::GradingPrimary gpdata{OCIO::GRADING_LIN}; + //gpdata.m_lift = OCIOUtils::QVec4ToRGBM(GetStandardValue(kLiftInput).value()); + gpdata.m_contrast = OCIOUtils::QVec4ToRGBM(GetStandardValue(kContrastInput).value()); + gpdata.m_exposure = OCIOUtils::QVec4ToRGBM(GetStandardValue(kExposureInput).value()); + //gpdata.m_gamma = OCIOUtils::QVec4ToRGBM(GetStandardValue(kGammaInput).value()); + //gpdata.m_gain = OCIOUtils::QVec4ToRGBM(GetStandardValue(kGainInput).value()); + gpdata.m_offset = OCIOUtils::QVec4ToRGBM(GetStandardValue(kOffsetInput).value()); + gpdata.m_saturation = GetStandardValue(kSaturationInput).value(); + gpdata.m_pivot = GetStandardValue(kPivotInput).value(); + gpdata.m_clampBlack = GetStandardValue(kClampBlackInput).value(); + gpdata.m_clampWhite = GetStandardValue(kClampWhiteInput).value(); + //gpdata.m_pivotBlack = GetStandardValue(kPivotBlackInput).value(); + //gpdata.m_pivotWhite = GetStandardValue(kPivotWhiteInput).value(); + try { + gp->setValue(gpdata); + + processor_ = std::make_shared(); + processor_->SetProsessor(project()->color_manager()->GetConfig()->getProcessor(gp)); + } catch (const OCIO::Exception &e) { + std::cerr << std::endl << e.what() << std::endl; + } + + processor_->GetProcessor()->getDefaultGPUProcessor()->extractGpuShaderInfo(shader_desc_); +} + +Node *OCIOGradingTransformNode::copy() const +{ + return new OCIOGradingTransformNode(); +} + +QString OCIOGradingTransformNode::Name() const +{ + return tr("OCIO Grading Transform"); +} + +QString OCIOGradingTransformNode::id() const +{ + return QStringLiteral("org.olivevideoeditor.Olive.ociogradingtransform"); +} + +QVector OCIOGradingTransformNode::Category() const +{ + return {kCategoryColor}; +} + +QString OCIOGradingTransformNode::Description() const +{ + return tr("Simple color grading using OCIO"); +} + +void OCIOGradingTransformNode::Retranslate() +{ + SetInputName(kTextureInput, tr("Input")); + SetInputName(kTypeInput, tr("Type")); + //SetInputName(kBrightnessInput, tr("Brightness")); + SetInputName(kContrastInput, tr("Contrast")); + //SetInputName(kGammaInput, tr("Gamma")); + SetInputName(kOffsetInput, tr("Offset")); + SetInputName(kExposureInput, tr("Exposure")); + //SetInputName(kLiftInput, tr("Lift")); + //SetInputName(kGainInput, tr("Gain")); + SetInputName(kSaturationInput, tr("Saturation")); + SetInputName(kPivotInput, tr("Pivot")); + //SetInputName(kPivotBlackInput, tr("Black Pivot")); + //SetInputName(kPivotWhiteInput, tr("White Pivot")); + SetInputName(kClampBlackInput, tr("Black Clamp")); + SetInputName(kClampWhiteInput, tr("White Clamp")); + + SetComboBoxStrings(kTypeInput, {"Linear"}); +} + +void OCIOGradingTransformNode::InputValueChangedEvent(const QString &input, int element) +{ + Q_UNUSED(element); + GenerateProcessor(); +} + +ShaderCode OCIOGradingTransformNode::GetShaderCode(const QString &shader_id) const +{ + Q_UNUSED(shader_id) + // Generate shader code using OCIO stub and our auto-generated name + QString shader_frag = FileFunctions::ReadFileAsString(QStringLiteral(":shaders/ociogradingtransform.frag")) + .arg(shader_desc_->getShaderText()); + return ShaderCode(shader_frag); +} + +void OCIOGradingTransformNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +{ + ShaderJob job; + job.InsertValue(value); + job.SetUseOCIO(true); + job.SetShaderDesc(shader_desc_); + job.SetColorProcessor(processor_); + + // If there's no texture, no need to run an operation + if (!job.GetValue(kTextureInput).data().isNull()) { + table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); + } +} + +} diff --git a/app/node/color/ociogradingtransform/ociogradingtransform.h b/app/node/color/ociogradingtransform/ociogradingtransform.h new file mode 100644 index 000000000..6de018d14 --- /dev/null +++ b/app/node/color/ociogradingtransform/ociogradingtransform.h @@ -0,0 +1,75 @@ +/*** + 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 . +***/ + +#ifndef OCIOGRADINGTRANSFORMNODE_H +#define OCIOGRADINGTRANSFORMNODE_H + +#include "node/node.h" +#include "render/colorprocessor.h" + +namespace olive { +class OCIOGradingTransformNode : public Node { + Q_OBJECT + public: + OCIOGradingTransformNode(); + + NODE_DEFAULT_DESTRUCTOR(OCIOGradingTransformNode) + + void GenerateProcessor(); + + virtual Node *copy() const override; + + virtual QString Name() const override; + virtual QString id() const override; + virtual QVector 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 QString &shader_id) const override; + virtual void Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const override; + + ColorProcessorPtr GetColorProcessor() { return processor_; }; + OCIO::GpuShaderDescRcPtr GetGPUShaderDesc() { return shader_desc_; }; + + static const QString kTextureInput; + static const QString kTypeInput; + static const QString kBrightnessInput; + static const QString kContrastInput; + static const QString kGammaInput; + static const QString kOffsetInput; + static const QString kExposureInput; + static const QString kLiftInput; + static const QString kGainInput; + static const QString kSaturationInput; + static const QString kPivotInput; + static const QString kPivotBlackInput; + static const QString kPivotWhiteInput; + static const QString kClampBlackInput; + static const QString kClampWhiteInput; + + const QString shader_text_; + + ColorProcessorPtr processor_; + OCIO::GpuShaderDescRcPtr shader_desc_; + + QString display_; + QString view_; +}; + +} // olive + +#endif diff --git a/app/node/factory.cpp b/app/node/factory.cpp index f2dd7934c..4f1e0ae18 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -30,6 +30,7 @@ #include "block/transition/crossdissolve/crossdissolvetransition.h" #include "block/transition/diptocolor/diptocolortransition.h" #include "color/displaytransform/displaytransform.h" +#include "color/ociogradingtransform/ociogradingtransform.h" #include "distort/cornerpin/cornerpindistortnode.h" #include "distort/crop/cropdistortnode.h" #include "distort/flip/flipdistortnode.h" @@ -270,6 +271,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id) return new CornerPinDistortNode(); case kDisplayTransform: return new DisplayTransformNode(); + case kOCIOGradingTransform: + return new OCIOGradingTransformNode(); case kInternalNodeCount: break; diff --git a/app/node/factory.h b/app/node/factory.h index 58e2673a9..086ae9c3e 100644 --- a/app/node/factory.h +++ b/app/node/factory.h @@ -68,6 +68,7 @@ public: kTimeOffsetNode, kCornerPinDistort, kDisplayTransform, + kOCIOGradingTransform, // Count value kInternalNodeCount diff --git a/app/render/colorprocessor.cpp b/app/render/colorprocessor.cpp index 6198b6d94..6080aae54 100644 --- a/app/render/colorprocessor.cpp +++ b/app/render/colorprocessor.cpp @@ -25,6 +25,9 @@ #include "node/color/colormanager/colormanager.h" namespace olive { +ColorProcessor::ColorProcessor() +{ +} ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const ColorTransform &transform) { diff --git a/app/render/colorprocessor.h b/app/render/colorprocessor.h index 60205055a..9a7c5552b 100644 --- a/app/render/colorprocessor.h +++ b/app/render/colorprocessor.h @@ -42,12 +42,14 @@ public: }; ColorProcessor(ColorManager* config, const QString& input, const ColorTransform& dest_space); + ColorProcessor(); DISABLE_COPY_MOVE(ColorProcessor) static ColorProcessorPtr Create(ColorManager* config, const QString& input, const ColorTransform& dest_space); OCIO::ConstProcessorRcPtr GetProcessor(); + void SetProsessor(OCIO::ConstProcessorRcPtr processor) { processor_ = processor; }; void ConvertFrame(FramePtr f); void ConvertFrame(Frame* f); diff --git a/app/shaders/ociogradingtransform.frag b/app/shaders/ociogradingtransform.frag new file mode 100644 index 000000000..ca1841dc2 --- /dev/null +++ b/app/shaders/ociogradingtransform.frag @@ -0,0 +1,13 @@ +uniform sampler2D tex_in; +varying vec2 ove_texcoord; + +// Program will replace this with OCIO's auto-generated shader code +%1 + +void main() { + vec4 col = texture2D(tex_in, ove_texcoord); + + col = OCIOGradingTransform(col); + + gl_FragColor = col; +}