From f38f3ae28458cd339c5e10f99fe79501b1b7db80 Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Thu, 6 Jun 2024 15:42:36 -0400 Subject: [PATCH 01/30] Hue Curve Transform GPU (cherry picked from commit 393c0f6cf42bd04ed2214eb5c537785927f386ac) Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTransforms.h | 122 +++- include/OpenColorIO/OpenColorTypes.h | 41 +- src/OpenColorIO/CMakeLists.txt | 5 + src/OpenColorIO/DynamicProperty.cpp | 112 +++- src/OpenColorIO/DynamicProperty.h | 38 ++ src/OpenColorIO/GpuShaderUtils.cpp | 37 ++ src/OpenColorIO/GpuShaderUtils.h | 6 + src/OpenColorIO/Op.cpp | 14 + src/OpenColorIO/Op.h | 1 + src/OpenColorIO/OpBuilders.h | 6 + src/OpenColorIO/OpOptimizers.cpp | 1 + src/OpenColorIO/Transform.cpp | 11 + .../fileformats/ctf/CTFReaderUtils.h | 10 + .../fileformats/ctf/CTFTransform.cpp | 153 +++++ .../ExposureContrastOpCPU.cpp | 2 + .../ExposureContrastOpData.cpp | 3 + .../ops/fixedfunction/FixedFunctionOpData.cpp | 49 +- .../ops/fixedfunction/FixedFunctionOpData.h | 6 + .../ops/fixedfunction/FixedFunctionOpGPU.cpp | 157 +++++- .../ops/fixedfunction/FixedFunctionOpGPU.h | 4 + .../gradingrgbcurve/GradingBSplineCurve.cpp | 524 +++++++++++++++++- .../ops/gradingrgbcurve/GradingBSplineCurve.h | 29 +- .../ops/gradingrgbcurve/GradingRGBCurveOp.cpp | 1 - .../ops/gradingrgbcurve/HueCurve.cpp | 245 ++++++++ .../ops/gradingrgbcurve/HueCurve.h | 56 ++ .../ops/gradingrgbcurve/HueCurveOp.cpp | 256 +++++++++ .../ops/gradingrgbcurve/HueCurveOp.h | 30 + .../ops/gradingrgbcurve/HueCurveOpData.cpp | 230 ++++++++ .../ops/gradingrgbcurve/HueCurveOpData.h | 88 +++ .../ops/gradingrgbcurve/HueCurveOpGPU.cpp | 496 +++++++++++++++++ .../ops/gradingrgbcurve/HueCurveOpGPU.h | 22 + .../transforms/HueCurveTransform.cpp | 189 +++++++ .../transforms/HueCurveTransform.h | 68 +++ tests/cpu/CMakeLists.txt | 5 + .../gradingrgbcurve/HueCurveOpData_tests.cpp | 138 +++++ .../ops/gradingrgbcurve/HueCurveOp_tests.cpp | 138 +++++ .../ops/gradingrgbcurve/HueCurve_tests.cpp | 130 +++++ .../transforms/HueCurveTransform_tests.cpp | 317 +++++++++++ tests/gpu/CMakeLists.txt | 1 + tests/gpu/HueCurveOp_test.cpp | 175 ++++++ tests/osl/HueCurveOp_test.cpp | 177 ++++++ 41 files changed, 4059 insertions(+), 34 deletions(-) create mode 100644 src/OpenColorIO/ops/gradingrgbcurve/HueCurve.cpp create mode 100644 src/OpenColorIO/ops/gradingrgbcurve/HueCurve.h create mode 100644 src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.cpp create mode 100644 src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.h create mode 100644 src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.cpp create mode 100644 src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.h create mode 100644 src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.cpp create mode 100644 src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.h create mode 100644 src/OpenColorIO/transforms/HueCurveTransform.cpp create mode 100644 src/OpenColorIO/transforms/HueCurveTransform.h create mode 100644 tests/cpu/ops/gradingrgbcurve/HueCurveOpData_tests.cpp create mode 100644 tests/cpu/ops/gradingrgbcurve/HueCurveOp_tests.cpp create mode 100644 tests/cpu/ops/gradingrgbcurve/HueCurve_tests.cpp create mode 100644 tests/cpu/transforms/HueCurveTransform_tests.cpp create mode 100644 tests/gpu/HueCurveOp_test.cpp create mode 100644 tests/osl/HueCurveOp_test.cpp diff --git a/include/OpenColorIO/OpenColorTransforms.h b/include/OpenColorIO/OpenColorTransforms.h index e37c59e4c2..a9df79e6a4 100644 --- a/include/OpenColorIO/OpenColorTransforms.h +++ b/include/OpenColorIO/OpenColorTransforms.h @@ -512,9 +512,9 @@ class OCIOEXPORT GradingBSplineCurve { public: /// Create a BSpline curve with a specified number of control points. - static GradingBSplineCurveRcPtr Create(size_t size); + static GradingBSplineCurveRcPtr Create(size_t size, BSplineCurveType curveType = B_SPLINE); /// Create a BSpline curve with a list of control points. - static GradingBSplineCurveRcPtr Create(std::initializer_list values); + static GradingBSplineCurveRcPtr Create(std::initializer_list values, BSplineCurveType curveType = B_SPLINE); virtual GradingBSplineCurveRcPtr createEditableCopy() const = 0; virtual size_t getNumControlPoints() const noexcept = 0; @@ -525,6 +525,8 @@ class OCIOEXPORT GradingBSplineCurve virtual void setSlope(size_t index, float slope) = 0; virtual bool slopesAreDefault() const = 0; virtual void validate() const = 0; + virtual BSplineCurveType getCurveType() const = 0; + virtual void setCurveType(BSplineCurveType curveType) = 0; GradingBSplineCurve(const GradingBSplineCurve &) = delete; GradingBSplineCurve & operator= (const GradingBSplineCurve &) = delete; @@ -571,6 +573,34 @@ extern OCIOEXPORT bool operator==(const GradingRGBCurve & lhs, const GradingRGBC extern OCIOEXPORT bool operator!=(const GradingRGBCurve & lhs, const GradingRGBCurve & rhs); extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingRGBCurve &); +/** + * A set of HUE/SAT/LUM curves. It is used by HueCurveTransform and can be used as + * a dynamic property (see \ref DynamicPropertyHueCurve). + */ +class OCIOEXPORT HueCurve +{ +public: + static HueCurveRcPtr Create(GradingStyle style); + static HueCurveRcPtr Create(const ConstHueCurveRcPtr & rhs); + static HueCurveRcPtr Create(const std::array & curves); + + virtual HueCurveRcPtr createEditableCopy() const = 0; + virtual void validate() const = 0; + virtual bool isIdentity() const = 0; + virtual ConstGradingBSplineCurveRcPtr getCurve(HueCurveType c) const = 0; + virtual GradingBSplineCurveRcPtr getCurve(HueCurveType c) = 0; + + /// Do not use (needed only for pybind11). + virtual ~HueCurve() = default; + +protected: + HueCurve() = default; +}; + +extern OCIOEXPORT bool operator==(const HueCurve & lhs, const HueCurve & rhs); +extern OCIOEXPORT bool operator!=(const HueCurve & lhs, const HueCurve & rhs); +extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const HueCurve &); + /** * Used by the grading tone transforms to hold the red, green, blue, master, start, * and width components of a single parameter. The master component affects all three channels @@ -734,6 +764,11 @@ extern OCIOEXPORT DynamicPropertyGradingPrimaryRcPtr AsGradingPrimary(DynamicPro * value. Will throw if property type is not DYNAMIC_PROPERTY_GRADING_RGBCURVE. */ extern OCIOEXPORT DynamicPropertyGradingRGBCurveRcPtr AsGradingRGBCurve(DynamicPropertyRcPtr & prop); +/** + * Get the property as DynamicPropertyHueCurveRcPtr to access the HueCurveRcPtr + * value. Will throw if property type is not DYNAMIC_PROPERTY_HUE_CURVE. + */ +extern OCIOEXPORT DynamicPropertyHueCurveRcPtr AsHueCurve(DynamicPropertyRcPtr & prop); /** * Get the property as DynamicPropertyGradingToneRcPtr to access the GradingTone value. Will throw * if property type is not DYNAMIC_PROPERTY_GRADING_TONE. @@ -790,6 +825,21 @@ class OCIOEXPORT DynamicPropertyGradingRGBCurve protected: DynamicPropertyGradingRGBCurve() = default; }; +class OCIOEXPORT DynamicPropertyHueCurve +{ +public: + virtual const ConstHueCurveRcPtr & getValue() const = 0; + /// Will throw if value is not valid. + virtual void setValue(const ConstHueCurveRcPtr & value) = 0; + + DynamicPropertyHueCurve(const DynamicPropertyHueCurve &) = delete; + DynamicPropertyHueCurve & operator=(const DynamicPropertyHueCurve &) = delete; + /// Do not use (needed only for pybind11). + virtual ~DynamicPropertyHueCurve() = default; + +protected: + DynamicPropertyHueCurve() = default; +}; /// Interface used to access dynamic property GradingTone value. class OCIOEXPORT DynamicPropertyGradingTone @@ -1196,6 +1246,74 @@ class OCIOEXPORT GradingPrimaryTransform : public Transform extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingPrimaryTransform &) noexcept; +/** + * Hue color correction controls. + * + * TODO: Description. + */ +class OCIOEXPORT HueCurveTransform : public Transform +{ +public: + /// Creates an instance of HueCurveTransform. + static HueCurveTransformRcPtr Create(GradingStyle style); + + TransformType getTransformType() const noexcept override { return TRANSFORM_TYPE_HUE_CURVE; } +// + virtual const FormatMetadata & getFormatMetadata() const noexcept = 0; + virtual FormatMetadata & getFormatMetadata() noexcept = 0; +// + ///// Checks if this equals other. + virtual bool equals(const HueCurveTransform & other) const noexcept = 0; +// + ///// Adjusts the behavior of the transform for log, linear, or video color space encodings. + virtual GradingStyle getStyle() const noexcept = 0; + ///// Will reset value to style's defaults if style is not the current style. + virtual void setStyle(GradingStyle style) noexcept = 0; +// + virtual const ConstHueCurveRcPtr getValue() const = 0; + ///// Throws if value is not valid. + //virtual + virtual void setValue(const ConstHueCurveRcPtr & values) = 0; + + /** + * It is possible to provide a desired slope value for each control point. The number of slopes is + * always the same as the number of control points and so the control points must be set before + * setting the slopes. The slopes are primarily intended for use by config authors looking to match + * a specific shape with as few control points as possible, they are not intended to be exposed to + * a user interface for direct manipulation. When a curve is being generated for creative purposes + * it is better to let OCIO calculate the slopes automatically. + */ + virtual float getSlope(HueCurveType c, size_t index) const = 0; + virtual void setSlope(HueCurveType c, size_t index, float slope) = 0; + virtual bool slopesAreDefault(HueCurveType c) const = 0; + + /** + * The scene-linear grading style applies a lin-to-log transform to the pixel + * values before going through the curve. However, in some cases (e.g. drawing curves in a UI) + * it may be useful to bypass the lin-to-log. Default value is false. + */ + virtual bool getBypassLinToLog() const = 0; + virtual void setBypassLinToLog(bool bypass) = 0; +// + ///** + // * Parameters can be made dynamic so the values can be changed through the CPU or GPU processor, + // * but if there are several GradingPrimaryTransform only one can have dynamic parameters. + // */ + virtual bool isDynamic() const noexcept = 0; + virtual void makeDynamic() noexcept = 0; + virtual void makeNonDynamic() noexcept = 0; + + HueCurveTransform(const HueCurveTransform &) = delete; + HueCurveTransform & operator= (const HueCurveTransform &) = delete; + /// Do not use (needed only for pybind11). + virtual ~HueCurveTransform() = default; + +protected: + HueCurveTransform() = default; +}; + +extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const HueCurveTransform &) noexcept; + /** * RGB curve color correction controls. diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 21243eb363..4001b394b6 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -114,6 +114,10 @@ class OCIOEXPORT GradingRGBCurve; typedef OCIO_SHARED_PTR ConstGradingRGBCurveRcPtr; typedef OCIO_SHARED_PTR GradingRGBCurveRcPtr; +class OCIOEXPORT HueCurve; +typedef OCIO_SHARED_PTR ConstHueCurveRcPtr; +typedef OCIO_SHARED_PTR HueCurveRcPtr; + class OCIOEXPORT ConfigIOProxy; typedef OCIO_SHARED_PTR ConstConfigIOProxyRcPtr; typedef OCIO_SHARED_PTR ConfigIOProxyRcPtr; @@ -163,6 +167,10 @@ class OCIOEXPORT DynamicPropertyGradingRGBCurve; typedef OCIO_SHARED_PTR ConstDynamicPropertyGradingRGBCurveRcPtr; typedef OCIO_SHARED_PTR DynamicPropertyGradingRGBCurveRcPtr; +class OCIOEXPORT DynamicPropertyHueCurve; +typedef OCIO_SHARED_PTR ConstDynamicPropertyHueCurveRcPtr; +typedef OCIO_SHARED_PTR DynamicPropertyHueCurveRcPtr; + class OCIOEXPORT DynamicPropertyGradingTone; typedef OCIO_SHARED_PTR ConstDynamicPropertyGradingToneRcPtr; typedef OCIO_SHARED_PTR DynamicPropertyGradingToneRcPtr; @@ -191,6 +199,10 @@ class OCIOEXPORT GradingPrimaryTransform; typedef OCIO_SHARED_PTR ConstGradingPrimaryTransformRcPtr; typedef OCIO_SHARED_PTR GradingPrimaryTransformRcPtr; +class OCIOEXPORT HueCurveTransform; +typedef OCIO_SHARED_PTR ConstHueCurveTransformRcPtr; +typedef OCIO_SHARED_PTR HueCurveTransformRcPtr; + class OCIOEXPORT GradingRGBCurveTransform; typedef OCIO_SHARED_PTR ConstGradingRGBCurveTransformRcPtr; typedef OCIO_SHARED_PTR GradingRGBCurveTransformRcPtr; @@ -362,7 +374,8 @@ enum TransformType TRANSFORM_TYPE_LUT1D, TRANSFORM_TYPE_LUT3D, TRANSFORM_TYPE_MATRIX, - TRANSFORM_TYPE_RANGE + TRANSFORM_TYPE_RANGE, + TRANSFORM_TYPE_HUE_CURVE }; /** @@ -549,7 +562,8 @@ enum DynamicPropertyType DYNAMIC_PROPERTY_GAMMA, ///< Image gamma value (double floating point value) DYNAMIC_PROPERTY_GRADING_PRIMARY, ///< Used by GradingPrimaryTransform DYNAMIC_PROPERTY_GRADING_RGBCURVE, ///< Used by GradingRGBCurveTransform - DYNAMIC_PROPERTY_GRADING_TONE ///< Used by GradingToneTransform + DYNAMIC_PROPERTY_GRADING_TONE, ///< Used by GradingToneTransform + DYNAMIC_PROPERTY_HUE_CURVE ///< Used by GradingToneTransform }; /// Types for GradingRGBCurve. @@ -562,6 +576,29 @@ enum RGBCurveType RGB_NUM_CURVES }; +/// Types for HueCurve. +enum HueCurveType +{ + HUE_HUE = 0, + HUE_SAT, + HUE_LUM, + LUM_SAT, + SAT_SAT, + LUM_LUM, + SAT_LUM, + HUE_FX, + HUE_NUM_CURVES +}; + +enum BSplineCurveType +{ + B_SPLINE = 0, //!< Monotonic quadratic B-spline based function. + DIAGONAL_B_SPLINE, //!< Monotonic quadratic B-spline based function (newer algorithm). + HUE_HUE_B_SPLINE, //!< Special B-spline used for the hue vs. hue curve (monotonic and periodic). + PERIODIC_B_SPLINE, //!< Periodic non-monotonic B-spline used for some of the hue curves. + HORIZONTAL_B_SPLINE +}; + /// Types for uniform data. enum UniformDataType { diff --git a/src/OpenColorIO/CMakeLists.txt b/src/OpenColorIO/CMakeLists.txt index 7d2894da6b..81fe096f5d 100755 --- a/src/OpenColorIO/CMakeLists.txt +++ b/src/OpenColorIO/CMakeLists.txt @@ -104,7 +104,11 @@ set(SOURCES ops/gradingrgbcurve/GradingRGBCurveOpData.cpp ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp ops/gradingrgbcurve/GradingRGBCurveOp.cpp + ops/gradingrgbcurve/HueCurveOpData.cpp + ops/gradingrgbcurve/HueCurveOp.cpp + ops/gradingrgbcurve/HueCurveOpGPU.cpp ops/gradingrgbcurve/GradingRGBCurve.cpp + ops/gradingrgbcurve/HueCurve.cpp ops/gradingtone/GradingTone.cpp ops/gradingtone/GradingToneOpCPU.cpp ops/gradingtone/GradingToneOpData.cpp @@ -170,6 +174,7 @@ set(SOURCES transforms/FileTransform.cpp transforms/FixedFunctionTransform.cpp transforms/GradingPrimaryTransform.cpp + transforms/HueCurveTransform.cpp transforms/GradingRGBCurveTransform.cpp transforms/GradingToneTransform.cpp transforms/GroupTransform.cpp diff --git a/src/OpenColorIO/DynamicProperty.cpp b/src/OpenColorIO/DynamicProperty.cpp index 5885492cfe..c3cc6550d5 100644 --- a/src/OpenColorIO/DynamicProperty.cpp +++ b/src/OpenColorIO/DynamicProperty.cpp @@ -8,6 +8,7 @@ #include "ops/gradingprimary/GradingPrimaryOpData.h" #include "ops/gradingrgbcurve/GradingRGBCurve.h" #include "ops/gradingtone/GradingToneOpData.h" +#include "ops/gradingrgbcurve/HueCurve.h" namespace OCIO_NAMESPACE { @@ -32,6 +33,12 @@ DynamicPropertyGradingRGBCurveRcPtr AsGradingRGBCurve(DynamicPropertyRcPtr & pro if (res) return res; throw Exception("Dynamic property value is not a grading RGB curve."); } +DynamicPropertyHueCurveRcPtr AsHueCurve(DynamicPropertyRcPtr & prop) +{ + auto res = OCIO_DYNAMIC_POINTER_CAST(prop); + if (res) return res; + throw Exception("Dynamic property value is not a hue curve."); +} DynamicPropertyGradingToneRcPtr AsGradingTone(DynamicPropertyRcPtr & prop) { auto res = OCIO_DYNAMIC_POINTER_CAST(prop); @@ -96,6 +103,11 @@ bool DynamicPropertyImpl::equals(const DynamicPropertyImpl & rhs) const auto rhst = dynamic_cast(&rhs); return lhst && rhst && (lhst->getValue() == rhst->getValue()); } + case DYNAMIC_PROPERTY_HUE_CURVE: + { + auto lhst = dynamic_cast(this); + auto rhst = dynamic_cast(&rhs); + return lhst && rhst && (*lhst->getValue() == *rhst->getValue());} } // Different values. return false; @@ -254,8 +266,8 @@ unsigned int DynamicPropertyGradingRGBCurveImpl::GetMaxCoefs() void DynamicPropertyGradingRGBCurveImpl::precompute() { m_knotsCoefs.m_localBypass = false; - m_knotsCoefs.m_knotsArray.resize(0); - m_knotsCoefs.m_coefsArray.resize(0); + m_knotsCoefs.m_nCoefs = 0; + m_knotsCoefs.m_nKnots = 0; // Compute knots and coefficients for each control point and pack all knots and coefs of // all curves in one knots array and one coef array, using an offset array to find specific @@ -266,7 +278,7 @@ void DynamicPropertyGradingRGBCurveImpl::precompute() auto curveImpl = dynamic_cast(curve.get()); curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c)); } - if (m_knotsCoefs.m_knotsArray.empty()) m_knotsCoefs.m_localBypass = true; + if (m_knotsCoefs.m_nKnots <= 0) m_knotsCoefs.m_localBypass = true; } DynamicPropertyGradingRGBCurveImplRcPtr DynamicPropertyGradingRGBCurveImpl::createEditableCopy() const @@ -276,6 +288,100 @@ DynamicPropertyGradingRGBCurveImplRcPtr DynamicPropertyGradingRGBCurveImpl::crea return res; } + +DynamicPropertyHueCurveImpl::DynamicPropertyHueCurveImpl( + const ConstHueCurveRcPtr & value, bool dynamic) + : DynamicPropertyImpl(DYNAMIC_PROPERTY_HUE_CURVE, dynamic) +{ + m_hueCurve = HueCurve::Create(value); + // Convert control points from the UI into knots and coefficients for the apply. + precompute(); +} + +const ConstHueCurveRcPtr & DynamicPropertyHueCurveImpl::getValue() const +{ + return m_hueCurve; +} + +void DynamicPropertyHueCurveImpl::setValue(const ConstHueCurveRcPtr & value) +{ + value->validate(); + + m_hueCurve = value->createEditableCopy(); + // Convert control points from the UI into knots and coefficients for the apply. + precompute(); +} + +bool DynamicPropertyHueCurveImpl::getLocalBypass() const +{ + return m_knotsCoefs.m_localBypass; +} + +int DynamicPropertyHueCurveImpl::getNumKnots() const +{ + return static_cast(m_knotsCoefs.m_knotsArray.size()); +} + +int DynamicPropertyHueCurveImpl::getNumCoefs() const +{ + return static_cast(m_knotsCoefs.m_coefsArray.size()); +} + +const int * DynamicPropertyHueCurveImpl::getKnotsOffsetsArray() const +{ + return m_knotsCoefs.m_knotsOffsetsArray.data(); +} + +const int * DynamicPropertyHueCurveImpl::getCoefsOffsetsArray() const +{ + return m_knotsCoefs.m_coefsOffsetsArray.data(); +} + +const float * DynamicPropertyHueCurveImpl::getKnotsArray() const +{ + return m_knotsCoefs.m_knotsArray.data(); +} + +const float * DynamicPropertyHueCurveImpl::getCoefsArray() const +{ + return m_knotsCoefs.m_coefsArray.data(); +} + +unsigned int DynamicPropertyHueCurveImpl::GetMaxKnots() +{ + return GradingBSplineCurveImpl::KnotsCoefs::MAX_NUM_KNOTS; +} + +unsigned int DynamicPropertyHueCurveImpl::GetMaxCoefs() +{ + return GradingBSplineCurveImpl::KnotsCoefs::MAX_NUM_COEFS; +} + +void DynamicPropertyHueCurveImpl::precompute() +{ + m_knotsCoefs.m_localBypass = false; + m_knotsCoefs.m_nCoefs = 0; + m_knotsCoefs.m_nKnots = 0; + + // Compute knots and coefficients for each control point and pack all knots and coefs of + // all curves in one knots array and one coef array, using an offset array to find specific + // curve data. + for (const auto c : { HUE_HUE, HUE_SAT, HUE_LUM, LUM_SAT, SAT_SAT, LUM_LUM, SAT_LUM, HUE_FX }) + { + ConstGradingBSplineCurveRcPtr curve = m_hueCurve->getCurve(c); + auto curveImpl = dynamic_cast(curve.get()); + curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c)); + } + if (m_knotsCoefs.m_nKnots == 0) m_knotsCoefs.m_localBypass = true; +} + +DynamicPropertyHueCurveImplRcPtr DynamicPropertyHueCurveImpl::createEditableCopy() const +{ + auto res = std::make_shared(getValue(), isDynamic()); + res->m_knotsCoefs = m_knotsCoefs; + return res; +} + DynamicPropertyGradingToneImpl::DynamicPropertyGradingToneImpl(const GradingTone & value, GradingStyle style, bool dynamic) diff --git a/src/OpenColorIO/DynamicProperty.h b/src/OpenColorIO/DynamicProperty.h index 0e398d9b30..a23a32466c 100644 --- a/src/OpenColorIO/DynamicProperty.h +++ b/src/OpenColorIO/DynamicProperty.h @@ -173,6 +173,44 @@ class DynamicPropertyGradingRGBCurveImpl : public DynamicPropertyImpl, GradingBSplineCurveImpl::KnotsCoefs m_knotsCoefs{ 4 }; }; +class DynamicPropertyHueCurveImpl; +typedef OCIO_SHARED_PTR DynamicPropertyHueCurveImplRcPtr; + +class DynamicPropertyHueCurveImpl : public DynamicPropertyImpl, + public DynamicPropertyHueCurve +{ +public: + DynamicPropertyHueCurveImpl() = delete; + DynamicPropertyHueCurveImpl(const ConstHueCurveRcPtr & value, bool dynamic); + ~DynamicPropertyHueCurveImpl() = default; + const ConstHueCurveRcPtr & getValue() const override; + void setValue(const ConstHueCurveRcPtr & value) override; + + bool getLocalBypass() const; + int getNumKnots() const; + int getNumCoefs() const; + static int GetNumOffsetValues() { return 8; } + const int * getKnotsOffsetsArray() const; + const int * getCoefsOffsetsArray() const; + const float * getKnotsArray() const; + const float * getCoefsArray() const; + + const GradingBSplineCurveImpl::KnotsCoefs & getKnotsCoefs() const { return m_knotsCoefs; } + + static unsigned int GetMaxKnots(); + static unsigned int GetMaxCoefs(); + + DynamicPropertyHueCurveImplRcPtr createEditableCopy() const; + +private: + void precompute(); + + ConstHueCurveRcPtr m_hueCurve; + + // Holds curve data as knots and coefs. There is 8 curve. + GradingBSplineCurveImpl::KnotsCoefs m_knotsCoefs{ 8 }; +}; + class DynamicPropertyGradingToneImpl; typedef OCIO_SHARED_PTR DynamicPropertyGradingToneImplRcPtr; diff --git a/src/OpenColorIO/GpuShaderUtils.cpp b/src/OpenColorIO/GpuShaderUtils.cpp index f8cbc88907..dd2612c56d 100644 --- a/src/OpenColorIO/GpuShaderUtils.cpp +++ b/src/OpenColorIO/GpuShaderUtils.cpp @@ -1353,6 +1353,25 @@ void AddLinToLogShader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st st.newLine() << "}"; } +void AddLinToLogShaderChannelBlue(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st) +{ + const std::string pix(shaderCreator->getPixelName()); + + st.newLine() << "{"; // establish scope so local variable names won't conflict + st.indent(); + st.newLine() << st.floatKeywordConst() << " xbrk = 0.0041318374739483946;"; + st.newLine() << st.floatKeywordConst() << " shift = -0.000157849851665374;"; + st.newLine() << st.floatKeywordConst() << " m = 1. / (0.18 + shift);"; + st.newLine() << st.floatKeywordConst() << " base2 = 1.4426950408889634;"; // 1/log(2) + st.newLine() << st.floatKeywordConst() << " gain = 363.034608563;"; + st.newLine() << st.floatKeywordConst() << " offs = -7.;"; + st.newLine() << st.float3Decl("ylin") << " = " << pix << ".rgb * gain + offs;"; + st.newLine() << st.float3Decl("ylog") << " = base2 * log( ( " << pix << ".rgb + shift ) * m );"; + st.newLine() << pix << ".rgb.b = (" << pix << ".rgb.b < xbrk) ? ylin.z : ylog.z;"; + st.dedent(); + st.newLine() << "}"; +} + void AddLogToLinShader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st) { const std::string pix(shaderCreator->getPixelName()); @@ -1373,4 +1392,22 @@ void AddLogToLinShader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st st.newLine() << "}"; } +void AddLogToLinShaderChannelBlue(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st) +{ + const std::string pix(shaderCreator->getPixelName()); + + st.newLine() << "{"; // establish scope so local variable names won't conflict + st.indent(); + st.newLine() << st.floatKeywordConst() << " ybrk = -5.5;"; + st.newLine() << st.floatKeywordConst() << " shift = -0.000157849851665374;"; + st.newLine() << st.floatKeywordConst() << " gain = 363.034608563;"; + st.newLine() << st.floatKeywordConst() << " offs = -7.;"; + st.newLine() << st.float3Decl("xlin") << " = (" << pix << ".rgb - offs) / gain;"; + st.newLine() << st.float3Decl("xlog") << " = pow( " << st.float3Const(2.0f) + << ", " << pix << ".rgb ) * (0.18 + shift) - shift;"; + st.newLine() << pix << ".rgb.b = (" << pix << ".rgb.b < ybrk) ? xlin.z : xlog.z;"; + st.dedent(); + st.newLine() << "}"; +} + } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/GpuShaderUtils.h b/src/OpenColorIO/GpuShaderUtils.h index 54fe58f62f..dcf16e9e70 100644 --- a/src/OpenColorIO/GpuShaderUtils.h +++ b/src/OpenColorIO/GpuShaderUtils.h @@ -269,9 +269,15 @@ std::string BuildResourceName(GpuShaderCreatorRcPtr & shaderCreator, const std:: // Convert scene-linear values to "grading log". void AddLinToLogShader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st); + +void AddLinToLogShaderChannelBlue(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st); + // Convert "grading log" values to scene-linear. void AddLogToLinShader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st); + +void AddLogToLinShaderChannelBlue(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st); + } // namespace OCIO_NAMESPACE #endif diff --git a/src/OpenColorIO/Op.cpp b/src/OpenColorIO/Op.cpp index 81dfe9867e..e761d6ad3f 100755 --- a/src/OpenColorIO/Op.cpp +++ b/src/OpenColorIO/Op.cpp @@ -17,6 +17,7 @@ #include "ops/gamma/GammaOp.h" #include "ops/gradingprimary/GradingPrimaryOp.h" #include "ops/gradingrgbcurve/GradingRGBCurveOp.h" +#include "ops/gradingrgbcurve/HueCurveOp.h" #include "ops/gradingtone/GradingToneOp.h" #include "ops/log/LogOp.h" #include "ops/lut1d/Lut1DOp.h" @@ -120,6 +121,8 @@ const char * GetTypeName(OpData::Type type) return "GradingPrimary"; case OpData::GradingRGBCurveType: return "GradingRGBCurve"; + case OpData::GradingHueCurveType : + return "HueCurve"; case OpData::GradingToneType: return "GradingTone"; case OpData::LogType: @@ -404,6 +407,9 @@ void ValidateDynamicProperty(OpRcPtr op, std::shared_ptr & prop, DynamicPrope case DYNAMIC_PROPERTY_GRADING_TONE: os << "Grading tone"; break; + case DYNAMIC_PROPERTY_HUE_CURVE: + os << "Hue curve"; + break; } os << " dynamic property can only be there once."; LogWarning(os.str()); @@ -542,6 +548,14 @@ void CreateOpVecFromOpData(OpRcPtrVec & ops, break; } + case OpData::GradingHueCurveType: + { + auto hueSrc = std::dynamic_pointer_cast(opData); + auto hue = std::make_shared(*hueSrc); + CreateHueCurveOp(ops, hue, dir); + break; + } + case OpData::GradingToneType: { auto toneSrc = std::dynamic_pointer_cast(opData); diff --git a/src/OpenColorIO/Op.h b/src/OpenColorIO/Op.h index d88d8a1f4b..b688c26764 100644 --- a/src/OpenColorIO/Op.h +++ b/src/OpenColorIO/Op.h @@ -104,6 +104,7 @@ class OpData GammaType, // A gamma (i.e. enhancement of the Exponent) GradingPrimaryType, // A set of primary grading controls GradingRGBCurveType, // A rgb curve + GradingHueCurveType, // A hue curve GradingToneType, // A set of grading controls for tonal ranges LogType, // A log Lut1DType, // A 1D LUT diff --git a/src/OpenColorIO/OpBuilders.h b/src/OpenColorIO/OpBuilders.h index cfeabcf6dd..81c6b4620e 100644 --- a/src/OpenColorIO/OpBuilders.h +++ b/src/OpenColorIO/OpBuilders.h @@ -100,6 +100,12 @@ void BuildGradingPrimaryOp(OpRcPtrVec & ops, const GradingPrimaryTransform & transform, TransformDirection dir); +void BuildHueCurveOp(OpRcPtrVec & ops, + const Config & config, + const ConstContextRcPtr & context, + const HueCurveTransform & transform, + TransformDirection dir); + void BuildGradingRGBCurveOp(OpRcPtrVec & ops, const Config & config, const ConstContextRcPtr & context, diff --git a/src/OpenColorIO/OpOptimizers.cpp b/src/OpenColorIO/OpOptimizers.cpp index 58b395d5c6..164123f413 100755 --- a/src/OpenColorIO/OpOptimizers.cpp +++ b/src/OpenColorIO/OpOptimizers.cpp @@ -41,6 +41,7 @@ bool IsPairInverseEnabled(OpData::Type type, OptimizationFlags flags) case OpData::GradingPrimaryType: case OpData::GradingRGBCurveType: + case OpData::GradingHueCurveType: case OpData::GradingToneType: return HasFlag(flags, OPTIMIZATION_PAIR_IDENTITY_GRADING); diff --git a/src/OpenColorIO/Transform.cpp b/src/OpenColorIO/Transform.cpp index 0d723212e6..b006d2cc34 100755 --- a/src/OpenColorIO/Transform.cpp +++ b/src/OpenColorIO/Transform.cpp @@ -15,6 +15,7 @@ #include "ops/gamma/GammaOp.h" #include "ops/gradingprimary/GradingPrimaryOp.h" #include "ops/gradingrgbcurve/GradingRGBCurveOp.h" +#include "ops/gradingrgbcurve/HueCurveOp.h" #include "ops/gradingtone/GradingToneOp.h" #include "ops/log/LogOp.h" #include "ops/lut1d/Lut1DOp.h" @@ -108,6 +109,11 @@ void BuildOps(OpRcPtrVec & ops, { BuildGradingRGBCurveOp(ops, config, context, *gradingCurveTransform, dir); } + else if (ConstHueCurveTransformRcPtr hueCurveTransform = \ + DynamicPtrCast(transform)) + { + BuildHueCurveOp(ops, config, context, *hueCurveTransform, dir); + } else if (ConstGradingToneTransformRcPtr gradingToneTransform = \ DynamicPtrCast(transform)) { @@ -232,6 +238,11 @@ std::ostream& operator<< (std::ostream & os, const Transform & transform) { os << *gradingRGBCurveTransform; } + else if (const HueCurveTransform * hueCurveTransform = \ + dynamic_cast(t)) + { + os << *hueCurveTransform; + } else if (const GradingToneTransform * gradingToneTransform = \ dynamic_cast(t)) { diff --git a/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h b/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h index 03f2d9c087..b7fc5ba416 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h +++ b/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h @@ -29,6 +29,7 @@ static constexpr char TAG_DYN_PROP_CONTRAST[] = "CONTRAST"; static constexpr char TAG_DYN_PROP_EXPOSURE[] = "EXPOSURE"; static constexpr char TAG_DYN_PROP_GAMMA[] = "GAMMA"; static constexpr char TAG_DYN_PROP_PRIMARY[] = "PRIMARY"; +static constexpr char TAG_DYN_PROP_HUECURVE[] = "HUE_CURVE"; static constexpr char TAG_DYN_PROP_RGBCURVE[] = "RGB_CURVE"; static constexpr char TAG_DYN_PROP_TONE[] = "TONE"; static constexpr char TAG_DYN_PROP_LOOK[] = "LOOK_SWITCH"; @@ -68,6 +69,15 @@ static constexpr char TAG_PRIMARY_SATURATION[] = "Saturation"; static constexpr char TAG_PROCESS_LIST[] = "ProcessList"; static constexpr char TAG_RANGE[] = "Range"; static constexpr char TAG_REFERENCE[] = "Reference"; +static constexpr char TAG_HUE_CURVE[] = "HueCurve"; +static constexpr char TAG_HUE_CURVE_HUE_HUE[] = "hue_hue"; +static constexpr char TAG_HUE_CURVE_HUE_SAT[] = "hue_sat"; +static constexpr char TAG_HUE_CURVE_HUE_LUM[] = "hue_lum"; +static constexpr char TAG_HUE_CURVE_LUM_SAT[] = "lum_sat"; +static constexpr char TAG_HUE_CURVE_SAT_SAT[] = "sat_sat"; +static constexpr char TAG_HUE_CURVE_LUM_LUM[] = "lum_lum"; +static constexpr char TAG_HUE_CURVE_SAT_LUM[] = "sat_lum"; +static constexpr char TAG_HUE_CURVE_HUE_FX[] = "hue_fx"; static constexpr char TAG_RGB_CURVE[] = "GradingRGBCurve"; static constexpr char TAG_RGB_CURVE_BLUE[] = "Blue"; static constexpr char TAG_RGB_CURVE_GREEN[] = "Green"; diff --git a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp index cf07176568..5ea20c92b8 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp @@ -16,6 +16,8 @@ #include "ops/gradingprimary/GradingPrimaryOpData.h" #include "ops/gradingrgbcurve/GradingRGBCurve.h" #include "ops/gradingrgbcurve/GradingRGBCurveOpData.h" +#include "ops/gradingrgbcurve/HueCurve.h" +#include "ops/gradingrgbcurve/HueCurveOpData.h" #include "ops/gradingtone/GradingToneOpData.h" #include "ops/log/LogOpData.h" #include "ops/log/LogUtils.h" @@ -276,6 +278,7 @@ CTFVersion GetOpMinimumVersion(const ConstOpDataRcPtr & op) } case OpData::GradingPrimaryType: case OpData::GradingRGBCurveType: + case OpData::GradingHueCurveType: case OpData::GradingToneType: case OpData::LogType: { @@ -1613,6 +1616,142 @@ void GradingRGBCurveWriter::writeContent() const m_formatter.writeEmptyTag(TAG_DYNAMIC_PARAMETER, attributes); } } +/////////////////////////////////////////////////////////////////////////////// + +class HueCurveWriter : public OpWriter +{ +public: + HueCurveWriter() = delete; + HueCurveWriter(const HueCurveWriter&) = delete; + HueCurveWriter& operator=(const HueCurveWriter&) = delete; + HueCurveWriter(XmlFormatter & formatter, ConstHueCurveOpDataRcPtr primary); + virtual ~HueCurveWriter(); + +protected: + ConstOpDataRcPtr getOp() const override; + const char * getTagName() const override; + void getAttributes(XmlFormatter::Attributes & attributes) const override; + void writeContent() const override; + +private: + void writeCurve(const char * tag, const ConstGradingBSplineCurveRcPtr & curve) const; + ConstHueCurveOpDataRcPtr m_curves; +}; + +HueCurveWriter::HueCurveWriter(XmlFormatter & formatter, + ConstHueCurveOpDataRcPtr curves) + : OpWriter(formatter) + , m_curves(curves) +{ +} + +HueCurveWriter::~HueCurveWriter() +{ +} + +ConstOpDataRcPtr HueCurveWriter::getOp() const +{ + return m_curves; +} + +const char * HueCurveWriter::getTagName() const +{ + return TAG_HUE_CURVE; +} + +void HueCurveWriter::getAttributes(XmlFormatter::Attributes& attributes) const +{ + OpWriter::getAttributes(attributes); + + const auto style = m_curves->getStyle(); + const auto dir = m_curves->getDirection(); + + const auto styleStr = ConvertGradingStyleAndDirToString(style, dir); + attributes.push_back(XmlFormatter::Attribute(ATTR_STYLE, styleStr)); + + if (m_curves->getBypassLinToLog()) + { + attributes.push_back(XmlFormatter::Attribute(ATTR_BYPASS_LIN_TO_LOG, "true")); + } +} + +void HueCurveWriter::writeCurve(const char * tag, + const ConstGradingBSplineCurveRcPtr & curve) const +{ + m_formatter.writeStartTag(tag, XmlFormatter::Attributes()); + { + XmlScopeIndent si0(m_formatter); + m_formatter.writeStartTag(TAG_CURVE_CTRL_PNTS, XmlFormatter::Attributes()); + { + XmlScopeIndent si1(m_formatter); + const size_t numPnts = curve->getNumControlPoints(); + + // Write 1 control point per line in the form of "X Y" + for (size_t i = 0; i < numPnts; ++i) + { + const auto & ctPt = curve->getControlPoint(i); + std::ostringstream oss; + SetOStream(0.f, oss); + oss << ctPt.m_x << " " << ctPt.m_y; + m_formatter.writeContent(oss.str()); + } + } + m_formatter.writeEndTag(TAG_CURVE_CTRL_PNTS); + + if (!curve->slopesAreDefault()) + { + m_formatter.writeStartTag(TAG_CURVE_SLOPES, XmlFormatter::Attributes()); + { + XmlScopeIndent si1(m_formatter); + // (Number of slopes is always the same as control points.) + const size_t numSlopes = curve->getNumControlPoints(); + std::ostringstream oss; + SetOStream(0.f, oss); + for (size_t i = 0; i < numSlopes; ++i) + { + const float val = curve->getSlope(i); + oss << val << " "; + } + m_formatter.writeContent(oss.str()); + } + m_formatter.writeEndTag(TAG_CURVE_SLOPES); + } + } + + m_formatter.writeEndTag(tag); +} + +void HueCurveWriter::writeContent() const +{ + const auto & vals = m_curves->getValue(); + + auto & defCurve = (m_curves->getStyle() == GRADING_LIN) ? HueCurveImpl::DefaultCurvesLin: + HueCurveImpl::DefaultCurves; + static const std::vector curveTags = { + TAG_HUE_CURVE_HUE_HUE, + TAG_HUE_CURVE_HUE_SAT, + TAG_HUE_CURVE_HUE_LUM, + TAG_HUE_CURVE_LUM_SAT, + TAG_HUE_CURVE_SAT_SAT, + TAG_HUE_CURVE_LUM_LUM, + TAG_HUE_CURVE_SAT_LUM, + TAG_HUE_CURVE_HUE_FX + }; + for (int c = 0; c < HUE_NUM_CURVES; ++c) + { + const auto & curve = vals->getCurve(static_cast(c)); + if ((*curve != defCurve[c]) || !(curve->slopesAreDefault())) + { + writeCurve(curveTags[c], curve); + } + } + if (m_curves->isDynamic()) + { + XmlFormatter::Attributes attributes; + attributes.push_back(XmlFormatter::Attribute(ATTR_PARAM, TAG_DYN_PROP_HUECURVE)); + m_formatter.writeEmptyTag(TAG_DYNAMIC_PARAMETER, attributes); + } +} /////////////////////////////////////////////////////////////////////////////// @@ -2694,6 +2833,20 @@ void TransformWriter::writeOps(const CTFVersion & version) const opWriter.write(); break; } + case OpData::GradingHueCurveType: + { + if (m_isCLF) + { + ThrowWriteOp("HueCurve"); + } + + auto hue = OCIO_DYNAMIC_POINTER_CAST(op); + HueCurveWriter opWriter(m_formatter, hue); + opWriter.setInputBitdepth(inBD); + opWriter.setOutputBitdepth(outBD); + opWriter.write(); + break; + } case OpData::GradingToneType: { if (m_isCLF) diff --git a/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpCPU.cpp b/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpCPU.cpp index d2288344c9..de35d72967 100644 --- a/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpCPU.cpp +++ b/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpCPU.cpp @@ -86,6 +86,7 @@ bool ECRendererBase::hasDynamicProperty(DynamicPropertyType type) const break; case DYNAMIC_PROPERTY_GRADING_PRIMARY: case DYNAMIC_PROPERTY_GRADING_RGBCURVE: + case DYNAMIC_PROPERTY_HUE_CURVE: case DYNAMIC_PROPERTY_GRADING_TONE: default: break; @@ -118,6 +119,7 @@ DynamicPropertyRcPtr ECRendererBase::getDynamicProperty(DynamicPropertyType type break; case DYNAMIC_PROPERTY_GRADING_PRIMARY: case DYNAMIC_PROPERTY_GRADING_RGBCURVE: + case DYNAMIC_PROPERTY_HUE_CURVE: case DYNAMIC_PROPERTY_GRADING_TONE: default: throw Exception("Dynamic property type not supported by ExposureContrast."); diff --git a/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpData.cpp b/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpData.cpp index 4197b7dde4..4c3179cbd7 100644 --- a/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpData.cpp +++ b/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpData.cpp @@ -311,6 +311,7 @@ bool ExposureContrastOpData::hasDynamicProperty(DynamicPropertyType type) const break; case DYNAMIC_PROPERTY_GRADING_PRIMARY: case DYNAMIC_PROPERTY_GRADING_RGBCURVE: + case DYNAMIC_PROPERTY_HUE_CURVE: case DYNAMIC_PROPERTY_GRADING_TONE: default: break; @@ -344,6 +345,7 @@ ExposureContrastOpData::getDynamicProperty(DynamicPropertyType type) const break; case DYNAMIC_PROPERTY_GRADING_PRIMARY: case DYNAMIC_PROPERTY_GRADING_RGBCURVE: + case DYNAMIC_PROPERTY_HUE_CURVE: case DYNAMIC_PROPERTY_GRADING_TONE: default: throw Exception("Dynamic property type not supported by ExposureContrast."); @@ -383,6 +385,7 @@ void ExposureContrastOpData::replaceDynamicProperty(DynamicPropertyType type, break; case DYNAMIC_PROPERTY_GRADING_PRIMARY: case DYNAMIC_PROPERTY_GRADING_RGBCURVE: + case DYNAMIC_PROPERTY_HUE_CURVE: case DYNAMIC_PROPERTY_GRADING_TONE: default: throw Exception("Dynamic property type not supported by ExposureContrast."); diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp index f4b32c8c15..bd20345095 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp @@ -77,7 +77,12 @@ constexpr char LIN_TO_GAMMA_LOG_STR[] = "Lin_TO_GammaLog"; constexpr char GAMMA_LOG_TO_LIN_STR[] = "GammaLog_TO_Lin"; constexpr char LIN_TO_DOUBLE_LOG_STR[] = "Lin_TO_DoubleLog"; constexpr char DOUBLE_LOG_TO_LIN_STR[] = "DoubleLog_TO_Lin"; - +constexpr char RGB_TO_HSY_LIN_STR[] = "RGB_TO_HSY_LIN"; +constexpr char RGB_TO_HSY_LOG_STR[] = "RGB_TO_HSY_LOG"; +constexpr char RGB_TO_HSY_VID_STR[] = "RGB_TO_HSY_VID"; +constexpr char HSY_LOG_TO_RGB_STR[] = "HSY_LOG_TO_RGB"; +constexpr char HSY_LIN_TO_RGB_STR[] = "HSY_LIN_TO_RGB"; +constexpr char HSY_VID_TO_RGB_STR[] = "HSY_VID_TO_RGB"; // NOTE: Converts the enumeration value to its string representation (i.e. CLF reader). // It could add details for error reporting. @@ -160,6 +165,18 @@ const char * FixedFunctionOpData::ConvertStyleToString(Style style, bool detaile return LIN_TO_DOUBLE_LOG_STR; case DOUBLE_LOG_TO_LIN: return DOUBLE_LOG_TO_LIN_STR; + case RGB_TO_HSY_LIN: + return RGB_TO_HSY_LIN_STR; + case RGB_TO_HSY_LOG: + return RGB_TO_HSY_LOG_STR; + case RGB_TO_HSY_VID: + return RGB_TO_HSY_VID_STR; + case HSY_LOG_TO_RGB: + return HSY_LOG_TO_RGB_STR; + case HSY_LIN_TO_RGB: + return HSY_LIN_TO_RGB_STR; + case HSY_VID_TO_RGB: + return HSY_VID_TO_RGB_STR; } std::stringstream ss("Unknown FixedFunction style: "); @@ -318,6 +335,30 @@ FixedFunctionOpData::Style FixedFunctionOpData::GetStyle(const char * name) { return DOUBLE_LOG_TO_LIN; } + else if (0 == Platform::Strcasecmp(name, RGB_TO_HSY_LIN_STR)) + { + return RGB_TO_HSY_LIN; + } + else if (0 == Platform::Strcasecmp(name, RGB_TO_HSY_LOG_STR)) + { + return RGB_TO_HSY_LOG; + } + else if (0 == Platform::Strcasecmp(name, RGB_TO_HSY_VID_STR)) + { + return RGB_TO_HSY_VID; + } + else if (0 == Platform::Strcasecmp(name, HSY_LOG_TO_RGB_STR)) + { + return HSY_LOG_TO_RGB; + } + else if (0 == Platform::Strcasecmp(name, HSY_LIN_TO_RGB_STR)) + { + return HSY_LIN_TO_RGB; + } + else if (0 == Platform::Strcasecmp(name, HSY_VID_TO_RGB_STR)) + { + return HSY_VID_TO_RGB; + } } std::string st("Unknown FixedFunction style: "); @@ -511,6 +552,12 @@ FixedFunctionStyle FixedFunctionOpData::ConvertStyle(FixedFunctionOpData::Style case FixedFunctionOpData::LIN_TO_DOUBLE_LOG: case FixedFunctionOpData::DOUBLE_LOG_TO_LIN: return FIXED_FUNCTION_LIN_TO_DOUBLE_LOG; + + // TODO: Implement the following conversions. + //case FixedFunctionOpData::RGB_TO_HSY_LIN: + //case FixedFunctionOpData::HSY_LIN_TO_RGB: + // return FIXED_FUNCTION_RGB_TO_HSY_LIN; + } std::stringstream ss("Unknown FixedFunction style: "); diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h index b3441ed158..b99150e695 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h @@ -62,6 +62,12 @@ class FixedFunctionOpData : public OpData ACES_TONESCALE_COMPRESS_20_INV, // ACES2 Tonescale and chroma compression (inv) ACES_GAMUT_COMPRESS_20_FWD, // ACES2 Gamut compression ACES_GAMUT_COMPRESS_20_INV // ACES2 Gamut compression (inv) + RGB_TO_HSY_LOG, // RGB to HSY (Hue, Saturation, Lightness) using log + RGB_TO_HSY_LIN, // RGB to HSY (Hue, Saturation, Lightness) using linear + RGB_TO_HSY_VID, // RGB to HSY (Hue, Saturation, Lightness) using video + HSY_LOG_TO_RGB, // HSY (Hue, Saturation, Lightness) using log to RGB + HSY_LIN_TO_RGB, // HSY (Hue, Saturation, Lightness) using linear to RGB + HSY_VID_TO_RGB, // HSY (Hue, Saturation, Lightness) using video to RGB }; static const char * ConvertStyleToString(Style style, bool detailed); diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp index be2ca6c352..dcd103a1e0 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp @@ -1692,6 +1692,124 @@ void Add_RGB_TO_HSV(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) ss.newLine() << pxl << ".rgb = " << ss.float3Const("hue * 1./6.", "sat", "val") << ";"; } +void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, float min0) +{ + ss.newLine() << ss.float3Decl("lumaWeights") << " = " << ss.float3Const(0.2126f, 0.7152f, 0.0722f) << ";"; + ss.newLine() << ss.float3Decl("ones") << " = " << ss.float3Const(1.f, 1.f, 1.f) << ";"; + ss.newLine() << "float luma = dot(outColor.rgb, lumaWeights);"; + ss.newLine() << "float minRGB = min( outColor.x, min( outColor.y, outColor.z ) );"; + ss.newLine() << "float maxRGB = max( outColor.x, max( outColor.y, outColor.z ) );"; + ss.newLine() << ss.float3Decl("RGBm") << " = " << "outColor.rgb - luma;"; + ss.newLine() << "float distRGB = dot( abs(RGBm), ones );"; + if (min0 > 0.) + { + ss.newLine() << "float sumRGB = dot( outColor.rgb, ones );"; + ss.newLine() << "float sat_hi = distRGB / (0.15 + sumRGB);"; + ss.newLine() << "float sat_lo = distRGB * 5.;"; + ss.newLine() << "float alpha = clamp( (luma - 0.001) / (0.01 - 0.001), 0., 1.);"; + + ss.newLine() << "float sat = sat_lo + alpha * (sat_hi - sat_lo);"; + ss.newLine() << "sat *= 1.4;"; + } + else if (min0 < 0.) + { + ss.newLine() << "float sat = distRGB * 4.;"; + } + else + { + ss.newLine() << "float sat = distRGB * 1.25;"; + } + ss.newLine() << "float hue = 0.0;"; + ss.newLine() << "if (minRGB != maxRGB) {"; + ss.newLine() << " float OneOverMaxMinusMin = 1.0 / (maxRGB - minRGB);"; + ss.newLine() << " if ( maxRGB == outColor.r ) hue = 1.0 + (outColor.g - outColor.b) * OneOverMaxMinusMin;"; + ss.newLine() << " else if ( maxRGB == outColor.g ) hue = 3.0 + (outColor.b - outColor.r) * OneOverMaxMinusMin;"; + ss.newLine() << " else hue = 5.0 + (outColor.r - outColor.g) * OneOverMaxMinusMin;"; + ss.newLine() << "}"; + ss.newLine() << "outColor.r = hue * 1./6.; outColor.g = sat; outColor.b = luma;"; +} + +void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, float min0) +{ + //ss.setIndent(2); + + ss.newLine() << "float luma = outColor.z;"; + ss.newLine() << "float Hue = outColor.x - 1./6.;"; + ss.newLine() << "Hue = (luma < 0.) ? Hue + 0.5 : Hue;"; + ss.newLine() << "Hue = ( Hue - floor( Hue ) ) * 6.0;"; + ss.newLine() << "float R = abs(Hue - 3.0) - 1.0;"; + ss.newLine() << "float G = 2.0 - abs(Hue - 2.0);"; + ss.newLine() << "float B = 2.0 - abs(Hue - 4.0);"; + ss.newLine() << ss.float3Decl("RGB0") << " = " << ss.float3Const("R", "G", "B") << ";"; + ss.newLine() << "RGB0 = clamp( RGB0, 0., 1. );"; + + ss.newLine() << ss.float3Decl("lumaWeights") << " = " << ss.float3Const(0.2126f, 0.7152f, 0.0722f ) << ";"; + ss.newLine() << ss.float3Decl("ones") << " = " << ss.float3Const(1.f, 1.f, 1.f ) << ";"; + ss.newLine() << "float currY = dot(RGB0, lumaWeights);"; + ss.newLine() << "RGB0 *= luma / currY;"; + + ss.newLine() << "float sat = outColor.y;"; + ss.newLine() << "float distRGB = dot( abs(RGB0 - luma), ones );"; + if (min0 > 0.) + { + ss.newLine() << "float sumRGB = dot( RGB0, ones );"; + ss.newLine() << "float k = 0.15;"; + ss.newLine() << "float lo_gain = 5.;"; + ss.newLine() << "sat /= 1.4;"; + ss.newLine() << "float tmp = -sat * sumRGB + sat * 3. * luma + distRGB;"; + ss.newLine() << "float s1 = (tmp == 0.) ? 0. : sat * (k + 3. * luma) / tmp;"; + ss.newLine() << "float s0 = sat / max(1e-10, distRGB * lo_gain);"; + ss.newLine() << "float alpha = clamp( (luma - 0.001) / (0.01 - 0.001), 0., 1.);"; + ss.newLine() << "float a = distRGB * lo_gain * (1. - alpha) * (sumRGB - 3. * luma);"; + ss.newLine() << "float b = distRGB * lo_gain * (1. - alpha) * (k + 3. * luma) + distRGB * alpha - sat * (sumRGB - 3. * luma);"; + ss.newLine() << "float c = -sat * (k + 3. * luma);"; + ss.newLine() << "float discrim = sqrt( b * b - 4. * a * c );"; + ss.newLine() << "float denom = -discrim - b;"; + ss.newLine() << "float sm = (2. * c) / denom;"; + ss.newLine() << "sm = (sm >= 0.) ? sm : (2. * c) / (denom + discrim * 2.);"; + ss.newLine() << "float gainS = (alpha == 1.) ? s1 : (alpha == 0.) ? s0 : sm;"; + } + else if (min0 < 0.) + { + ss.newLine() << "float gainS = sat / max(1e-10, distRGB * 4.);"; + } + else + { + ss.newLine() << "float gainS = sat / max(1e-10, distRGB * 1.25);"; + } + ss.newLine() << "outColor.rgb = luma + gainS * (RGB0 - luma);"; +} + +void Add_RGB_TO_HSY_LOG(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) +{ + Add_RGB_TO_HSY(shaderCreator, ss, -0.1f); +} + +void Add_RGB_TO_HSY_LIN(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) +{ + Add_RGB_TO_HSY(shaderCreator, ss, 0.2f); +} + +void Add_RGB_TO_HSY_VID(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) +{ + Add_RGB_TO_HSY(shaderCreator, ss, 0.0f); +} + +void Add_HSY_LOG_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) +{ + Add_HSY_TO_RGB(shaderCreator, ss, -0.1f); +} + +void Add_HSY_LIN_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) +{ + Add_HSY_TO_RGB(shaderCreator, ss, 0.2f); +} + +void Add_HSY_VID_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) +{ + Add_HSY_TO_RGB(shaderCreator, ss, 0.0f); +} + void Add_HSV_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) { const std::string pxl(shaderCreator->getPixelName()); @@ -2089,6 +2207,14 @@ void GetFixedFunctionGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, ConstFixedFunctionOpDataRcPtr & func) { GpuShaderText ss(shaderCreator->getLanguage()); + GetFixedFunctionGPUProcessingText(shaderCreator, ss, func); + shaderCreator->addToFunctionShaderCode(ss.string().c_str()); +} + +void GetFixedFunctionGPUProcessingText(GpuShaderCreatorRcPtr & shaderCreator, + GpuShaderText & ss, + ConstFixedFunctionOpDataRcPtr & func) +{ ss.indent(); ss.newLine() << ""; @@ -2241,6 +2367,36 @@ void GetFixedFunctionGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, Add_RGB_TO_HSV(shaderCreator, ss); break; } + case FixedFunctionOpData::RGB_TO_HSY_LOG: + { + Add_RGB_TO_HSY_LOG(shaderCreator, ss); + break; + } + case FixedFunctionOpData::RGB_TO_HSY_LIN: + { + Add_RGB_TO_HSY_LIN(shaderCreator, ss); + break; + } + case FixedFunctionOpData::RGB_TO_HSY_VID: + { + Add_RGB_TO_HSY_VID(shaderCreator, ss); + break; + } + case FixedFunctionOpData::HSY_LOG_TO_RGB: + { + Add_HSY_LOG_TO_RGB(shaderCreator, ss); + break; + } + case FixedFunctionOpData::HSY_LIN_TO_RGB: + { + Add_HSY_LIN_TO_RGB(shaderCreator, ss); + break; + } + case FixedFunctionOpData::HSY_VID_TO_RGB: + { + Add_HSY_VID_TO_RGB(shaderCreator, ss); + break; + } case FixedFunctionOpData::HSV_TO_RGB: { Add_HSV_TO_RGB(shaderCreator, ss); @@ -2313,7 +2469,6 @@ void GetFixedFunctionGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, ss.newLine() << "}"; ss.dedent(); - shaderCreator->addToFunctionShaderCode(ss.string().c_str()); } } // OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.h b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.h index 802c99af54..71708fa03c 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.h +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.h @@ -15,6 +15,10 @@ namespace OCIO_NAMESPACE void GetFixedFunctionGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, ConstFixedFunctionOpDataRcPtr & func); +void GetFixedFunctionGPUProcessingText(GpuShaderCreatorRcPtr & shaderCreator, + GpuShaderText & shaderText, + ConstFixedFunctionOpDataRcPtr & func); + } // namespace OCIO_NAMESPACE #endif diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index 72c656725a..f04ab82498 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -6,6 +6,7 @@ #include #include +#include "OpenColorIO/DynamicProperty.h" #include "GpuShaderUtils.h" #include "ops/gradingrgbcurve/GradingBSplineCurve.h" @@ -13,16 +14,16 @@ namespace OCIO_NAMESPACE { -GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size) +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size, BSplineCurveType curveType) { - auto newSpline = std::make_shared(size); + auto newSpline = std::make_shared(size, curveType); GradingBSplineCurveRcPtr res = newSpline; return res; } -GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values) +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values, BSplineCurveType curveType) { - auto newSpline = std::make_shared(values.size()); + auto newSpline = std::make_shared(values.size(), curveType); size_t i = 0; for (const auto & c : values) { @@ -33,13 +34,13 @@ GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list & controlPoints) - : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f) +GradingBSplineCurveImpl::GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineCurveType curveType) + : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_curveType(curveType) { } @@ -48,6 +49,7 @@ GradingBSplineCurveRcPtr GradingBSplineCurveImpl::createEditableCopy() const auto copy = std::make_shared(0); copy->m_controlPoints = m_controlPoints; copy->m_slopesArray = m_slopesArray; + copy->m_curveType = m_curveType; GradingBSplineCurveRcPtr res; res = copy; return res; @@ -169,7 +171,7 @@ bool IsGradingCurveIdentity(const ConstGradingBSplineCurveRcPtr & curve) namespace { -void EstimateSlopes(const std::vector & ctrlPnts, std::vector & slopes) +void EstimateSlopesBSpline(const std::vector & ctrlPnts, std::vector & slopes) { std::vector secantSlope; std::vector secantLen; @@ -321,7 +323,25 @@ bool AdjustSlopes(const std::vector & ctrlPnts, } // namespace -void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int curveIdx) const + +//------------------------------------------------------------------------------------------------ +// +BSplineCurveType GradingBSplineCurveImpl::getCurveType() const +{ + return m_curveType; +} + +//------------------------------------------------------------------------------------------------ +// +void GradingBSplineCurveImpl::setCurveType(BSplineCurveType curveType) +{ + m_curveType = curveType; +} + + +//------------------------------------------------------------------------------------------------ +// +void GradingBSplineCurveImpl::computeKnotsAndCoefsBSpline(KnotsCoefs & knotsCoefs, int curveIdx) const { // Skip invalid data and identity. if (m_controlPoints.size() < 2 || isIdentity()) @@ -348,7 +368,7 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int else { // Otherwise, estimate slopes based on the control points. - EstimateSlopes(m_controlPoints, slopes); + EstimateSlopesBSpline(m_controlPoints, slopes); } FitSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); @@ -362,9 +382,9 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int FitSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); } - const int numKnots = static_cast(knotsCoefs.m_knotsArray.size()); + const int numKnots = static_cast(knotsCoefs.m_nKnots); const int newKnots = static_cast(knots.size()); - const int numCoefs = static_cast(knotsCoefs.m_coefsArray.size()); + const int numCoefs = static_cast(knotsCoefs.m_nCoefs); const int newCoefs = static_cast(coefsA.size() * 3); if (numKnots + newKnots > KnotsCoefs::MAX_NUM_KNOTS || @@ -378,13 +398,412 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = numCoefs; knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = newCoefs; - knotsCoefs.m_knotsArray.insert(knotsCoefs.m_knotsArray.end(), knots.begin(), knots.end()); - knotsCoefs.m_coefsArray.insert(knotsCoefs.m_coefsArray.end(), coefsA.begin(), coefsA.end()); - knotsCoefs.m_coefsArray.insert(knotsCoefs.m_coefsArray.end(), coefsB.begin(), coefsB.end()); - knotsCoefs.m_coefsArray.insert(knotsCoefs.m_coefsArray.end(), coefsC.begin(), coefsC.end()); + const unsigned coefsSize = (unsigned) coefsA.size(); + std::copy(knots.begin(), knots.end(), knotsCoefs.m_knotsArray.begin() + numKnots); + std::copy(coefsA.begin(), coefsA.end(), knotsCoefs.m_coefsArray.begin() + numCoefs); + std::copy(coefsB.begin(), coefsB.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize); + std::copy(coefsC.begin(), coefsC.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize * 2); + + knotsCoefs.m_nKnots += newKnots; + knotsCoefs.m_nCoefs += newCoefs; } } +//------------------------------------------------------------------------------------------------ +// +void prepHueCurveData(const std::vector& ctrlPnts, + std::vector& outCtrlPnts, + bool isPeriodic, + bool isHorizontal) + { + size_t numCtrlPnts = ctrlPnts.size(); + for (unsigned i = 0; i < numCtrlPnts; ++i) + { + const float xval = ctrlPnts[ i ].m_x; + const float yval = ctrlPnts[ i ].m_y; + // Wrap periodic x values into [0,1). + if (isPeriodic && (xval < 0.f)) + { + outCtrlPnts.push_back(GradingControlPoint(xval + 1.f, isHorizontal ? yval : yval + 1.f)); + } + else if (isPeriodic && (xval >= 1.f)) + { + outCtrlPnts.push_back(GradingControlPoint(xval - 1.f, isHorizontal ? yval : yval - 1.f)); + } + else + { + outCtrlPnts.push_back(GradingControlPoint(xval, yval)); + } + } + + // Sort x and y based on x order. + for (unsigned i = 0; i < numCtrlPnts; ++i) + { + unsigned min_index = i; + float min_val = outCtrlPnts[i].m_x; + for (unsigned j = i + 1; j < numCtrlPnts; ++j) + { + if (outCtrlPnts[j].m_x < min_val) + { + min_val = outCtrlPnts[j].m_x; + min_index = j; + } + } + + std::swap( outCtrlPnts[i], outCtrlPnts[min_index] ); + } + + // Ensure that there is a minimum space between the x values. + const float tol = 2e-3f; + const float x_span = outCtrlPnts[numCtrlPnts - 1].m_x - outCtrlPnts[0].m_x; + for (unsigned i = 1; i < outCtrlPnts.size(); ++i) + { + if ( (outCtrlPnts[i].m_x - outCtrlPnts[i - 1].m_x) < x_span * tol ) + { + outCtrlPnts[i].m_x = outCtrlPnts[i - 1].m_x + x_span * tol; + } + } + if (!isHorizontal) + { + const float y_span = outCtrlPnts[numCtrlPnts - 1].m_y - outCtrlPnts[0].m_y; + for (unsigned i = 1; i < outCtrlPnts.size(); ++i) + { + if ( (outCtrlPnts[i].m_y - outCtrlPnts[i - 1].m_y) < y_span * tol ) + { + outCtrlPnts[i].m_y = outCtrlPnts[i - 1].m_y + y_span * tol; + } + } + } + + if (isPeriodic) + { + // Copy a value from each side and wrap it around to the other side. + GradingControlPoint firstCtrlPnt = outCtrlPnts[numCtrlPnts - 1]; + firstCtrlPnt.m_x -= 1.f; + firstCtrlPnt.m_y = isHorizontal ? firstCtrlPnt.m_y : firstCtrlPnt.m_y - 1.f; + outCtrlPnts.insert(outCtrlPnts.begin(), firstCtrlPnt); + + GradingControlPoint lastCtrlPnt = outCtrlPnts[1]; + lastCtrlPnt.m_x += 1.f; + lastCtrlPnt.m_y = isHorizontal ? lastCtrlPnt.m_y : lastCtrlPnt.m_y + 1.f; + outCtrlPnts.push_back(lastCtrlPnt); + } + } + + //------------------------------------------------------------------------------------------------ + // + float calcKsi(unsigned i, + const std::vector& outCtrlPnts, + const std::vector& slopes) + { + const GradingControlPoint& p0 = outCtrlPnts[i]; + const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + + const float k = 0.2f; + + const float dx = p1.m_x - p0.m_x; + const float secantSlope = (p1.m_y - p0.m_y) / dx; + + float secant = secantSlope; + float m0 = slopes[i]; + float m1 = slopes[i + 1]; + if (secant < 0.f) + { + m0 = -slopes[i]; m1 = -slopes[i + 1]; + secant = -secant; + } + const float x_mid = p0.m_x + 0.5f * dx; + + const float left_bnd = p0.m_x + dx * k; + const float right_bnd = p1.m_x - dx * k; + float top_bnd = left_bnd; + float bottom_bnd = right_bnd; + float m_min = m0; + float m_max = m1; + if (m0 > m1) + { + m_max = m0; m_min = m1; + top_bnd = right_bnd; bottom_bnd = left_bnd; + } + const float dm = m_max - m_min; + const float b = 1.f - 0.5f * k; + const float b_high = m_min + b * dm; + const float b_low = m_min + (1.f - b) * dm; + const float bbb = m_max * 4.f; + const float bb = m_max * 1.1f; + + const float m_rel_diff = dm / std::max(0.01f, m_max); + const float alpha = std::max( 0.f, std::min( (m_rel_diff - 0.05f) / (0.75f - 0.05f), 1.f ) ); + top_bnd = x_mid + alpha * (top_bnd - x_mid); + bottom_bnd = x_mid + alpha * (bottom_bnd - x_mid); + + // Calculate the middle knot. + float ksi = 0.f; + + if (secant >= bbb) + { + ksi = x_mid; + } + else if (secant > bb) + { + const float blend = (secant - bb) / (bbb - bb); + ksi = top_bnd + blend * (x_mid - top_bnd); + } + else if (secant >= b_high) + { + ksi = top_bnd; + } + else if ((secant > b_low) && (b_high != b_low)) + { + const float blend = (secant - b_low) / (b_high - b_low); + ksi = bottom_bnd + blend * (top_bnd - bottom_bnd); + } + else + { + ksi = bottom_bnd; + } + + return ksi; + } + + //------------------------------------------------------------------------------------------------ +// +void fitHueSpline(const std::vector& outCtrlPnts, + const std::vector& slopes, + std::vector& knots, + std::vector& coefsA, + std::vector& coefsB, + std::vector& coefsC) +{ + knots.push_back( outCtrlPnts[0].m_x ); + unsigned numCtrlPnts = outCtrlPnts.size(); + for (unsigned i = 0; i < numCtrlPnts - 1; ++i) + { + const GradingControlPoint& p0 = outCtrlPnts[i]; + const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + + const float dx = p1.m_x - p0.m_x; + const float secantSlope = (p1.m_y - p0.m_y) / dx; + + if ( fabsf( (slopes[i] + slopes[i + 1]) - 2.f * secantSlope ) <= 1e-5f ) + { + coefsC.push_back( p0.m_y ); + coefsB.push_back( slopes[i] ); + coefsA.push_back( 0.5f * (slopes[i + 1] - slopes[i]) / dx ); + } + else + { + // Calculate the middle knot. + const float ksi = calcKsi(i, outCtrlPnts, slopes); + + // Calculate the coefficients. + const float m_bar = (2.f * secantSlope - slopes[i + 1]) + + (slopes[i + 1] - slopes[i]) * (ksi - p0.m_x) / (p1.m_x - p0.m_x); + const float eta = (m_bar - slopes[i]) / (ksi - p0.m_x); + coefsC.push_back( p0.m_y ); + coefsB.push_back( slopes[i] ); + coefsA.push_back( 0.5f * eta ); + coefsC.push_back( p0.m_y + slopes[i] * (ksi - p0.m_x) + 0.5f * eta * (ksi - p0.m_x) * (ksi - p0.m_x) ); + coefsB.push_back( m_bar ); + coefsA.push_back( 0.5f * (slopes[i + 1] - m_bar) / (p1.m_x - ksi) ); + knots.push_back( ksi ); + } + + knots.push_back( p1.m_x ); + } +} + +//------------------------------------------------------------------------------------------------ +// +void estimateHueSlopes(std::vector& outCtrlPnts, + std::vector& slopes, + bool isPeriodic, + bool isHorizontal) +{ + slopes.clear(); + unsigned numCtrlPnts = outCtrlPnts.size(); + std::vector secantSlope; + std::vector secantLen; + for (unsigned i = 0; i < numCtrlPnts - 1; ++i) + { + const GradingControlPoint& p0 = outCtrlPnts[i]; + const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + + const float del_x = p1.m_x - p0.m_x; // prepHueCurveData ensures this is > 0 + const float del_y = p1.m_y - p0.m_y; + secantSlope.push_back( del_y / del_x ); + secantLen.push_back( sqrt( del_x * del_x + del_y * del_y ) ); + } + + if (numCtrlPnts == 2) + { + slopes.push_back( secantSlope[0] ); + slopes.push_back( secantSlope[0] ); + return; + } + + slopes.push_back(0.f); + + if (isHorizontal) // All horizontal curves and diagonal hue-hue. + { + for (unsigned i = 1; i < numCtrlPnts - 1; ++i) + { + float s = 0.f; + float denom = secantSlope[i] + secantSlope[i - 1]; + if (fabsf(denom) < 1e-3f) + { + const float minval = denom < 0.f ? -1e-3f : 1e-3f; + s = 2.f * secantSlope[i] * secantSlope[i - 1] / minval; + } + else + { + s = 2.f * secantSlope[i] * secantSlope[i - 1] / denom; + } + // Set slope to zero at flat areas or extrema. + if ( secantSlope[i] * secantSlope[i - 1] <= 0.f ) + { + s = 0.f; + } + slopes.push_back( s ); + } + slopes.push_back( 0.5f * ( 3.f * secantSlope[numCtrlPnts - 2] - slopes[numCtrlPnts - 2] ) ); + slopes[0] = 0.5f * ( 3.f * secantSlope[0] - slopes[1] ); + } + else // Diagonal curves except hue-hue (LvL and SvS). + { + unsigned i = 0; + while (true) + { + unsigned j = i; + float DL = secantLen[i]; + while ( ( j < numCtrlPnts - 2 ) && ( fabsf( secantSlope[j + 1] - secantSlope[j] ) < 1e-6f ) ) + { + DL += secantLen[ j + 1 ]; + j++; + } + for (unsigned k = i; k <= j; ++k) + secantLen[k] = DL; + if (j >= numCtrlPnts - 3) + break; + i = j + 1; + } + + for (unsigned k = 1; k < numCtrlPnts - 1; ++k) + { + const float s = ( secantLen[k] * secantSlope[k] + secantLen[k - 1] * secantSlope[k - 1] ) / + ( secantLen[k] + secantLen[k - 1] ); + slopes.push_back( s ); + } + + const float minSlope = 0.01f; + slopes.push_back( std::max(minSlope, 0.5f * ( 3.f * secantSlope[numCtrlPnts - 2] - slopes[numCtrlPnts - 2] )) ); + slopes[0] = std::max(minSlope, 0.5f * ( 3.f * secantSlope[0] - slopes[1] )); + } + + // Adjust slopes that are not shape-preserving. + for (unsigned i = 0; i < numCtrlPnts - 1; ++i) + { + float k = 0.2f; + if (fabsf(slopes[i]) > fabsf(slopes[i+1])) + k = 1.f - k; + const float m_near_min = slopes[i] + k * (slopes[i + 1] - slopes[i]); + float scale = 1.f; + if (m_near_min != 0.f) + scale = 0.75f * 2.f * secantSlope[i] / m_near_min; + if (scale < 1.f) + { + slopes[i] = scale * slopes[i]; + slopes[i + 1] = scale * slopes[i + 1]; + } + } + + // Copy end slopes from the opposite side. + if (isPeriodic) + { + slopes[0] = slopes[numCtrlPnts - 2]; + slopes[numCtrlPnts - 1] = slopes[1]; + } +} + +//------------------------------------------------------------------------------------------------ +// +void GradingBSplineCurveImpl::computeKnotsAndCoefsHueCurves(KnotsCoefs & knotsCoefs, int curveIdx) const +{ + // Return 0 knots and coefficients when the curve is identity. + // TODO: isIdentity() is need to be reworked for the differents b spline types. + // Should impact only performance? + //if (isIdentity()) return; + + bool isPeriodic = false; + bool isHorizontal = true; + if (m_curveType == BSplineCurveType::PERIODIC_B_SPLINE || + m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE) + { + isPeriodic = true; + } + if (m_curveType == BSplineCurveType::DIAGONAL_B_SPLINE || + m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE) + { + isHorizontal = false; + } + + std::vector resultCtrlPnts; + prepHueCurveData(m_controlPoints, resultCtrlPnts, isPeriodic, isHorizontal); + + std::vector slopes; + + // For the purposes of slope estimation, consider the hue-hue spline to be horizontal. + if (m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE) + { + isHorizontal = true; + } + estimateHueSlopes(resultCtrlPnts, slopes, isPeriodic, isHorizontal); + + std::vector knots; + std::vector coefsA; + std::vector coefsB; + std::vector coefsC; + fitHueSpline(resultCtrlPnts, slopes, knots, coefsA, coefsB, coefsC); + + const int numKnots = static_cast(knotsCoefs.m_nKnots); + const int newKnots = static_cast(knots.size()); + const int numCoefs = static_cast(knotsCoefs.m_nCoefs); + const int newCoefs = static_cast(coefsA.size() * 3); + + if (numKnots + newKnots > KnotsCoefs::MAX_NUM_KNOTS || + numCoefs + newCoefs > KnotsCoefs::MAX_NUM_COEFS) + { + throw Exception("Hue curve: maximum number of control points reached."); + } + + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = numKnots; + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = newKnots; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = numCoefs; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = newCoefs; + + const unsigned coefsSize = (unsigned) coefsA.size(); + std::copy(knots.begin(), knots.end(), knotsCoefs.m_knotsArray.begin() + numKnots); + std::copy(coefsA.begin(), coefsA.end(), knotsCoefs.m_coefsArray.begin() + numCoefs); + std::copy(coefsB.begin(), coefsB.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize); + std::copy(coefsC.begin(), coefsC.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize * 2); + + knotsCoefs.m_nKnots += newKnots; + knotsCoefs.m_nCoefs += newCoefs; +} + + +void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int curveIdx) const +{ + if(m_curveType == BSplineCurveType::B_SPLINE) + { + computeKnotsAndCoefsBSpline(knotsCoefs, curveIdx); + } + else + { + computeKnotsAndCoefsHueCurves(knotsCoefs, curveIdx); + } +} + void GradingBSplineCurveImpl::AddShaderEval(GpuShaderText & st, const std::string & knotsOffsets, const std::string & coefsOffsets, @@ -508,6 +927,77 @@ void GradingBSplineCurveImpl::AddShaderEval(GpuShaderText & st, } } +void GradingBSplineCurveImpl::AddShaderEvalHueCurve(GpuShaderText & st, + const std::string & knotsOffsets, + const std::string & coefsOffsets, + const std::string & knots, + const std::string & coefs, bool isInv) +{ + st.indent(); + st.newLine() << "int knotsOffs = " << knotsOffsets << "[curveIdx * 2];"; + st.newLine() << "int knotsCnt = " << knotsOffsets << "[curveIdx * 2 + 1];"; + st.newLine() << "int coefsOffs = " << coefsOffsets << "[curveIdx * 2];"; + st.newLine() << "int coefsCnt = " << coefsOffsets << "[curveIdx * 2 + 1];"; + st.newLine() << "int coefsSets = coefsCnt / 3;"; + + // If the curve has the default/identity values the coef data is empty, so return the input. + st.newLine() << "if (coefsSets == 0)"; + st.newLine() << "{"; + st.newLine() << " return identity_x;"; + st.newLine() << "}"; + + st.newLine() << "float knStart = " << knots << "[knotsOffs];"; + st.newLine() << "float knEnd = " << knots << "[knotsOffs + knotsCnt - 1];"; + + st.newLine() << "float y;"; + + st.newLine() << "if (x <= knStart)"; + st.newLine() << "{"; + st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets];"; + st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 2];"; + st.newLine() << " y = (x - knStart) * B + C;"; + st.newLine() << "}"; + + st.newLine() << "else if (x >= knEnd)"; + st.newLine() << "{"; + st.newLine() << " float A = " << coefs << "[coefsOffs + coefsSets - 1];"; + st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets * 2 - 1];"; + st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 3 - 1];"; + st.newLine() << " float kn = " << knots << "[knotsOffs + knotsCnt - 2];"; + st.newLine() << " float t = knEnd - kn;"; + st.newLine() << " float slope = 2. * A * t + B;"; + st.newLine() << " float offs = ( A * t + B ) * t + C;"; + st.newLine() << " y = (x - knEnd) * slope + offs;"; + st.newLine() << "}"; + + st.newLine() << "else"; + st.newLine() << "{"; + st.newLine() << " int i = 0;"; + st.newLine() << " while ( x < " << knots << "[knotsOffs + i] || x > " << knots << "[knotsOffs + i + 1] )"; + st.newLine() << " {"; + st.newLine() << " i++;"; + st.newLine() << " }"; + st.newLine() << " float A = " << coefs << "[coefsOffs + i];"; + st.newLine() << " float B = " << coefs << "[coefsOffs + i + coefsSets];"; + st.newLine() << " float C = " << coefs << "[coefsOffs + i + coefsSets * 2];"; + st.newLine() << " float kn = " << knots << "[knotsOffs + i];"; + st.newLine() << " float t = x - kn;"; + st.newLine() << " y = ( A * t + B ) * t + C;"; + st.newLine() << "}"; + + st.newLine() << "return y;"; + st.dedent(); +} + +GradingBSplineCurveImpl::KnotsCoefs::KnotsCoefs(size_t numCurves) +{ + m_knotsOffsetsArray.resize(2 * numCurves); + m_coefsOffsetsArray.resize(2 * numCurves); + + m_coefsArray.resize(DynamicPropertyGradingRGBCurveImpl::GetMaxCoefs()); + m_knotsArray.resize(DynamicPropertyGradingRGBCurveImpl::GetMaxKnots()); +} + float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x) const { const int coefsSets = m_coefsOffsetsArray[2 * c + 1] / 3; diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h index 9944564051..4de6d12e25 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h @@ -17,8 +17,8 @@ class GpuShaderText; class GradingBSplineCurveImpl : public GradingBSplineCurve { public: - explicit GradingBSplineCurveImpl(size_t size); - GradingBSplineCurveImpl(const std::vector & controlPoints); + explicit GradingBSplineCurveImpl(size_t size, BSplineCurveType curveType = B_SPLINE); + GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineCurveType curveType = B_SPLINE); ~GradingBSplineCurveImpl() = default; GradingBSplineCurveRcPtr createEditableCopy() const override; @@ -31,6 +31,9 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve bool slopesAreDefault() const override; void validate() const override; + BSplineCurveType getCurveType() const override; + void setCurveType(BSplineCurveType curveType) override; + bool isIdentity() const; // The KnotsCoefs struct is used when evaluating the curves. Unlike the GradingBSplineCurve @@ -57,11 +60,7 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve { KnotsCoefs() = delete; - explicit KnotsCoefs(size_t numCurves) - { - m_knotsOffsetsArray.resize(2 * numCurves); - m_coefsOffsetsArray.resize(2 * numCurves); - } + explicit KnotsCoefs(size_t numCurves); // Pre-processing scalar values. @@ -106,6 +105,9 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve std::vector m_coefsArray; // Contains packed coefs of ALL curves. std::vector m_knotsArray; // Contains packed knots of ALL curves. + int m_nCoefs = 0; + int m_nKnots = 0; + float evalCurve(int curveIdx, float x) const; float evalCurveRev(int curveIdx, float x) const; }; @@ -117,11 +119,24 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve static void AddShaderEval(GpuShaderText & st, const std::string & knotsOffsets, const std::string & coefsOffsets, const std::string & knots, const std::string & coefs, bool isInv); + + static void AddShaderEvalHueCurve(GpuShaderText & st, + const std::string & knotsOffsets, const std::string & coefsOffsets, + const std::string & knots, const std::string & coefs, bool isInv); private: + void computeKnotsAndCoefsBSpline(KnotsCoefs & knotsCoefs, int curveIdx) const; + void computeKnotsAndCoefsHueCurves(KnotsCoefs & knotsCoefs, int curveIdx) const; + void validateIndex(size_t index) const; + void prepData(const std::vector& inCtrlPnts, + std::vector& outCtrlPnt, + bool isPeriodic, + bool isHorizontal) const; std::vector m_controlPoints; std::vector m_slopesArray; // Optional slope values for the control points. + + BSplineCurveType m_curveType = BSplineCurveType::B_SPLINE; }; bool IsGradingCurveIdentity(const ConstGradingBSplineCurveRcPtr & curve); diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOp.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOp.cpp index 598ae28e67..6722c02aa1 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOp.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOp.cpp @@ -248,6 +248,5 @@ void BuildGradingRGBCurveOp(OpRcPtrVec & ops, CreateGradingRGBCurveOp(ops, curveData, dir); } - } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/HueCurve.cpp new file mode 100644 index 0000000000..cd945990fc --- /dev/null +++ b/src/OpenColorIO/ops/gradingrgbcurve/HueCurve.cpp @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#include +#include + +#include + +#include "ops/gradingrgbcurve/HueCurve.h" + +namespace OCIO_NAMESPACE +{ +namespace +{ +static const std::vector DefaultHueHueCtrl{ {0.0f, 0.0f}, + {1.f/6.f, 1.f/6.f}, + {2.f/6.f, 2.f/6.f}, + {0.5f, 0.5f}, + {4.f/6.f, 4.f/6.f}, + {5.f/6.f, 5.f/6.f} }; + +static const std::vector DefaultHueSatCtrl{ {0.0f, 1.0f}, + {1.f/6.f, 1.0f}, + {2.f/6.f, 1.0f}, + {0.5f, 1.0f}, + {4.f/6.f, 1.0f}, + {5.f/6.f, 1.0f} }; + +static const std::vector DefaultHueFxCtrl{ {0.0f, 0.0f}, + {1.f/6.f, 0.f}, + {2.f/6.f, 0.f}, + {0.5f, 0.f}, + {4.f/6.f, 0.f}, + {5.f/6.f, 0.f} }; + +static const std::vector DefaultLumSatCtrl{ {0.0f, 1.0f}, {0.5f, 1.0f}, {1.0f, 1.0f} }; +static const std::vector DefaultLumSatLinCtrl{ {-7.0f, 1.0f}, {0.f, 1.0f}, {7.0f, 1.0f} }; + +static const std::vector DefaultSatSatCtrl{ {0.0f, 0.0f}, {0.5f, 0.5f}, {1.0f, 1.0f} }; +static const std::vector DefaultSatLumCtrl{ {0.0f, 1.0f}, {0.5f, 1.0f}, {1.0f, 1.0f} }; + +static const std::vector DefaultLumLumCtrl{ { 0.f, 0.f },{ 0.5f, 0.5f },{ 1.0f, 1.0f } }; +static const std::vector DefaultLumLumLinCtrl{ { -7.0f, -7.0f },{ 0.f, 0.f },{ 7.0f, 7.0f } }; + +} + +const GradingBSplineCurveImpl HueCurveImpl::DefaultHueHue(DefaultHueHueCtrl, BSplineCurveType::HUE_HUE_B_SPLINE ); +const GradingBSplineCurveImpl HueCurveImpl::DefaultHueSat(DefaultHueSatCtrl, BSplineCurveType::PERIODIC_B_SPLINE ); +const GradingBSplineCurveImpl HueCurveImpl::DefaultHueFx(DefaultHueFxCtrl, BSplineCurveType::PERIODIC_B_SPLINE ); +const GradingBSplineCurveImpl HueCurveImpl::DefaultLumSat(DefaultLumSatCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); +const GradingBSplineCurveImpl HueCurveImpl::DefaultLumSatLin(DefaultLumSatLinCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); +const GradingBSplineCurveImpl HueCurveImpl::DefaultSatSat(DefaultSatSatCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); +const GradingBSplineCurveImpl HueCurveImpl::DefaultSatLum(DefaultSatLumCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); +const GradingBSplineCurveImpl HueCurveImpl::DefaultLumLum(DefaultLumLumCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); +const GradingBSplineCurveImpl HueCurveImpl::DefaultLumLumLin(DefaultLumLumLinCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); + +const std::array, static_cast(HUE_NUM_CURVES)> + HueCurveImpl::DefaultCurvesLin( { std::ref(DefaultHueHue), + std::ref(DefaultHueSat), + std::ref(DefaultHueSat), + std::ref(DefaultLumSatLin), + std::ref(DefaultSatSat), + std::ref(DefaultLumLumLin), + std::ref(DefaultSatLum), + std::ref(DefaultHueFx) }); + + +const std::array, static_cast(HUE_NUM_CURVES)> + HueCurveImpl::DefaultCurves( { std::ref(DefaultHueHue), + std::ref(DefaultHueSat), + std::ref(DefaultHueSat), + std::ref(DefaultLumSat), + std::ref(DefaultSatSat), + std::ref(DefaultLumLum), + std::ref(DefaultSatLum), + std::ref(DefaultHueFx) }); + +HueCurveImpl::HueCurveImpl() : + HueCurveImpl(GRADING_LOG) +{} + +HueCurveImpl::HueCurveImpl(GradingStyle style) +{ + auto & curves = (style == GRADING_LIN) ? HueCurveImpl::DefaultCurvesLin: + HueCurveImpl::DefaultCurves; + + for (int c = 0; c < HUE_NUM_CURVES; ++c) + { + m_curves[c] = curves[c].get().createEditableCopy(); + } +} + +HueCurveImpl::HueCurveImpl(const std::array & curves) +{ + if (curves.size() != static_cast(HUE_NUM_CURVES)) + { + throw Exception("All curves have to be defined"); + } + + for (int c = 0; c < HUE_NUM_CURVES; ++c) + { + if(curves[c]) + { + m_curves[c] = curves[c]->createEditableCopy(); + } + } +} + +HueCurveImpl::HueCurveImpl(const ConstHueCurveRcPtr & rhs) +{ + auto impl = dynamic_cast(rhs.get()); + if (impl) + { + for (int c = 0; c < HUE_NUM_CURVES; ++c) + { + m_curves[c] = impl->m_curves[c]->createEditableCopy(); + } + } +} + +HueCurveRcPtr HueCurveImpl::createEditableCopy() const +{ + auto newCurve = std::make_shared(); + for (int c = 0; c < HUE_NUM_CURVES; ++c) + { + newCurve->m_curves[c] = m_curves[c]->createEditableCopy(); + } + + HueCurveRcPtr res = newCurve; + return res; +} + +namespace +{ +const char * CurveType(int c) +{ + const HueCurveType curve = static_cast(c); + switch (curve) + { + case HUE_HUE: + return "hue_hue"; + case HUE_SAT: + return "hue_sat"; + case HUE_LUM: + return "hue_lum"; + case LUM_SAT: + return "lum_sat"; + case SAT_SAT: + return "sat_sat"; + case LUM_LUM: + return "lum_lum"; + case SAT_LUM: + return "sat_lum"; + case HUE_FX: + return "hue_fx"; + case HUE_NUM_CURVES: + default: + break; + } + return "invalid"; +} +} + +void HueCurveImpl::validate() const +{ + for (int c = 0; c < HUE_NUM_CURVES; ++c) + { + try + { + m_curves[c]->validate(); + } + catch (Exception & e) + { + std::ostringstream oss; + oss << "HueCurve validation failed for curve: " << CurveType(c) << "' curve " + << "with: " << e.what(); + throw Exception(oss.str().c_str()); + } + } +} + +bool HueCurveImpl::isIdentity() const +{ + for (int c = 0; c < HUE_NUM_CURVES; ++c) + { + if (!IsGradingCurveIdentity(m_curves[c])) + { + return false; + } + } + return true; +} + +ConstGradingBSplineCurveRcPtr HueCurveImpl::getCurve(HueCurveType c) const +{ + return m_curves[c]; +} + +GradingBSplineCurveRcPtr HueCurveImpl::getCurve(HueCurveType c) +{ + return m_curves[c]; +} + +HueCurveRcPtr HueCurve::Create(GradingStyle style) +{ + auto newCurve = std::make_shared(style); + HueCurveRcPtr res = newCurve; + return res; +} + +HueCurveRcPtr HueCurve::Create(const ConstHueCurveRcPtr & rhs) +{ + auto newCurve = std::make_shared(rhs); + HueCurveRcPtr res = newCurve; + return res; +} + +HueCurveRcPtr HueCurve::Create(const std::array & curves) +{ + auto newCurve = std::make_shared(curves); + HueCurveRcPtr res = newCurve; + return res; +} + +bool operator==(const HueCurve & lhs, const HueCurve & rhs) +{ + + for (int c = 0; c < HUE_NUM_CURVES; ++c) + { + if (*(lhs.getCurve(static_cast(c))) != *(rhs.getCurve(static_cast(c)))) + { + return false; + } + } + return true; +} + +bool operator!=(const HueCurve & lhs, const HueCurve & rhs) +{ + return !(lhs == rhs); +} + + +} // namespace OCIO_NAMESPACE + diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurve.h b/src/OpenColorIO/ops/gradingrgbcurve/HueCurve.h new file mode 100644 index 0000000000..fed4defb89 --- /dev/null +++ b/src/OpenColorIO/ops/gradingrgbcurve/HueCurve.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#ifndef INCLUDED_OCIO_HUECURVE_H +#define INCLUDED_OCIO_HUECURVE_H + + +#include + +#include "ops/gradingrgbcurve/GradingBSplineCurve.h" + +namespace OCIO_NAMESPACE +{ + +// Class to hold the RGB curve data that is used in the corresponding dynamic property and in +// the CTF reader.. This allows moving some of the code from DynamicProperty to here. The +// dynamic property is then used by the OpData, which is then used by the Op and Transform. +class HueCurveImpl : public HueCurve +{ +public: + HueCurveImpl(); + HueCurveImpl(GradingStyle style); + HueCurveImpl(const std::array & curves ); + HueCurveImpl(const ConstHueCurveRcPtr & rhs); + + HueCurveRcPtr createEditableCopy() const override; + + void validate() const override; + bool isIdentity() const override; + ConstGradingBSplineCurveRcPtr getCurve(HueCurveType) const override; + GradingBSplineCurveRcPtr getCurve(HueCurveType) override; + + static const GradingBSplineCurveImpl DefaultHueHue; + static const GradingBSplineCurveImpl DefaultHueSat; + static const GradingBSplineCurveImpl DefaultHueFx; + static const GradingBSplineCurveImpl DefaultLumSat; + static const GradingBSplineCurveImpl DefaultLumSatLin; + static const GradingBSplineCurveImpl DefaultSatSat; + static const GradingBSplineCurveImpl DefaultSatLum; + static const GradingBSplineCurveImpl DefaultLumLum; + static const GradingBSplineCurveImpl DefaultLumLumLin; + + static const std::array, static_cast(HUE_NUM_CURVES)> DefaultCurvesLin; + static const std::array, static_cast(HUE_NUM_CURVES)> DefaultCurves; + +private: + std::array(HUE_NUM_CURVES)> m_curves; +}; + +typedef OCIO_SHARED_PTR ConstHueCurveImplRcPtr; +typedef OCIO_SHARED_PTR HueCurveImplRcPtr; + +} + +#endif //INCLUDED_OCIO_GRADINGRGBCURVE_H diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.cpp b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.cpp new file mode 100644 index 0000000000..58e4380da4 --- /dev/null +++ b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.cpp @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#include +#include + +#include + +#include "GpuShaderUtils.h" +//#include "ops/gradingrgbcurve/GradingRGBCurveOpCPU.h" +#include "ops/gradingrgbcurve/HueCurveOpGPU.h" +#include "ops/gradingrgbcurve/HueCurveOp.h" +#include "transforms/HueCurveTransform.h" + +namespace OCIO_NAMESPACE +{ + +namespace +{ + +class HueCurveOp; +typedef OCIO_SHARED_PTR HueCurveOpRcPtr; +typedef OCIO_SHARED_PTR ConstHueCurveOpRcPtr; + +class HueCurveOp : public Op +{ +public: + HueCurveOp() = delete; + HueCurveOp(const HueCurveOp &) = delete; + explicit HueCurveOp(HueCurveOpDataRcPtr & data); + + virtual ~HueCurveOp(); + + OpRcPtr clone() const override; + + std::string getInfo() const override; + + bool isIdentity() const override; + bool isSameType(ConstOpRcPtr & op) const override; + bool isInverse(ConstOpRcPtr & op) const override; + bool canCombineWith(ConstOpRcPtr & op) const override; + void combineWith(OpRcPtrVec & ops, ConstOpRcPtr & secondOp) const override; + + std::string getCacheID() const override; + + bool isDynamic() const override; + bool hasDynamicProperty(DynamicPropertyType type) const override; + DynamicPropertyRcPtr getDynamicProperty(DynamicPropertyType type) const override; + void replaceDynamicProperty(DynamicPropertyType type, + DynamicPropertyGradingRGBCurveImplRcPtr & prop) override; + void removeDynamicProperties() override; + + ConstOpCPURcPtr getCPUOp(bool fastLogExpPow) const override; + + void extractGpuShaderInfo(GpuShaderCreatorRcPtr & shaderCreator) const override; + +protected: + ConstHueCurveOpDataRcPtr hueCurveData() const + { + return DynamicPtrCast(data()); + } + HueCurveOpDataRcPtr hueCurveData() + { + return DynamicPtrCast(data()); + } +}; + + +HueCurveOp::HueCurveOp(HueCurveOpDataRcPtr & hueCurveData) + : Op() +{ + data() = hueCurveData; +} + +OpRcPtr HueCurveOp::clone() const +{ + HueCurveOpDataRcPtr p = hueCurveData()->clone(); + return std::make_shared(p); +} + +HueCurveOp::~HueCurveOp() +{ +} + +std::string HueCurveOp::getInfo() const +{ + return ""; +} + +bool HueCurveOp::isIdentity() const +{ + return hueCurveData()->isIdentity(); +} + +bool HueCurveOp::isSameType(ConstOpRcPtr & op) const +{ + ConstHueCurveOpRcPtr typedRcPtr = DynamicPtrCast(op); + return (bool)typedRcPtr; +} + +bool HueCurveOp::isInverse(ConstOpRcPtr & op) const +{ + ConstHueCurveOpRcPtr typedRcPtr = DynamicPtrCast(op); + if (!typedRcPtr) return false; + + ConstHueCurveOpDataRcPtr hueOpData = typedRcPtr->hueCurveData(); + return hueCurveData()->isInverse(hueOpData); +} + +bool HueCurveOp::canCombineWith(ConstOpRcPtr & /*op*/) const +{ + return false; +} + +void HueCurveOp::combineWith(OpRcPtrVec & /*ops*/, ConstOpRcPtr & secondOp) const +{ + if (!canCombineWith(secondOp)) + { + throw Exception("HueCurveOp: canCombineWith must be checked " + "before calling combineWith."); + } +} + +std::string HueCurveOp::getCacheID() const +{ + // Create the cacheID. + std::ostringstream cacheIDStream; + cacheIDStream << "getCacheID(); + cacheIDStream << ">"; + + return cacheIDStream.str(); +} + +bool HueCurveOp::isDynamic() const +{ + return hueCurveData()->isDynamic(); +} + +bool HueCurveOp::hasDynamicProperty(DynamicPropertyType type) const +{ + if (type != DYNAMIC_PROPERTY_HUE_CURVE) + { + return false; + } + + return hueCurveData()->isDynamic(); +} + +DynamicPropertyRcPtr HueCurveOp::getDynamicProperty(DynamicPropertyType type) const +{ + if (type != DYNAMIC_PROPERTY_HUE_CURVE) + { + throw Exception("Dynamic property type not supported by hue curve op."); + } + if (!isDynamic()) + { + throw Exception("Hue curve property is not dynamic."); + } + + return hueCurveData()->getDynamicProperty(); +} + +void HueCurveOp::replaceDynamicProperty(DynamicPropertyType type, + DynamicPropertyGradingRGBCurveImplRcPtr & prop) +{ + if (type != DYNAMIC_PROPERTY_HUE_CURVE) + { + throw Exception("Dynamic property type not supported by hue curve op."); + } + if (!isDynamic()) + { + throw Exception("Hue curve property is not dynamic."); + } + auto propGC = OCIO_DYNAMIC_POINTER_CAST(prop); + if (!propGC) + { + throw Exception("Dynamic property type not supported by hue curve op."); + } + + hueCurveData()->replaceDynamicProperty(propGC); +} + +void HueCurveOp::removeDynamicProperties() +{ + hueCurveData()->removeDynamicProperty(); +} + +ConstOpCPURcPtr HueCurveOp::getCPUOp(bool /*fastLogExpPow*/) const +{ + // TODO: Add CPU renderer before merging back the adsk fork + //ConstGradingRGBCurveOpDataRcPtr data = rgbCurveData(); + //return GetGradingRGBCurveCPURenderer(data); + return ConstOpCPURcPtr(); +} + +void HueCurveOp::extractGpuShaderInfo(GpuShaderCreatorRcPtr & shaderCreator) const +{ + ConstHueCurveOpDataRcPtr data = hueCurveData(); + GetHueCurveGPUShaderProgram(shaderCreator, data); +} + + +} // Anon namespace + + + + +/////////////////////////////////////////////////////////////////////////// + + +void CreateHueCurveOp(OpRcPtrVec & ops, + HueCurveOpDataRcPtr & curveData, + TransformDirection direction) +{ + auto curve = curveData; + if (direction == TRANSFORM_DIR_INVERSE) + { + curve = curve->inverse(); + } + + ops.push_back(std::make_shared(curve)); +} + +/////////////////////////////////////////////////////////////////////////// + +void CreateHueCurveTransform(GroupTransformRcPtr & group, ConstOpRcPtr & op) +{ + auto gc = DynamicPtrCast(op); + if (!gc) + { + throw Exception("CreateHueCurveTransform: op has to be a HueCurveOp."); + } + auto gcData = DynamicPtrCast(op->data()); + auto gcTransform = HueCurveTransform::Create(gcData->getStyle()); + auto & data = dynamic_cast(gcTransform.get())->data(); + data = *gcData; + + group->appendTransform(gcTransform); +} + +void BuildHueCurveOp(OpRcPtrVec & ops, + const Config & /*config*/, + const ConstContextRcPtr & /*context*/, + const HueCurveTransform & transform, + TransformDirection dir) +{ + const auto & data = dynamic_cast(transform).data(); + data.validate(); + + auto curveData = data.clone(); + CreateHueCurveOp(ops, curveData, dir); +} + +} // namespace OCIO_NAMESPACE + diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.h b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.h new file mode 100644 index 0000000000..d06380ac8f --- /dev/null +++ b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#ifndef INCLUDED_OCIO_HUECURVE_OP_H +#define INCLUDED_OCIO_HUECURVE_OP_H + + +#include + +#include + +#include "Op.h" +#include "ops/gradingrgbcurve/HueCurveOpData.h" + + +namespace OCIO_NAMESPACE +{ + +void CreateHueCurveOp(OpRcPtrVec & ops, + HueCurveOpDataRcPtr & gpData, + TransformDirection direction); + +// Create a copy of the rgb curve transform in the op and append it to +// the GroupTransform. +void CreateHueCurveTransform(GroupTransformRcPtr & group, ConstOpRcPtr & op); + +} // namespace OCIO_NAMESPACE + +#endif diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.cpp b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.cpp new file mode 100644 index 0000000000..27b668450d --- /dev/null +++ b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.cpp @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#include + +#include + +#include "ops/gradingrgbcurve/HueCurve.h" +#include "ops/gradingrgbcurve/HueCurveOpData.h" +#include "ops/range/RangeOpData.h" +#include "Platform.h" + +namespace OCIO_NAMESPACE +{ + +namespace DefaultValues +{ +const std::streamsize FLOAT_DECIMALS = 7; +} + +HueCurveOpData::HueCurveOpData(GradingStyle style) + : OpData() + , m_style(style) +{ + ConstHueCurveRcPtr hueCurve = HueCurve::Create(style); + m_value = std::make_shared(hueCurve, false); +} + +HueCurveOpData::HueCurveOpData(const HueCurveOpData & rhs) + : OpData(rhs) + , m_style(rhs.m_style) +{ + ConstHueCurveRcPtr hueCurve = HueCurve::Create(rhs.m_style); + m_value = std::make_shared(hueCurve, false); + + *this = rhs; +} + +HueCurveOpData::HueCurveOpData(GradingStyle style, + const std::array & curves) + : OpData() + , m_style(style) +{ + ConstHueCurveRcPtr hueCurve = HueCurve::Create(curves); + m_value = std::make_shared(hueCurve, false); +} + +HueCurveOpData & HueCurveOpData::operator=(const HueCurveOpData & rhs) +{ + if (this == &rhs) return *this; + + OpData::operator=(rhs); + + m_direction = rhs.m_direction; + m_style = rhs.m_style; + m_bypassLinToLog = rhs.m_bypassLinToLog; + + // Copy dynamic properties. Sharing happens when needed, with CPUOp for instance. + m_value->setValue(rhs.m_value->getValue()); + if (rhs.m_value->isDynamic()) + { + m_value->makeDynamic(); + } + + return *this; +} + +HueCurveOpData::~HueCurveOpData() +{ +} + +HueCurveOpDataRcPtr HueCurveOpData::clone() const +{ + return std::make_shared(*this); +} + +void HueCurveOpData::validate() const +{ + // This should already be valid. + m_value->getValue()->validate(); + return; +} + +bool HueCurveOpData::isNoOp() const +{ + return isIdentity(); +} + +bool HueCurveOpData::isIdentity() const +{ + if (isDynamic()) return false; + + return m_value->getValue()->isIdentity(); +} + +bool HueCurveOpData::isInverse(ConstHueCurveOpDataRcPtr & r) const +{ + if (isDynamic() || r->isDynamic()) + { + return false; + } + + if (m_style == r->m_style && + (m_style != GRADING_LIN || m_bypassLinToLog == r->m_bypassLinToLog) && + m_value->equals(*r->m_value)) + { + if (CombineTransformDirections(getDirection(), r->getDirection()) == TRANSFORM_DIR_INVERSE) + { + return true; + } + } + return false; +} + +HueCurveOpDataRcPtr HueCurveOpData::inverse() const +{ + auto res = clone(); + res->m_direction = GetInverseTransformDirection(m_direction); + return res; +} + +std::string HueCurveOpData::getCacheID() const +{ + AutoMutex lock(m_mutex); + + std::ostringstream cacheIDStream; + if (!getID().empty()) + { + cacheIDStream << getID() << " "; + } + + cacheIDStream.precision(DefaultValues::FLOAT_DECIMALS); + + cacheIDStream << GradingStyleToString(getStyle()) << " "; + cacheIDStream << TransformDirectionToString(getDirection()) << " "; + if (m_bypassLinToLog) + { + cacheIDStream << " bypassLinToLog"; + } + if (!isDynamic()) + { + cacheIDStream << *(m_value->getValue()); + } + return cacheIDStream.str(); +} + +void HueCurveOpData::setStyle(GradingStyle style) noexcept +{ + if (style != m_style) + { + m_style = style; + // Reset value to default when style is changing. + ConstHueCurveRcPtr reset = HueCurve::Create(style); + m_value->setValue(reset); + } +} + +float HueCurveOpData::getSlope(HueCurveType c, size_t index) const +{ + ConstGradingBSplineCurveRcPtr curve = m_value->getValue()->getCurve(c); + return curve->getSlope(index); +} + +void HueCurveOpData::setSlope(HueCurveType c, size_t index, float slope) +{ + HueCurveRcPtr hueCurve( m_value->getValue()->createEditableCopy() ); + GradingBSplineCurveRcPtr curve = hueCurve->getCurve(c); + curve->setSlope(index, slope); + m_value->setValue(hueCurve); +} + +bool HueCurveOpData::slopesAreDefault(HueCurveType c) const +{ + ConstGradingBSplineCurveRcPtr curve = m_value->getValue()->getCurve(c); + return curve->slopesAreDefault(); +} + +TransformDirection HueCurveOpData::getDirection() const noexcept +{ + return m_direction; +} + +void HueCurveOpData::setDirection(TransformDirection dir) noexcept +{ + m_direction = dir; +} + +bool HueCurveOpData::isDynamic() const noexcept +{ + return m_value->isDynamic(); +} + +DynamicPropertyRcPtr HueCurveOpData::getDynamicProperty() const noexcept +{ + return m_value; +} + +void HueCurveOpData::replaceDynamicProperty(DynamicPropertyHueCurveImplRcPtr prop) noexcept +{ + m_value = prop; +} + +void HueCurveOpData::removeDynamicProperty() noexcept +{ + m_value->makeNonDynamic(); +} + +bool HueCurveOpData::equals(const OpData & other) const +{ + if (!OpData::equals(other)) return false; + + const HueCurveOpData* rop = static_cast(&other); + + if (m_direction != rop->m_direction || + m_style != rop->m_style || + m_bypassLinToLog != rop->m_bypassLinToLog || + !m_value->equals( *(rop->m_value) )) + { + return false; + } + + return true; +} + +bool operator==(const HueCurveOpData & lhs, const HueCurveOpData & rhs) +{ + return lhs.equals(rhs); +} + +} // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.h b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.h new file mode 100644 index 0000000000..53d24e9890 --- /dev/null +++ b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.h @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#ifndef INCLUDED_OCIO_HUECURVE_OPDATA_H +#define INCLUDED_OCIO_HUECURVE_OPDATA_H + + +#include + +#include "Op.h" + + +namespace OCIO_NAMESPACE +{ + +class HueCurveOpData; +typedef OCIO_SHARED_PTR HueCurveOpDataRcPtr; +typedef OCIO_SHARED_PTR ConstHueCurveOpDataRcPtr; + + +class HueCurveOpData : public OpData +{ +public: + + HueCurveOpData(GradingStyle style); + HueCurveOpData(const HueCurveOpData & rhs); + HueCurveOpData(GradingStyle style, + const std::array & curve); + HueCurveOpData & operator=(const HueCurveOpData & rhs); + virtual ~HueCurveOpData(); + + HueCurveOpDataRcPtr clone() const; + + void validate() const override; + + Type getType() const override { return GradingRGBCurveType; } + + bool isNoOp() const override; + bool isIdentity() const override; + + bool hasChannelCrosstalk() const override { return false; } + + bool isInverse(ConstHueCurveOpDataRcPtr & r) const; + HueCurveOpDataRcPtr inverse() const; + + std::string getCacheID() const override; + + GradingStyle getStyle() const noexcept { return m_style; } + void setStyle(GradingStyle style) noexcept; + + const ConstHueCurveRcPtr getValue() const { return m_value->getValue(); } + void setValue(const ConstHueCurveRcPtr & values) { m_value->setValue(values); } + + float getSlope(HueCurveType c, size_t index) const; + void setSlope(HueCurveType c, size_t index, float slope); + bool slopesAreDefault(HueCurveType c) const; + + bool getBypassLinToLog() const noexcept { return m_bypassLinToLog; } + void setBypassLinToLog(bool bypass) noexcept { m_bypassLinToLog = bypass; } + + TransformDirection getDirection() const noexcept; + void setDirection(TransformDirection dir) noexcept; + + bool isDynamic() const noexcept; + DynamicPropertyRcPtr getDynamicProperty() const noexcept; + void replaceDynamicProperty(DynamicPropertyHueCurveImplRcPtr prop) noexcept; + void removeDynamicProperty() noexcept; + + DynamicPropertyHueCurveImplRcPtr getDynamicPropertyInternal() const noexcept + { + return m_value; + } + + bool equals(const OpData & other) const override; + +private: + GradingStyle m_style; + DynamicPropertyHueCurveImplRcPtr m_value; + bool m_bypassLinToLog{ false }; + TransformDirection m_direction{ TRANSFORM_DIR_FORWARD }; +}; + +bool operator==(const HueCurveOpData & lhs, const HueCurveOpData & rhs); + +} // namespace OCIO_NAMESPACE + +#endif diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.cpp new file mode 100644 index 0000000000..e1ef812c5d --- /dev/null +++ b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.cpp @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#include + +#include "Logging.h" +#include "ops/gradingrgbcurve/HueCurveOpGPU.h" +#include "ops/fixedfunction/FixedFunctionOpGPU.h" +#include "ops/fixedfunction/FixedFunctionOpData.h" +#include "utils/StringUtils.h" + + +namespace OCIO_NAMESPACE +{ + +// The curve evaluation is done using a piecewise quadratic polynomial function. The shader +// may handle a dynamic number of curves and a dynamic number of knots and coefficients per +// curve. +// +// For optimization, the knots of ALL the curves are packed in one single array. This is +// exactly the same for coefficients. For example : +// +// KnotsArray = { Curve1[kn0, kn1], Curve2[kn0, kn1, kn2], Curve3[kn0, kn1] } +// +// In order to access knots of a specific curve in this single array, the position of the +// first knot and the number of knots of each curve is stored in an offset array. +// This array is dynamic according to the number of curves. For example : +// +// KnotOffsetArray = {Curve1StartPos, Curve1NumKnots, Curve2StartPos, Curve2NumKnots} +// +// Here is an example of what the arrays would look like in memory with the following +// curve information: +// +// Curve 1 : Knots = { 0, 1, 2 } Coefficients = { 10, 11, 12, 13, 14, 15 } +// Curve 2 : Knots = { 0.1, 0.5, 1, 3 } Coefficients = { 20, 21, 22, 23, 24, 25, 26, 27, 28 } +// +// KnotsArray : { 0, 1, 2, 0.1, 0.5, 1, 3 } +// CoefsArray : { 10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 26, 27, 28 } +// +// KnotsOffsetsArray : { 0, 3, 3, 4 } +// CoefsOffsetsArray : { 0, 6, 6, 9 } +// +// To access the knots of the second curve in C++, you would do the following : +// +// { +// const unsigned curveIdx = 1; // Second curve. This is 0 based. +// const unsigned startPos = KnotsOffsetsArray[curveIdx*2]; // Data is in pairs. +// const unsigned numKnots = KnotsOffsetsArray[curveIdx*2+1]; +// +// const float firstKnot = KnotsArray[startPos]; +// const float lastKnot = KnotsArray[startPos+numKnots-1]; +// } +// +// In GLSL, offset arrays are loaded as vec2 uniforms. To achieve the previous example +// in GLSL, you would do the following : +// +// { +// const int curveIdx = 1; +// const int startPos = KnotsOffsetsArray[curveIdx*2]; +// const int numKnots = KnotsOffsetsArray[curveIdx*2+1]; +// +// const float firstKnot = KnotsArray[startPos].x; +// const float lastKnot = KnotsArray[startPos+numKnots-1].x; +// } +// +// The coefficients array contains the polynomial coefficients which are stored +// as all the quadratic terms for the first curve, then all the linear terms for +// the first curve, then all the constant terms for the first curve. The number +// of coefficient sets is the number of knots minus one. + +namespace +{ +struct GCProperties +{ + std::string m_knotsOffsets{ "knotsOffsets" }; + std::string m_knots{ "knots" }; + std::string m_coefsOffsets{ "coefsOffsets" }; + std::string m_coefs{ "coefs" }; + std::string m_localBypass{ "localBypass" }; + std::string m_eval{ "evalBSplineCurve" }; +}; + +void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, + const GpuShaderCreator::SizeGetter & getSize, + const GpuShaderCreator::VectorFloatGetter & getVector, + unsigned int maxSize, + const std::string & name) +{ + // Add the uniform if it does not already exist. + if (shaderCreator->addUniform(name.c_str(), getSize, getVector)) + { + // Declare uniform. + GpuShaderText stDecl(shaderCreator->getLanguage()); + stDecl.declareUniformArrayFloat(name, maxSize); + shaderCreator->addToDeclareShaderCode(stDecl.string().c_str()); + } +} + +void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, + const GpuShaderCreator::SizeGetter & getSize, + const GpuShaderCreator::VectorIntGetter & getVector, + const std::string & name) +{ + // Add the uniform if it does not already exist. + if (shaderCreator->addUniform(name.c_str(), getSize, getVector)) + { + // Declare uniform. + GpuShaderText stDecl(shaderCreator->getLanguage()); + // Need 2 ints for each curves. + stDecl.declareUniformArrayInt(name, 16); // TODO: Avoid magic numbers (8 Curves * 2 values) + shaderCreator->addToDeclareShaderCode(stDecl.string().c_str()); + } +} + +void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, + const GpuShaderCreator::BoolGetter & getBool, + const std::string & name) +{ + // Add the uniform if it does not already exist. + if (shaderCreator->addUniform(name.c_str(), getBool)) + { + // Declare uniform. + GpuShaderText stDecl(shaderCreator->getLanguage()); + stDecl.declareUniformBool(name); + shaderCreator->addToDeclareShaderCode(stDecl.string().c_str()); + } +} + +std::string BuildResourceNameIndexed(GpuShaderCreatorRcPtr & shaderCreator, const std::string & prefix, + const std::string & base, unsigned int index) +{ + std::string name{ BuildResourceName(shaderCreator, prefix, base) }; + name += "_"; + name += std::to_string(index); + // Note: Remove potentially problematic double underscores from GLSL resource names. + StringUtils::ReplaceInPlace(name, "__", "_"); + return name; +} + +static const std::string opPrefix{ "huecurve" }; + +void SetGCProperties(GpuShaderCreatorRcPtr & shaderCreator, bool dynamic, GCProperties & propNames) +{ + if (dynamic) + { + // If there are several dynamic ops, they will use the same names for uniforms. + propNames.m_knotsOffsets = BuildResourceName(shaderCreator, opPrefix, + propNames.m_knotsOffsets); + propNames.m_knots = BuildResourceName(shaderCreator, opPrefix, propNames.m_knots); + propNames.m_coefsOffsets = BuildResourceName(shaderCreator, opPrefix, + propNames.m_coefsOffsets); + propNames.m_coefs = BuildResourceName(shaderCreator, opPrefix, propNames.m_coefs); + propNames.m_localBypass = BuildResourceName(shaderCreator, opPrefix, + propNames.m_localBypass); + propNames.m_eval = BuildResourceName(shaderCreator, opPrefix, propNames.m_eval); + } + else + { + // Non-dynamic ops need an helper function for each op. + const auto resIndex = shaderCreator->getNextResourceIndex(); + + propNames.m_knotsOffsets = BuildResourceNameIndexed(shaderCreator, opPrefix, + propNames.m_knotsOffsets, resIndex); + propNames.m_knots = BuildResourceNameIndexed(shaderCreator, opPrefix, + propNames.m_knots, resIndex); + propNames.m_coefsOffsets = BuildResourceNameIndexed(shaderCreator, opPrefix, + propNames.m_coefsOffsets, resIndex); + propNames.m_coefs = BuildResourceNameIndexed(shaderCreator, opPrefix, + propNames.m_coefs, resIndex); + propNames.m_eval = BuildResourceNameIndexed(shaderCreator, opPrefix, + propNames.m_eval, resIndex); + } +} + +// Only called once for dynamic ops. +void AddGCPropertiesUniforms(GpuShaderCreatorRcPtr & shaderCreator, + DynamicPropertyHueCurveImplRcPtr & shaderProp, + const GCProperties & propNames) +{ + // Use the shader dynamic property to bind the uniforms. + auto curveProp = shaderProp.get(); + + // Note: No need to add an index to the name to avoid collisions as the dynamic properties + // are unique. + + auto getNK = std::bind(&DynamicPropertyHueCurveImpl::getNumKnots, curveProp); + auto getKO = std::bind(&DynamicPropertyHueCurveImpl::getKnotsOffsetsArray, + curveProp); + auto getK = std::bind(&DynamicPropertyHueCurveImpl::getKnotsArray, curveProp); + auto getNC = std::bind(&DynamicPropertyHueCurveImpl::getNumCoefs, curveProp); + auto getCO = std::bind(&DynamicPropertyHueCurveImpl::getCoefsOffsetsArray, + curveProp); + auto getC = std::bind(&DynamicPropertyHueCurveImpl::getCoefsArray, curveProp); + auto getLB = std::bind(&DynamicPropertyHueCurveImpl::getLocalBypass, curveProp); + // Uniforms are added if they are not already there (added by another op). + AddUniform(shaderCreator, DynamicPropertyHueCurveImpl::GetNumOffsetValues, + getKO, propNames.m_knotsOffsets); + AddUniform(shaderCreator, getNK, getK, + DynamicPropertyHueCurveImpl::GetMaxKnots(), + propNames.m_knots); + AddUniform(shaderCreator, DynamicPropertyHueCurveImpl::GetNumOffsetValues, + getCO, propNames.m_coefsOffsets); + AddUniform(shaderCreator, getNC, getC, + DynamicPropertyHueCurveImpl::GetMaxCoefs(), + propNames.m_coefs); + AddUniform(shaderCreator, getLB, propNames.m_localBypass); +} + +void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, + ConstHueCurveOpDataRcPtr & gcData, + const GCProperties & props, + bool dyn) +{ + GpuShaderText st(shaderCreator->getLanguage()); + + // Dynamic version uses uniforms declared globally. Non-dynamic version declares local + // variables in the op specific helper function. + if (!dyn) + { + auto propGC = gcData->getDynamicPropertyInternal(); + + // 2 ints for each curve. + st.newLine() << ""; + st.declareIntArrayConst(props.m_knotsOffsets, 8 * 2, propGC->getKnotsOffsetsArray()); // TODO: Avoid magic numbers (8 Curves * 2 values) + st.declareFloatArrayConst(props.m_knots, propGC->getNumKnots(), propGC->getKnotsArray()); + st.declareIntArrayConst(props.m_coefsOffsets, 8 * 2, propGC->getCoefsOffsetsArray()); + st.declareFloatArrayConst(props.m_coefs, propGC->getNumCoefs(), propGC->getCoefsArray()); + } + + st.newLine() << ""; + if (shaderCreator->getLanguage() == LANGUAGE_OSL_1 || shaderCreator->getLanguage() == GPU_LANGUAGE_MSL_2_0) + { + st.newLine() << st.floatKeyword() << " " << props.m_eval << "(int curveIdx, float x, float identity_x)"; + } + else + { + st.newLine() << st.floatKeyword() << " " << props.m_eval << "(in int curveIdx, in float x, in float identity_x)"; + } + st.newLine() << "{"; + st.indent(); + + const bool isInv = gcData->getDirection() == TRANSFORM_DIR_INVERSE; + GradingBSplineCurveImpl::AddShaderEvalHueCurve(st, props.m_knotsOffsets, props.m_coefsOffsets, + props.m_knots, props.m_coefs, isInv); + + st.dedent(); + st.newLine() << "}"; + + shaderCreator->addToHelperShaderCode(st.string().c_str()); +} + + +void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, + GpuShaderText & st, + const GCProperties & props, + bool dyn, + bool doLinToLog, + GradingStyle style) +{ + if (dyn) + { + st.newLine() << "if (!" << props.m_localBypass << ")"; + st.newLine() << "{"; + st.indent(); + } + + + // Add the conversion from RGB to HSY. + { + FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + switch(style) + { + case GRADING_LIN: + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + break; + case GRADING_LOG: + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LOG; + break; + case GRADING_VIDEO: + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_VID; + break; + } + + st.newLine() << "{"; // establish scope so local variable names won't conflict + st.indent(); + ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); + GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); + st.dedent(); + st.newLine() << "}"; + } + + if (doLinToLog) + { + // NB: Although the linToLog and logToLin are correct inverses, the limits of + // floating-point arithmetic cause errors in the lowest bit of the round trip. + + st.newLine() << "// Convert from lin to log."; + AddLinToLogShaderChannelBlue(shaderCreator, st); + st.newLine() << ""; + } + + const std::string pix(shaderCreator->getPixelName()); + + st.newLine() << ""; + st.newLine() << "float hueSatGain = max(0., " << props.m_eval << "(1, outColor.r, 1.));"; // HUE-SAT + st.newLine() << "float hueLumGain = max(0., " << props.m_eval << "(2, outColor.r, 1.));"; // HUE-LUM + st.newLine() << "outColor.r = " << props.m_eval << "(0, outColor.r, outColor.r);"; // HUE-HUE + st.newLine() << "outColor.g = max(0., " << props.m_eval << "(4, outColor.g, outColor.g));"; // SAT-SAT + st.newLine() << "float lumSatGain = max(0., " << props.m_eval << "(3, outColor.b, 1.));"; // LUM-SAT + st.newLine() << "float satGain = lumSatGain * hueSatGain;"; + st.newLine() << "outColor.g = satGain * outColor.g;"; + st.newLine() << "float satLumGain = max(0., " << props.m_eval << "(6, outColor.g, 1.));"; // SAT-LUM + st.newLine() << "outColor.b = " << props.m_eval << "(5, outColor.b, outColor.b);"; // LUM-LUM + st.newLine() << ""; + + if (doLinToLog) + { + st.newLine() << ""; + st.newLine() << "// Convert from log to lin."; + AddLogToLinShaderChannelBlue(shaderCreator, st); + } + + st.newLine() << ""; + st.newLine() << "hueLumGain = 1. - (1. - hueLumGain) * min( 1., outColor.g );"; + if (style == GRADING_LOG) + // Use shift rather than scale for log mode. + st.newLine() << "outColor.b = outColor.b + (hueLumGain + satLumGain - 2.) * 0.1;"; + else + // Note this is applied in linear space, for linear style. + st.newLine() << "outColor.b = outColor.b * hueLumGain * satLumGain;"; + st.newLine() << ""; + + st.newLine() << "outColor.r = outColor.r - floor( outColor.r );"; + st.newLine() << "outColor.r = outColor.r + " << props.m_eval << "(7, outColor.r, 0.);"; // HUE-FX + + { + FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + switch(style) + { + case GRADING_LIN: + hsyStyle = FixedFunctionOpData::HSY_LIN_TO_RGB; + break; + case GRADING_LOG: + hsyStyle = FixedFunctionOpData::HSY_LOG_TO_RGB; + break; + case GRADING_VIDEO: + hsyStyle = FixedFunctionOpData::HSY_VID_TO_RGB; + break; + } + st.newLine() << "{"; + st.indent(); + ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); + GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); + st.dedent(); + st.newLine() << "}"; + } + + if (dyn) + { + st.dedent(); + st.newLine() << "}"; + } +} + +// TODO: Implement the inverse shader. +//void AddGCInverseShader(GpuShaderCreatorRcPtr & shaderCreator, +// GpuShaderText & st, +// const GCProperties & props, +// bool dyn, +// bool doLinToLog, +// GradingStyle style) +//{ +// if (dyn) +// { +// st.newLine() << "if (!" << props.m_localBypass << ")"; +// st.newLine() << "{"; +// st.indent(); +// } +// +// if (doLinToLog) +// { +// // NB: Although the linToLog and logToLin are correct inverses, the limits of +// // floating-point arithmetic cause errors in the lowest bit of the round trip. +// +// st.newLine() << "// Convert from lin to log."; +// AddLinToLogShader(shaderCreator, st); +// st.newLine() << ""; +// } +// +// const std::string pix(shaderCreator->getPixelName()); +// +// // Call the curve evaluation method for each curve. +// st.newLine() << pix << ".rgb.r = " << props.m_eval << "(3, " << pix << ".rgb.r);"; // MASTER +// st.newLine() << pix << ".rgb.g = " << props.m_eval << "(3, " << pix << ".rgb.g);"; // MASTER +// st.newLine() << pix << ".rgb.b = " << props.m_eval << "(3, " << pix << ".rgb.b);"; // MASTER +// st.newLine() << pix << ".rgb.r = " << props.m_eval << "(0, " << pix << ".rgb.r);"; // RED +// st.newLine() << pix << ".rgb.g = " << props.m_eval << "(1, " << pix << ".rgb.g);"; // GREEN +// st.newLine() << pix << ".rgb.b = " << props.m_eval << "(2, " << pix << ".rgb.b);"; // BLUE +// +// if (doLinToLog) +// { +// st.newLine() << ""; +// st.newLine() << "// Convert from log to lin."; +// AddLogToLinShader(shaderCreator, st); +// } +// +// if (dyn) +// { +// st.dedent(); +// st.newLine() << "}"; +// } +//} + +} + +void GetHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, + ConstHueCurveOpDataRcPtr & gcData) +{ + const bool dyn = gcData->isDynamic() && shaderCreator->getLanguage() != LANGUAGE_OSL_1; + if (!dyn) + { + auto propGC = gcData->getDynamicPropertyInternal(); + if (propGC->getLocalBypass()) + { + return; + } + } + + if (gcData->isDynamic() && shaderCreator->getLanguage() == LANGUAGE_OSL_1) + { + std::string msg("The dynamic properties are not yet supported by the 'Open Shading language"\ + " (OSL)' translation: The '"); + msg += opPrefix; + msg += "' dynamic property is replaced by a local variable."; + + LogWarning(msg); + } + + const GradingStyle style = gcData->getStyle(); + const TransformDirection dir = gcData->getDirection(); + + GpuShaderText st(shaderCreator->getLanguage()); + st.indent(); + + st.newLine() << ""; + st.newLine() << "// Add HueCurve '" + << TransformDirectionToString(dir) << " processing"; + st.newLine() << ""; + st.newLine() << "{"; + st.indent(); + + GCProperties properties; + SetGCProperties(shaderCreator, dyn, properties); + + if (dyn) + { + // Add the dynamic property to the shader creator. + auto prop = gcData->getDynamicPropertyInternal(); + + // Property is decoupled. + auto shaderProp = prop->createEditableCopy(); + DynamicPropertyRcPtr newProp = shaderProp; + shaderCreator->addDynamicProperty(newProp); + + // Add uniforms only if needed. + AddGCPropertiesUniforms(shaderCreator, shaderProp, properties); // _addAttributeTextToShaderProgram + + // Add helper function plus global variables if they are not dynamic. + AddCurveEvalMethodTextToShaderProgram(shaderCreator, gcData, properties, dyn); // _addHelperMethodTextToShaderProgram + } + else + { + // Declare the op specific helper function. + AddCurveEvalMethodTextToShaderProgram(shaderCreator, gcData, properties, dyn); + } + + const bool bypassLinToLog = (style == GRADING_LIN) && !gcData->getBypassLinToLog(); + switch (dir) + { + case TRANSFORM_DIR_FORWARD: + AddGCForwardShader(shaderCreator, st, properties, dyn, bypassLinToLog, style); // _addProcessingTextToShaderProgram + break; + case TRANSFORM_DIR_INVERSE: // TODO: Implement the inverse shader. + //AddGCInverseShader(shaderCreator, st, properties, dyn, bypassLinToLog, style); // _addProcessingTextToShaderProgram + break; + } + + st.dedent(); + st.newLine() << "}"; + + st.dedent(); + shaderCreator->addToFunctionShaderCode(st.string().c_str()); +} + +} // OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.h b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.h new file mode 100644 index 0000000000..148f1d1339 --- /dev/null +++ b/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#ifndef INCLUDED_OCIO_HUECURVE_GPU_H +#define INCLUDED_OCIO_HUECURVE_GPU_H + +#include + +#include "GpuShaderUtils.h" +#include "ops/gradingrgbcurve/HueCurveOpData.h" + +namespace OCIO_NAMESPACE +{ + +void GetHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, + ConstHueCurveOpDataRcPtr & gpData); + +} // namespace OCIO_NAMESPACE + +#endif + + diff --git a/src/OpenColorIO/transforms/HueCurveTransform.cpp b/src/OpenColorIO/transforms/HueCurveTransform.cpp new file mode 100644 index 0000000000..496803c288 --- /dev/null +++ b/src/OpenColorIO/transforms/HueCurveTransform.cpp @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#include + +#include + +#include "transforms/HueCurveTransform.h" + +namespace OCIO_NAMESPACE +{ + +HueCurveTransformRcPtr HueCurveTransform::Create(GradingStyle style) +{ + return HueCurveTransformRcPtr(new HueCurveTransformImpl(style), + &HueCurveTransformImpl::deleter); +} + +HueCurveTransformImpl::HueCurveTransformImpl(GradingStyle style) : + m_data(style) +{ +} + +void HueCurveTransformImpl::deleter(HueCurveTransform* t) +{ + delete static_cast(t); +} + +TransformRcPtr HueCurveTransformImpl::createEditableCopy() const +{ + HueCurveTransformRcPtr transform = HueCurveTransform::Create(getStyle()); + dynamic_cast(transform.get())->data() = data(); + return transform; +} + +TransformDirection HueCurveTransformImpl::getDirection() const noexcept +{ + return data().getDirection(); +} + +void HueCurveTransformImpl::setDirection(TransformDirection dir) noexcept +{ + data().setDirection(dir); +} + +void HueCurveTransformImpl::validate() const +{ + try + { + Transform::validate(); + data().validate(); + } + catch(Exception & ex) + { + std::string errMsg("HueCurveTransform validation failed: "); + errMsg += ex.what(); + throw Exception(errMsg.c_str()); + } + + return; +} + +FormatMetadata & HueCurveTransformImpl::getFormatMetadata() noexcept +{ + return data().getFormatMetadata(); +} + +const FormatMetadata & HueCurveTransformImpl::getFormatMetadata() const noexcept +{ + return data().getFormatMetadata(); +} + + +bool HueCurveTransformImpl::equals(const HueCurveTransform & other) const noexcept +{ + if (this == &other) return true; + return data() == dynamic_cast(&other)->data(); +} + +GradingStyle HueCurveTransformImpl::getStyle() const noexcept +{ + return data().getStyle(); +} + +void HueCurveTransformImpl::setStyle(GradingStyle style) noexcept +{ + data().setStyle(style); +} + +const ConstHueCurveRcPtr HueCurveTransformImpl::getValue() const +{ + return data().getValue(); +} + + +void HueCurveTransformImpl::setValue(const ConstHueCurveRcPtr & values) +{ + data().setValue(values); +} + +float HueCurveTransformImpl::getSlope(HueCurveType c, size_t index) const +{ + return data().getSlope(c, index); +} + +void HueCurveTransformImpl::setSlope(HueCurveType c, size_t index, float slope) +{ + data().setSlope(c, index, slope); +} + +bool HueCurveTransformImpl::slopesAreDefault(HueCurveType c) const +{ + return data().slopesAreDefault(c); +} + +bool HueCurveTransformImpl::getBypassLinToLog() const noexcept +{ + return data().getBypassLinToLog(); +} + +void HueCurveTransformImpl::setBypassLinToLog(bool bypass) noexcept +{ + data().setBypassLinToLog(bypass); +} + +bool HueCurveTransformImpl::isDynamic() const noexcept +{ + return data().isDynamic(); +} + +void HueCurveTransformImpl::makeDynamic() noexcept +{ + data().getDynamicPropertyInternal()->makeDynamic(); +} + +void HueCurveTransformImpl::makeNonDynamic() noexcept +{ + data().getDynamicPropertyInternal()->makeNonDynamic(); +} + +std::ostream& operator<< (std::ostream & os, const HueCurveTransform & t) noexcept +{ + os << ""; + return os; +} + +// TODO: Duplicated from GradingRGB, should it be accessible here? +//std::ostream & operator<<(std::ostream & os, const GradingControlPoint & cp) +//{ +// os << ""; +// return os; +//} +// +//std::ostream & operator<<(std::ostream & os, const GradingBSplineCurve & bspline) +//{ +// os << ""; +// return os; +//} + +std::ostream & operator<<(std::ostream & os, const HueCurve & hueCurve) +{ + os << ""; + + return os; +} + +} // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/transforms/HueCurveTransform.h b/src/OpenColorIO/transforms/HueCurveTransform.h new file mode 100644 index 0000000000..05cdff727e --- /dev/null +++ b/src/OpenColorIO/transforms/HueCurveTransform.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#ifndef INCLUDED_OCIO_HUECURVETRANSFORM_H +#define INCLUDED_OCIO_HUECURVETRANSFORM_H + +#include + +#include "ops/gradingrgbcurve/HueCurveOpData.h" + + +namespace OCIO_NAMESPACE +{ + +class HueCurveTransformImpl : public HueCurveTransform +{ +public: + HueCurveTransformImpl(GradingStyle style); + HueCurveTransformImpl() = delete; + HueCurveTransformImpl(const HueCurveTransformImpl &) = delete; + HueCurveTransformImpl& operator=(const HueCurveTransformImpl &) = delete; + ~HueCurveTransformImpl() override = default; + + TransformRcPtr createEditableCopy() const override; + + TransformDirection getDirection() const noexcept override; + void setDirection(TransformDirection dir) noexcept override; + + FormatMetadata & getFormatMetadata() noexcept override; + const FormatMetadata & getFormatMetadata() const noexcept override; + + void validate() const override; + + bool equals(const HueCurveTransform & other) const noexcept override; + + GradingStyle getStyle() const noexcept override; + + void setStyle(GradingStyle style) noexcept override; + + const ConstHueCurveRcPtr getValue() const override; + + void setValue(const ConstHueCurveRcPtr & values) override; + + float getSlope(HueCurveType c, size_t index) const override; + void setSlope(HueCurveType c, size_t index, float slope) override; + bool slopesAreDefault(HueCurveType c) const override; + + bool getBypassLinToLog() const noexcept override; + void setBypassLinToLog(bool bypass) noexcept override; + + bool isDynamic() const noexcept override; + void makeDynamic() noexcept override; + void makeNonDynamic() noexcept override; + + HueCurveOpData & data() noexcept { return m_data; } + const HueCurveOpData & data() const noexcept { return m_data; } + + static void deleter(HueCurveTransform* t); + +private: + HueCurveOpData m_data; +}; + + +} // namespace OCIO_NAMESPACE + +#endif // INCLUDED_OCIO_GRADINGPRIMARYTRANSFORM_H diff --git a/tests/cpu/CMakeLists.txt b/tests/cpu/CMakeLists.txt index 66c32cb8a7..652384964c 100755 --- a/tests/cpu/CMakeLists.txt +++ b/tests/cpu/CMakeLists.txt @@ -153,6 +153,7 @@ set(SOURCES ops/gamma/GammaOpGPU.cpp ops/gradingprimary/GradingPrimaryOpGPU.cpp ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp + ops/gradingrgbcurve/HueCurveOpGPU.cpp ops/gradingtone/GradingToneOpGPU.cpp ops/log/LogOpGPU.cpp ops/lut1d/Lut1DOpCPU_SSE2.cpp @@ -254,7 +255,10 @@ set(TESTS ops/gradingprimary/GradingPrimaryOp_tests.cpp ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp ops/gradingrgbcurve/GradingRGBCurve_tests.cpp + ops/gradingrgbcurve/HueCurve_tests.cpp ops/gradingrgbcurve/GradingRGBCurveOpCPU_tests.cpp + ops/gradingrgbcurve/HueCurveOp_tests.cpp + ops/gradingrgbcurve/HueCurveOpData_tests.cpp ops/gradingrgbcurve/GradingRGBCurveOpData_tests.cpp ops/gradingrgbcurve/GradingRGBCurveOp_tests.cpp ops/gradingtone/GradingTone_tests.cpp @@ -303,6 +307,7 @@ set(TESTS transforms/FixedFunctionTransform_tests.cpp transforms/GradingPrimaryTransform_tests.cpp transforms/GradingRGBCurveTransform_tests.cpp + transforms/HueCurveTransform_tests.cpp transforms/GradingToneTransform_tests.cpp transforms/GroupTransform_tests.cpp transforms/LogAffineTransform_tests.cpp diff --git a/tests/cpu/ops/gradingrgbcurve/HueCurveOpData_tests.cpp b/tests/cpu/ops/gradingrgbcurve/HueCurveOpData_tests.cpp new file mode 100644 index 0000000000..07c6c47dea --- /dev/null +++ b/tests/cpu/ops/gradingrgbcurve/HueCurveOpData_tests.cpp @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#include "ops/gradingrgbcurve/HueCurveOpData.cpp" + +#include "testutils/UnitTest.h" + +namespace OCIO = OCIO_NAMESPACE; + +OCIO_ADD_TEST(HueCurveOpData, accessors) +{ + // Create GradingRGBCurveOpData and check values. Changes them and check. + //OCIO::HueCurveOpData gc; + + //static constexpr char expected[]{ "log forward " + // "]>, " + // "green=]>, " + // "blue=]>, " + // "master=]>>" }; + //OCIO_CHECK_EQUAL(gc.getCacheID(), expected); + + //OCIO_CHECK_EQUAL(gc.getStyle(), OCIO::GRADING_LOG); + //auto curves = gc.getValue(); + //OCIO_REQUIRE_ASSERT(curves); + //OCIO_CHECK_ASSERT(curves->isIdentity()); + //OCIO_CHECK_ASSERT(gc.isIdentity()); + //OCIO_CHECK_ASSERT(gc.isNoOp()); + //OCIO_CHECK_ASSERT(!gc.hasChannelCrosstalk()); + //OCIO_CHECK_ASSERT(!gc.getBypassLinToLog()); + + //gc.setStyle(OCIO::GRADING_LIN); + //OCIO_CHECK_EQUAL(gc.getStyle(), OCIO::GRADING_LIN); + //gc.setBypassLinToLog(true); + //OCIO_CHECK_ASSERT(gc.getBypassLinToLog()); + + // Get dynamic property as a generic dynamic property and as a type one and verify they are + // the same and can be made dynamic. + //OCIO_CHECK_ASSERT(!gc.isDynamic()); + //auto dp = gc.getDynamicProperty(); + //OCIO_REQUIRE_ASSERT(dp); + //OCIO_CHECK_EQUAL(dp->getType(), OCIO::DYNAMIC_PROPERTY_GRADING_RGBCURVE); + //auto dpImpl = gc.getDynamicPropertyInternal(); + //OCIO_REQUIRE_ASSERT(dpImpl); + //OCIO_CHECK_ASSERT(dp == dpImpl); + //OCIO_CHECK_ASSERT(!dpImpl->isDynamic()); + //dpImpl->makeDynamic(); + //OCIO_CHECK_ASSERT(gc.isDynamic()); +// + //OCIO_CHECK_EQUAL(gc.getDirection(), OCIO::TRANSFORM_DIR_FORWARD); + //gc.setDirection(OCIO::TRANSFORM_DIR_INVERSE); + //OCIO_CHECK_EQUAL(gc.getDirection(), OCIO::TRANSFORM_DIR_INVERSE); + + // Test operator==. + //OCIO::HueCurveOpData gc1{}; + //OCIO::HueCurveOpData gc2{}; +// + //OCIO_CHECK_ASSERT(gc1 == gc2); + //gc1.setDirection(OCIO::TRANSFORM_DIR_INVERSE); + //OCIO_CHECK_ASSERT(!(gc1 == gc2)); + //gc2.setDirection(OCIO::TRANSFORM_DIR_INVERSE); + //OCIO_CHECK_ASSERT(gc1 == gc2); + + //gc1.setStyle(OCIO::GRADING_LOG); + //OCIO_CHECK_ASSERT(!(gc1 == gc2)); + //gc2.setStyle(OCIO::GRADING_LOG); + //OCIO_CHECK_ASSERT(gc1 == gc2); +// + //auto v1 = gc1.getValue()->createEditableCopy(); + //auto red = v1->getCurve(OCIO::RGB_RED); + //red->setNumControlPoints(4); + //red->getControlPoint(3).m_x = red->getControlPoint(2).m_x + 1.f; + //red->getControlPoint(3).m_y = red->getControlPoint(2).m_y + 0.5f; + //gc1.setValue(v1); + //OCIO_CHECK_ASSERT(!(gc1 == gc2)); + //auto v2 = gc2.getValue()->createEditableCopy(); + //auto red2 = v2->getCurve(OCIO::RGB_RED); + //red2->setNumControlPoints(4); + //red2->getControlPoint(3).m_x = red2->getControlPoint(2).m_x + 1.f; + //red2->getControlPoint(3).m_y = red2->getControlPoint(2).m_y + 0.5f; + //gc2.setValue(v2); + //OCIO_CHECK_ASSERT(gc1 == gc2); +// + //gc1.setSlope(OCIO::RGB_BLUE, 2, 0.9f); + //OCIO_CHECK_EQUAL(gc1.getSlope(OCIO::RGB_BLUE, 2), 0.9f); + //OCIO_CHECK_ASSERT(gc1.slopesAreDefault(OCIO::RGB_GREEN)); + //OCIO_CHECK_ASSERT(!gc1.slopesAreDefault(OCIO::RGB_BLUE)); +// + //OCIO_CHECK_EQUAL(gc1.isIdentity(), false); + //OCIO_CHECK_ASSERT(!gc1.hasChannelCrosstalk()); +// + //// Check isInverse. +// + //// We have equal ops, inverse one. + //gc1.setDirection(OCIO::TRANSFORM_DIR_FORWARD); + //// Need a shared pointer for the parameter. + //OCIO::ConstGradingRGBCurveOpDataRcPtr gcptr2 = gc1.clone(); + //gc1.setDirection(OCIO::TRANSFORM_DIR_INVERSE); + //OCIO_CHECK_ASSERT(gc1.isInverse(gcptr2)); + //// Change value of one: no longer inverse. + //red->getControlPoint(3).m_y += 0.1f; + //gc1.setValue(v1); + //OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); + //// Restore value. + //red->getControlPoint(3).m_y -= 0.1f; + //gc1.setValue(v1); + //OCIO_CHECK_ASSERT(gc1.isInverse(gcptr2)); + //// Change direction: no longer inverse. + //gc1.setDirection(OCIO::TRANSFORM_DIR_FORWARD); + //OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); +} + +OCIO_ADD_TEST(HueCurveOpData, validate) +{ + // Default is valid. + //OCIO::HueCurveOpData gc; + //OCIO_CHECK_NO_THROW(gc.validate()); + + // Curves with a single control point are not valid. + //auto curve = OCIO::GradingBSplineCurve::Create(1); + //auto curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); + //OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, + // "There must be at least 2 control points."); +// + //// Curve x coordinates have to increase. + //curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.7f,0.3f }, + // { 0.5f,0.7f },{ 1.f,1.f } }); + //curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); + //OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, + // "has a x coordinate '0.5' that is less from previous control " + // "point x cooordinate '0.7'."); +// + //// Fix the curve x coordinate. + //curve->getControlPoint(1).m_x = 0.3f; + //curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); + //OCIO_CHECK_NO_THROW(gc.setValue(curves)); + //OCIO_CHECK_NO_THROW(gc.validate()); +} diff --git a/tests/cpu/ops/gradingrgbcurve/HueCurveOp_tests.cpp b/tests/cpu/ops/gradingrgbcurve/HueCurveOp_tests.cpp new file mode 100644 index 0000000000..b755ad4ffa --- /dev/null +++ b/tests/cpu/ops/gradingrgbcurve/HueCurveOp_tests.cpp @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#include "OpBuilders.h" + +#include "ops/gradingrgbcurve/HueCurveOp.cpp" + +#include "testutils/UnitTest.h" + +namespace OCIO = OCIO_NAMESPACE; + +OCIO_ADD_TEST(HueCurveOp, create) +{ + //OCIO::TransformDirection direction = OCIO::TRANSFORM_DIR_FORWARD; + //OCIO::HueCurveOpDataRcPtr data = + // std::make_shared(); + //OCIO::OpRcPtrVec ops; +// + //OCIO_CHECK_NO_THROW(OCIO::CreateHueCurveOp(ops, data, direction)); + //OCIO_REQUIRE_EQUAL(ops.size(), 1); + //OCIO_REQUIRE_ASSERT(ops[0]); + //OCIO_CHECK_EQUAL(ops[0]->getInfo(), ""); + //OCIO_CHECK_ASSERT(ops[0]->isIdentity()); + //OCIO_CHECK_ASSERT(ops[0]->isNoOp()); + + //data->getDynamicPropertyInternal()->makeDynamic(); + //OCIO_CHECK_NO_THROW(OCIO::CreateGradingRGBCurveOp(ops, data, direction)); + //OCIO_REQUIRE_EQUAL(ops.size(), 2); + //OCIO_REQUIRE_ASSERT(ops[1]); + //OCIO_CHECK_EQUAL(ops[1]->getInfo(), ""); + //OCIO_CHECK_ASSERT(!ops[1]->isIdentity()); + //OCIO_CHECK_ASSERT(!ops[1]->isNoOp()); +} + +OCIO_ADD_TEST(HueCurveOp, create_transform) +{ + //OCIO::TransformDirection direction = OCIO::TRANSFORM_DIR_FORWARD; + //OCIO::HueCurveOpDataRcPtr data = + // std::make_shared(); + ////data->getDynamicPropertyInternal()->makeDynamic(); + //OCIO::OpRcPtrVec ops; +// + //OCIO_CHECK_NO_THROW(OCIO::CreateHueCurveOp(ops, data, direction)); + //OCIO_REQUIRE_EQUAL(ops.size(), 1); + + //OCIO::GroupTransformRcPtr group = OCIO::GroupTransform::Create(); +// + //OCIO::ConstOpRcPtr op(ops[0]); +// + //OCIO::CreateGradingRGBCurveTransform(group, op); + //OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 1); + //auto transform = group->getTransform(0); + //OCIO_REQUIRE_ASSERT(transform); + //auto gcTransform = OCIO_DYNAMIC_POINTER_CAST(transform); + //OCIO_REQUIRE_ASSERT(gcTransform); + //OCIO_CHECK_EQUAL(gcTransform->getStyle(), OCIO::GRADING_LOG); + //OCIO_CHECK_ASSERT(gcTransform->isDynamic()); +} + +OCIO_ADD_TEST(HueCurveOp, build_ops) +{ + //OCIO::ConstConfigRcPtr config = OCIO::Config::CreateRaw(); +// + //auto gcTransform = OCIO::HueCurveTransform::Create(); + //OCIO_CHECK_ASSERT(gcTransform.get()); +// + //// Identity does create an op. + //OCIO::OpRcPtrVec ops; + //OCIO::BuildOps(ops, *(config.get()), config->getCurrentContext(), gcTransform, + // OCIO::TRANSFORM_DIR_FORWARD); + //OCIO_REQUIRE_EQUAL(ops.size(), 1); + //OCIO_CHECK_ASSERT(ops[0]->isIdentity()); + //OCIO_CHECK_ASSERT(ops[0]->isNoOp()); + //ops.clear(); +// + //// Make it dynamic and keep default values. + //gcTransform->makeDynamic(); +// + //OCIO::BuildOps(ops, *(config.get()), config->getCurrentContext(), gcTransform, + // OCIO::TRANSFORM_DIR_FORWARD); +// + //OCIO_REQUIRE_EQUAL(ops.size(), 1); + //OCIO_REQUIRE_ASSERT(ops[0]); + //OCIO_CHECK_EQUAL(ops[0]->getInfo(), ""); + //OCIO::ConstGradingRGBCurveOpRcPtr gco = OCIO_DYNAMIC_POINTER_CAST(ops[0]); + //auto data = gco->data(); + //OCIO_REQUIRE_ASSERT(data); + //auto gcd = OCIO_DYNAMIC_POINTER_CAST(data); + //OCIO_REQUIRE_ASSERT(gcd); + //OCIO_CHECK_ASSERT(gcd->isDynamic()); +// + //auto valsOp = gcd->getValue(); + //OCIO_CHECK_EQUAL(3, valsOp->getCurve(OCIO::RGB_GREEN)->getNumControlPoints()); +// + //// Create processor with dynamic identity before changing the transform. + //auto proc = config->getProcessor(gcTransform); + //OCIO_CHECK_ASSERT(proc->hasDynamicProperty(OCIO::DYNAMIC_PROPERTY_GRADING_RGBCURVE)); + //OCIO_CHECK_ASSERT(!proc->hasDynamicProperty(OCIO::DYNAMIC_PROPERTY_EXPOSURE)); +// + //auto cpu = proc->getDefaultCPUProcessor(); +// + //// Sharing of dynamic properties is done through processor, changing the source will not + //// change the op. + //auto curve = OCIO::GradingBSplineCurve::Create({ { 0.f,1.f },{ 0.2f,0.3f }, + // { 0.5f,0.8f },{ 2.f,1.5f } }); + //auto rgbCurve = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); + //gcTransform->setValue(rgbCurve); +// + //// Still use the default identity curves. + //valsOp = gcd->getValue(); + //OCIO_CHECK_EQUAL(3, valsOp->getCurve(OCIO::RGB_GREEN)->getNumControlPoints()); +// + //// Get dynamic property from the CPU proc. + //OCIO::DynamicPropertyRcPtr dp; + //OCIO_CHECK_NO_THROW(dp = cpu->getDynamicProperty(OCIO::DYNAMIC_PROPERTY_GRADING_RGBCURVE)); + //OCIO_REQUIRE_ASSERT(dp); + //// Get typed value accessor. + //auto dpgc = OCIO_DYNAMIC_POINTER_CAST(dp); + //OCIO_REQUIRE_ASSERT(dpgc); +// + //float pixel[]{ 0.0f, 0.2f, 2.0f }; + //cpu->applyRGB(pixel); + //// Default values are identity. + //static constexpr float error = 1e-5f; + //OCIO_CHECK_CLOSE(pixel[0], 0.0f, error); + //OCIO_CHECK_CLOSE(pixel[1], 0.2f, error); + //OCIO_CHECK_CLOSE(pixel[2], 2.0f, error); +// + //// Use other curve that has 4 control points. + //dpgc->setValue(rgbCurve); +// + //// Control point has moved. + //cpu->applyRGB(pixel); + //OCIO_CHECK_CLOSE(pixel[0], 1.11148262f, error); + //OCIO_CHECK_CLOSE(pixel[1], 0.04518771f, error); + //OCIO_CHECK_CLOSE(pixel[2], 1.32527864f, error); +} diff --git a/tests/cpu/ops/gradingrgbcurve/HueCurve_tests.cpp b/tests/cpu/ops/gradingrgbcurve/HueCurve_tests.cpp new file mode 100644 index 0000000000..394d222307 --- /dev/null +++ b/tests/cpu/ops/gradingrgbcurve/HueCurve_tests.cpp @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#include "DynamicProperty.h" +#include "ops/gradingrgbcurve/HueCurve.cpp" + +#include "testutils/UnitTest.h" + +namespace OCIO = OCIO_NAMESPACE; + +OCIO_ADD_TEST(HueCurve, basic) +{ + //auto curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.2f,0.2f }, + // { 0.5f,0.7f },{ 1.f,1.f } }); + //OCIO::ConstGradingBSplineCurveRcPtr curveR = curve; + //OCIO_CHECK_EQUAL(0.2f, curveR->getControlPoint(1).m_y); + //curve->getControlPoint(1).m_y = 0.3f; + //OCIO_CHECK_EQUAL(0.3f, curveR->getControlPoint(1).m_y); + //OCIO::ConstGradingBSplineCurveRcPtr curveG = OCIO::GradingBSplineCurve::Create(4); + //OCIO::ConstGradingBSplineCurveRcPtr curveB = OCIO::GradingBSplineCurve::Create(3); + //OCIO::ConstGradingBSplineCurveRcPtr curveM = OCIO::GradingBSplineCurve::Create(2); + //// The Create function takes 4 pointers to curves and creates new curves that are copies of + //// the 4 parameters. + //auto rgbCurve = OCIO::GradingRGBCurve::Create(curveR, curveG, curveB, curveM); + //OCIO_REQUIRE_ASSERT(rgbCurve); + //OCIO_REQUIRE_ASSERT(rgbCurve->getCurve(OCIO::RGB_RED)); + //OCIO_REQUIRE_ASSERT(rgbCurve->getCurve(OCIO::RGB_GREEN)); + //OCIO_REQUIRE_ASSERT(rgbCurve->getCurve(OCIO::RGB_BLUE)); + //OCIO_REQUIRE_ASSERT(rgbCurve->getCurve(OCIO::RGB_MASTER)); + //OCIO_CHECK_THROW_WHAT(rgbCurve->getCurve(OCIO::RGB_NUM_CURVES), OCIO::Exception, + // "Invalid curve."); + //auto copiedCurve = rgbCurve->getCurve(OCIO::RGB_RED); + //OCIO_CHECK_EQUAL(0.3f, copiedCurve->getControlPoint(1).m_y); + //curve->getControlPoint(1).m_y = 0.4f; + //OCIO_CHECK_EQUAL(0.3f, copiedCurve->getControlPoint(1).m_y); +// + //// Test default curves. + //auto rgbCurveLin = OCIO::GradingRGBCurve::Create(OCIO::GRADING_LIN); + //auto rgbCurveLog = OCIO::GradingRGBCurve::Create(OCIO::GRADING_LOG); + //auto rgbCurveVideo = OCIO::GradingRGBCurve::Create(OCIO::GRADING_VIDEO); + //OCIO_REQUIRE_ASSERT(rgbCurveLin && rgbCurveLog && rgbCurveVideo); + //OCIO_CHECK_ASSERT(*rgbCurveLog == *rgbCurveVideo); + //OCIO_CHECK_ASSERT(*rgbCurveLog != *rgbCurveLin); + //OCIO_CHECK_ASSERT(*rgbCurveLog->getCurve(OCIO::RGB_RED) == + // *rgbCurveLog->getCurve(OCIO::RGB_GREEN)); + //OCIO_CHECK_ASSERT(*rgbCurveLog->getCurve(OCIO::RGB_RED) == + // *rgbCurveLog->getCurve(OCIO::RGB_BLUE)); + //OCIO_CHECK_ASSERT(*rgbCurveLog->getCurve(OCIO::RGB_RED) == + // *rgbCurveLog->getCurve(OCIO::RGB_MASTER)); + //OCIO_CHECK_EQUAL(3, rgbCurveLog->getCurve(OCIO::RGB_RED)->getNumControlPoints()); + //OCIO_CHECK_EQUAL(0.f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(0).m_x); + //OCIO_CHECK_EQUAL(0.f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(0).m_y); + //OCIO_CHECK_EQUAL(0.5f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(1).m_x); + //OCIO_CHECK_EQUAL(0.5f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(1).m_y); + //OCIO_CHECK_EQUAL(1.f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(2).m_x); + //OCIO_CHECK_EQUAL(1.f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(2).m_y); +// + //OCIO_CHECK_ASSERT(*rgbCurveLin->getCurve(OCIO::RGB_RED) == + // *rgbCurveLin->getCurve(OCIO::RGB_GREEN)); + //OCIO_CHECK_ASSERT(*rgbCurveLin->getCurve(OCIO::RGB_RED) == + // *rgbCurveLin->getCurve(OCIO::RGB_BLUE)); + //OCIO_CHECK_ASSERT(*rgbCurveLin->getCurve(OCIO::RGB_RED) == + // *rgbCurveLin->getCurve(OCIO::RGB_MASTER)); + //OCIO_CHECK_EQUAL(3, rgbCurveLin->getCurve(OCIO::RGB_RED)->getNumControlPoints()); + //OCIO_CHECK_EQUAL(-7.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(0).m_x); + //OCIO_CHECK_EQUAL(-7.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(0).m_y); + //OCIO_CHECK_EQUAL(0.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(1).m_x); + //OCIO_CHECK_EQUAL(0.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(1).m_y); + //OCIO_CHECK_EQUAL(7.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(2).m_x); + //OCIO_CHECK_EQUAL(7.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(2).m_y); +// + //auto rgbCurveLinCopy = OCIO::GradingRGBCurve::Create(rgbCurveLin); + //OCIO_REQUIRE_ASSERT(rgbCurveLinCopy); + //OCIO_CHECK_ASSERT(*rgbCurveLin == *rgbCurveLinCopy); +// + //rgbCurveLinCopy = rgbCurveLin->createEditableCopy(); + //OCIO_REQUIRE_ASSERT(rgbCurveLinCopy); + //OCIO_CHECK_ASSERT(*rgbCurveLin == *rgbCurveLinCopy); +// + //std::ostringstream oss; + //oss << *rgbCurveLin; + //OCIO_CHECK_EQUAL(std::string("]>, " + // "green=]>, " + // "blue=]>, " + // "master=]>>"), + // oss.str()); +} + +OCIO_ADD_TEST(HueCurve, curves) +{ + //auto curves = OCIO::GradingRGBCurve::Create(OCIO::GRADING_VIDEO); + //OCIO_CHECK_ASSERT(curves->isIdentity()); + //// Use non const curve accessor to modify one of the spline of the curves. + //OCIO::GradingBSplineCurveRcPtr spline = curves->getCurve(OCIO::RGB_GREEN); + //spline->setNumControlPoints(4); + //spline->getControlPoint(3).m_x = 1.1f; + //spline->getControlPoint(3).m_y = 2.f; + //OCIO_CHECK_ASSERT(!curves->isIdentity()); + //spline->getControlPoint(3).m_x = 2.f; + //OCIO_CHECK_ASSERT(curves->isIdentity()); + //OCIO_CHECK_EQUAL(curves->getCurve(OCIO::RGB_GREEN)->getNumControlPoints(), 4); +// + //// Changing the pointer does not change the curves. + //auto newSpline = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 1.f,2.f } }); + //spline = newSpline; + //OCIO_CHECK_EQUAL(curves->getCurve(OCIO::RGB_GREEN)->getNumControlPoints(), 4); +} + +OCIO_ADD_TEST(HueCurve, max_ctrl_pnts) +{ + //auto curveR = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, + // { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); +// + //auto curveG = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, + // { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); +// + //auto curveB = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, + // { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); +// + //auto curveM = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, + // { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); +// + //auto rgbCurve = OCIO::GradingRGBCurve::Create(curveR, curveG, curveB, curveM); + //OCIO_REQUIRE_ASSERT(rgbCurve); +// + //OCIO::DynamicPropertyGradingRGBCurveImplRcPtr res; + //OCIO_CHECK_THROW_WHAT(res = std::make_shared(rgbCurve, false), + // OCIO::Exception, "RGB curve: maximum number of control points reached"); +} diff --git a/tests/cpu/transforms/HueCurveTransform_tests.cpp b/tests/cpu/transforms/HueCurveTransform_tests.cpp new file mode 100644 index 0000000000..9d2212b465 --- /dev/null +++ b/tests/cpu/transforms/HueCurveTransform_tests.cpp @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#include "ops/gradingrgbcurve/GradingBSplineCurve.h" +#include "transforms/HueCurveTransform.cpp" + +#include "testutils/UnitTest.h" +#include "UnitTestLogUtils.h" + +namespace OCIO = OCIO_NAMESPACE; + +OCIO_ADD_TEST(HueCurveTransform, basic) +{ + // Create transform and validate default values for all styles. + + auto gctLin = OCIO::HueCurveTransform::Create(OCIO::GRADING_LOG); + //OCIO_CHECK_EQUAL(gctLin->getStyle(), OCIO::GRADING_LIN); + OCIO_CHECK_EQUAL(gctLin->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); + gctLin->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + OCIO_CHECK_EQUAL(gctLin->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); + //OCIO_CHECK_ASSERT(!gctLin->getBypassLinToLog()); + //OCIO_CHECK_ASSERT(!gctLin->isDynamic()); + //auto red = gctLin->getValue()->getCurve(OCIO::RGB_RED); + //OCIO_CHECK_EQUAL(red->getNumControlPoints(), 3); + //OCIO_CHECK_EQUAL(red->getControlPoint(0), OCIO::GradingControlPoint(-7.f, -7.f)); + //OCIO_CHECK_EQUAL(red->getControlPoint(1), OCIO::GradingControlPoint(0.f, 0.f)); + //OCIO_CHECK_EQUAL(red->getControlPoint(2), OCIO::GradingControlPoint(7.f, 7.f)); + //OCIO_CHECK_EQUAL(*gctLin->getValue()->getCurve(OCIO::RGB_GREEN), *red); + //OCIO_CHECK_EQUAL(*gctLin->getValue()->getCurve(OCIO::RGB_BLUE), *red); + //OCIO_CHECK_EQUAL(*gctLin->getValue()->getCurve(OCIO::RGB_MASTER), *red); + //OCIO_CHECK_ASSERT(gctLin.get()); + //OCIO_CHECK_NO_THROW(gctLin->validate()); + + //auto gctLog = OCIO::GradingRGBCurveTransform::Create(OCIO::GRADING_LOG); + //OCIO_CHECK_EQUAL(gctLog->getStyle(), OCIO::GRADING_LOG); + //OCIO_CHECK_EQUAL(gctLog->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); + //OCIO_CHECK_ASSERT(!gctLog->getBypassLinToLog()); + //OCIO_CHECK_ASSERT(!gctLog->isDynamic()); + //red = gctLog->getValue()->getCurve(OCIO::RGB_RED); + //OCIO_CHECK_EQUAL(red->getNumControlPoints(), 3); + //OCIO_CHECK_EQUAL(red->getControlPoint(0), OCIO::GradingControlPoint(0.f, 0.f)); + //OCIO_CHECK_EQUAL(red->getControlPoint(1), OCIO::GradingControlPoint(0.5f, 0.5f)); + //OCIO_CHECK_EQUAL(red->getControlPoint(2), OCIO::GradingControlPoint(1.f, 1.f)); + //OCIO_CHECK_EQUAL(*gctLog->getValue()->getCurve(OCIO::RGB_GREEN), *red); + //OCIO_CHECK_EQUAL(*gctLog->getValue()->getCurve(OCIO::RGB_BLUE), *red); + //OCIO_CHECK_EQUAL(*gctLog->getValue()->getCurve(OCIO::RGB_MASTER), *red); + //OCIO_CHECK_NO_THROW(gctLog->validate()); +// + //auto gctVid = OCIO::GradingRGBCurveTransform::Create(OCIO::GRADING_VIDEO); + //OCIO_CHECK_EQUAL(gctVid->getStyle(), OCIO::GRADING_VIDEO); + //OCIO_CHECK_EQUAL(gctVid->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); + //OCIO_CHECK_ASSERT(!gctVid->getBypassLinToLog()); + //OCIO_CHECK_ASSERT(!gctVid->isDynamic()); + //red = gctVid->getValue()->getCurve(OCIO::RGB_RED); + //OCIO_CHECK_EQUAL(red->getNumControlPoints(), 3); + //OCIO_CHECK_EQUAL(red->getControlPoint(0), OCIO::GradingControlPoint(0.f, 0.f)); + //OCIO_CHECK_EQUAL(red->getControlPoint(1), OCIO::GradingControlPoint(0.5f, 0.5f)); + //OCIO_CHECK_EQUAL(red->getControlPoint(2), OCIO::GradingControlPoint(1.f, 1.f)); + //OCIO_CHECK_EQUAL(*gctVid->getValue()->getCurve(OCIO::RGB_GREEN), *red); + //OCIO_CHECK_EQUAL(*gctVid->getValue()->getCurve(OCIO::RGB_BLUE), *red); + //OCIO_CHECK_EQUAL(*gctVid->getValue()->getCurve(OCIO::RGB_MASTER), *red); + //OCIO_CHECK_NO_THROW(gctVid->validate()); +// + //// Change values. + //auto t = gctVid->createEditableCopy(); + //auto gct = OCIO_DYNAMIC_POINTER_CAST(t); + //gct->setStyle(OCIO::GRADING_LIN); + //OCIO_CHECK_EQUAL(gct->getStyle(), OCIO::GRADING_LIN); + //gct->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + //OCIO_CHECK_EQUAL(gct->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); + //gct->setBypassLinToLog(true); + //OCIO_CHECK_ASSERT(gct->getBypassLinToLog()); + //gct->makeDynamic(); + //OCIO_CHECK_ASSERT(gct->isDynamic()); + //gct->setValue(gctLin->getValue()); + //red = gct->getValue()->getCurve(OCIO::RGB_RED); + //OCIO_CHECK_EQUAL(red->getControlPoint(0), OCIO::GradingControlPoint(-7.f, -7.f)); + //OCIO_CHECK_NO_THROW(gct->validate()); +// + //// Access out of range point. + //OCIO_CHECK_THROW_WHAT(red->getControlPoint(4), OCIO::Exception, + // "There are '3' control points. '4' is invalid."); +// + //// X has to be increasing. + //auto invalidCurve = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.5f, 0.2f }, + // { 0.2f, 0.7f }, { 1.0f, 1.0f } }); + //auto newCurve = OCIO::GradingRGBCurve::Create(red, red, invalidCurve, red); + //OCIO_CHECK_THROW_WHAT(gct->setValue(newCurve), OCIO::Exception, + // "has a x coordinate '0.2' that is less from previous control " + // "point x cooordinate '0.5'."); +// + //// Check slopes. + //gct->setSlope(OCIO::RGB_BLUE, 2, 0.9f); + //OCIO_CHECK_NO_THROW(gct->validate()); + //OCIO_CHECK_EQUAL(gct->getSlope(OCIO::RGB_BLUE, 2), 0.9f); + //OCIO_CHECK_THROW_WHAT(gct->setSlope(OCIO::RGB_BLUE, 4, 2.f), OCIO::Exception, + // "There are '3' control points. '4' is invalid."); + //OCIO_CHECK_ASSERT(gct->slopesAreDefault(OCIO::RGB_GREEN)); + //OCIO_CHECK_ASSERT(!gct->slopesAreDefault(OCIO::RGB_BLUE)); +} + +OCIO_ADD_TEST(HueCurveTransform, processor_several_transforms) +{ + //OCIO::ConfigRcPtr config = OCIO::Config::Create(); + //const float srcPixel[3] = { 0.2f, 0.3f, 0.4f }; +// + //auto c1 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.2f, 0.2f }, + // { 0.5f, 0.7f }, { 1.0f, 1.0f } }); + //auto c2 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.5f }, { 0.3f, 0.7f }, + // { 0.5f, 1.1f }, { 1.0f, 1.5f } }); + //auto c3 = OCIO::GradingBSplineCurve::Create({ { 0.0f, -0.5f }, { 0.2f, -0.4f }, + // { 0.3f, 0.1f }, { 0.5f, 0.4f }, + // { 0.7f, 0.9f }, { 1.0f, 1.1f } }); + //auto c4 = OCIO::GradingBSplineCurve::Create({ { -1.0f, 0.0f }, { 0.2f, 0.2f }, + // { 0.8f, 0.8f }, { 2.0f, 1.0f } }); + //auto c5 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 1.0f, 1.0f } }); + + + //auto rgbCurveA = OCIO::GradingRGBCurve::Create(c1, c2, c3, c5); + + //auto gcta = OCIO::HueCurveTransform::Create(); + //OCIO_CHECK_ASSERT(gcta.get()); + //OCIO_CHECK_NO_THROW(gcta->validate()); + //gcta->setValue(rgbCurveA); +// + //// Will hold results for rgbCurveA. + //float pixel_a[3] = { srcPixel[0], srcPixel[1], srcPixel[2] }; + //// Will hold results for rgbCurveA applied twice. + //float pixel_aa[3]; + //{ + // OCIO::ConstProcessorRcPtr processor = config->getProcessor(gcta); + // OCIO::ConstCPUProcessorRcPtr cpuProcessor = processor->getDefaultCPUProcessor(); + // cpuProcessor->applyRGB(pixel_a); +// + // pixel_aa[0] = pixel_a[0]; + // pixel_aa[1] = pixel_a[1]; + // pixel_aa[2] = pixel_a[2]; + // cpuProcessor->applyRGB(pixel_aa); + //} +// + //auto rgbCurveB = OCIO::GradingRGBCurve::Create(c4, c1, c2, c5); + //auto gctb = OCIO::GradingRGBCurveTransform::Create(OCIO::GRADING_LOG); + //gctb->setValue(rgbCurveB); +// + //// Will hold results for rgbCurveB. + //float pixel_b[3] = { srcPixel[0], srcPixel[1], srcPixel[2] }; + //// Will hold results for rgbCurveB applied twice. + //float pixel_bb[3]; + //// Will hold results for rgbCurveA applied then rgbCurveB applied. + //float pixel_ab[3] = { pixel_a[0], pixel_a[1], pixel_a[2] }; + //{ + // OCIO::ConstProcessorRcPtr processor = config->getProcessor(gctb); + // OCIO::ConstCPUProcessorRcPtr cpuProcessor = processor->getDefaultCPUProcessor(); + // cpuProcessor->applyRGB(pixel_b); +// + // pixel_bb[0] = pixel_b[0]; + // pixel_bb[1] = pixel_b[1]; + // pixel_bb[2] = pixel_b[2]; +// + // cpuProcessor->applyRGB(pixel_bb); + // cpuProcessor->applyRGB(pixel_ab); + //} +// + //// Make second transform dynamic. + //gctb->makeDynamic(); + //const float error = 1e-6f; +// + //// + //// Test with two grading rgb curve transforms where only the second one is dynamic. + //// + //OCIO::GroupTransformRcPtr grp1 = OCIO::GroupTransform::Create(); + //gctb->setValue(rgbCurveA); + //grp1->appendTransform(gcta); // gpta values are rgbCurveA + //grp1->appendTransform(gctb); // gptb values are rgbCurveA +// + //{ + // OCIO::ConstProcessorRcPtr processor = config->getProcessor(grp1); + // OCIO::ConstCPUProcessorRcPtr cpuProcessor = processor->getDefaultCPUProcessor(); +// + // // Second transform is dynamic. Value is still rgbCurveA. + // OCIO::DynamicPropertyRcPtr dp; + // OCIO_CHECK_NO_THROW(dp = cpuProcessor->getDynamicProperty(OCIO::DYNAMIC_PROPERTY_GRADING_RGBCURVE)); + // auto dpVal = OCIO::DynamicPropertyValue::AsGradingRGBCurve(dp); + // OCIO_REQUIRE_ASSERT(dpVal); +// + // float pixel[3] = { srcPixel[0], srcPixel[1], srcPixel[2] }; +// + // // Apply rgbCurveA then rgbCurveA. + // cpuProcessor->applyRGB(pixel); +// + // OCIO_CHECK_CLOSE(pixel[0], pixel_aa[0], error); + // OCIO_CHECK_CLOSE(pixel[1], pixel_aa[1], error); + // OCIO_CHECK_CLOSE(pixel[2], pixel_aa[2], error); +// + // // Change the 2nd values. + // dpVal->setValue(rgbCurveB); + // pixel[0] = srcPixel[0]; + // pixel[1] = srcPixel[1]; + // pixel[2] = srcPixel[2]; +// + // // Apply rgbCurveA then rgbCurveB. + // cpuProcessor->applyRGB(pixel); +// + // OCIO_CHECK_CLOSE(pixel[0], pixel_ab[0], error); + // OCIO_CHECK_CLOSE(pixel[1], pixel_ab[1], error); + // OCIO_CHECK_CLOSE(pixel[2], pixel_ab[2], error); + //} +// + //// + //// Test two grading rgb curve transforms can't be both dynamic. + //// +// + //// Make first dynamic (second already is). + //gcta->makeDynamic(); +// + //OCIO::GroupTransformRcPtr grp2 = OCIO::GroupTransform::Create(); + //grp2->appendTransform(gcta); + //grp2->appendTransform(gctb); +// + //{ + // OCIO::LogGuard log; + // OCIO::SetLoggingLevel(OCIO::LOGGING_LEVEL_WARNING); + // OCIO_CHECK_NO_THROW(config->getProcessor(grp2)); + // OCIO_CHECK_EQUAL(log.output(), "[OpenColorIO Warning]: Grading RGB curve dynamic property " + // "can only be there once.\n"); + //} +} + +OCIO_ADD_TEST(HueCurveTransform, serialization) +{ + // Test the serialization of the transform. + + //auto c1 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.2f, 0.2f }, + // { 0.5f, 0.7f }, { 1.0f, 1.0f } }); + //auto c2 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.5f }, { 0.3f, 0.7f }, + // { 0.5f, 1.1f }, { 1.0f, 1.5f } }); + //auto c3 = OCIO::GradingBSplineCurve::Create({ { 0.0f, -0.5f }, { 0.2f, -0.4f }, + // { 0.3f, 0.1f }, { 0.5f, 0.4f }, + // { 0.7f, 0.9f }, { 1.0f, 1.1f } }); + //auto c4 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 1.0f, 1.0f } }); +// +// + //auto data = OCIO::GradingRGBCurve::Create(c1, c2, c3, c4); + + //auto curve = OCIO::HueCurveTransform::Create(); + //OCIO_CHECK_ASSERT(curve.get()); + //OCIO_CHECK_NO_THROW(curve->validate()); + + //curve->setValue(data); + + //static constexpr char CURVE_STR[] + // = "]>, "\ + // "green=]>, "\ + // "blue=]>, "\ + // "master=]>>>"; +// + //{ + // std::ostringstream oss; + // oss << *curve; +// + // OCIO_CHECK_EQUAL(oss.str(), CURVE_STR); + //} +// + //OCIO::GroupTransformRcPtr grp = OCIO::GroupTransform::Create(); + //grp->appendTransform(OCIO::DynamicPtrCast(curve)); +// + //{ + // std::ostringstream oss; + // oss << *grp; +// + // std::string GROUP_STR("createEditableCopy(); + OCIO_CHECK_ASSERT(transformBypass.get()); + + OCIO_CHECK_NO_THROW(transform->validate()); + + //OCIO::ConstConfigRcPtr config = OCIO::Config::CreateRaw(); +// + //OCIO::ConstProcessorRcPtr proc = config->getProcessor(transform); + //OCIO::ConstGPUProcessorRcPtr gpu = proc->getOptimizedGPUProcessor(OCIO::OPTIMIZATION_NONE); +// + //OCIO::GpuShaderDescRcPtr shaderDesc = OCIO::GpuShaderDesc::CreateShaderDesc(); +// + //OCIO_CHECK_NO_THROW(gpu->extractGpuShaderInfo(shaderDesc)); +// + //static const std::string gpuStr("\n" + // "// Declaration of the OCIO shader function\n" + // "\n" + // "vec4 OCIOMain(vec4 inPixel)\n" + // "{\n" + // " vec4 outColor = inPixel;\n" + // "\n" + // " return outColor;\n" + // "}\n"); +// + //OCIO_CHECK_EQUAL(gpuStr, std::string(shaderDesc->getShaderText())); +} diff --git a/tests/gpu/CMakeLists.txt b/tests/gpu/CMakeLists.txt index 2c56f2948b..5181c18095 100644 --- a/tests/gpu/CMakeLists.txt +++ b/tests/gpu/CMakeLists.txt @@ -15,6 +15,7 @@ set(SOURCES GPUUnitTest.cpp GradingPrimaryOp_test.cpp GradingRGBCurveOp_test.cpp + HueCurveOp_test.cpp GradingToneOp_test.cpp LogOp_test.cpp Lut1DOp_test.cpp diff --git a/tests/gpu/HueCurveOp_test.cpp b/tests/gpu/HueCurveOp_test.cpp new file mode 100644 index 0000000000..534b4a92a1 --- /dev/null +++ b/tests/gpu/HueCurveOp_test.cpp @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#include + +#include "GPUUnitTest.h" + +namespace OCIO = OCIO_NAMESPACE; + +void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +{ + // TODO: Put back the HueCurve test once the CPU implementation is available.' + // GPU test compares the result of the GPU processor to the CPU processor. + + //auto c = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.785f, 0.231f }, + // { 0.809f, 0.631f },{ 0.948f, 0.704f }, + // { 1.0f, 1.0f } }); + //auto curve = OCIO::HueCurve::Create(c); + //auto hc = OCIO::HueCurveTransform::Create(OCIO::GRADING_LOG); + //if(!hc.get()) + //{ + // throw OCIO::Exception("Cannot create HueCurveTransform."); + //} + //hc->setValue(curve); + //hc->setDirection(dir); + //if (dynamic) + //{ + // hc->makeDynamic(); + //} + + //test.setProcessor(hc); + + //test.setErrorThreshold(2e-5f); + //test.setExpectedMinimalValue(1.0f); + //test.setRelativeComparison(true); + //test.setTestWideRange(true); + //test.setTestInfinity(true); + //test.setTestNaN(true); +} + +OCIO_ADD_GPU_TEST(HueCurve, style_log_fwd) +{ + GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_FORWARD, false); +} + +OCIO_ADD_GPU_TEST(HueCurve, style_log_fwd_dynamic) +{ + GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_FORWARD, true); +} + +OCIO_ADD_GPU_TEST(HueCurve, style_log_rev) +{ + GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, false); +} + +OCIO_ADD_GPU_TEST(HueCurve, style_log_rev_dynamic) +{ + GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, true); +} + +void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +{ + // TODO: Put back the HueCurve test once the CPU implementation is available.' + // GPU test compares the result of the GPU processor to the CPU processor. + + //auto c = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.785f, 0.231f }, + // { 0.809f, 0.631f },{ 0.948f, 0.704f }, + // { 1.0f, 1.0f } }); + //auto curve = OCIO::HueCurve::Create(c); + //auto hc = OCIO::HueCurveTransform::Create(OCIO::GRADING_LIN); + //if(!hc.get()) + //{ + // throw OCIO::Exception("Cannot create HueCurveTransform."); + //} + //hc->setValue(curve); + //hc->setDirection(dir); + //if (dynamic) + //{ + // hc->makeDynamic(); + //} + + //test.setProcessor(hc); + + //test.setErrorThreshold(2e-5f); + //test.setExpectedMinimalValue(1.0f); + //test.setRelativeComparison(true); + //test.setTestWideRange(true); + //test.setTestInfinity(true); + //test.setTestNaN(true); +} + +OCIO_ADD_GPU_TEST(HueCurve, style_lin_fwd) +{ + HueCurveLin(test, OCIO::TRANSFORM_DIR_FORWARD, false); +} + +OCIO_ADD_GPU_TEST(HueCurve, style_lin_fwd_dynamic) +{ + HueCurveLin(test, OCIO::TRANSFORM_DIR_FORWARD, true); +} + +OCIO_ADD_GPU_TEST(HueCurve, style_lin_rev) +{ + HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, false); +} + +OCIO_ADD_GPU_TEST(HueCurve, style_lin_rev_dynamic) +{ + HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, true); +} + +void HueSCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +{ + // TODO: Implement this HueCurve test once the CPU implementation is available.' + // GPU test compares the result of the GPU processor to the CPU processor. + + // Create an S-curve with 0 slope at each end. + //auto curve = OCIO::GradingBSplineCurve::Create({ + // {-5.26017743f, -4.f}, + // {-3.75502745f, -3.57868829f}, + // {-2.24987747f, -1.82131329f}, + // {-0.74472749f, 0.68124124f}, + // { 1.06145248f, 2.87457742f}, + // { 2.86763245f, 3.83406206f}, + // { 4.67381243f, 4.f} + // }); + //float slopes[] = { 0.f, 0.55982688f, 1.77532247f, 1.55f, 0.8787017f, 0.18374463f, 0.f }; + //for (size_t i = 0; i < 7; ++i) + //{ + // curve->setSlope( i, slopes[i] ); + //} + + //OCIO::ConstGradingBSplineCurveRcPtr m = curve; + //// Adjust scaling to ensure the test vector for the inverse hits the flat areas. + //auto scaling = OCIO::GradingBSplineCurve::Create({ { -5.f, 0.f }, { 5.f, 1.f } }); + //OCIO::ConstGradingBSplineCurveRcPtr z = scaling; + //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(m, m, m, z); + + //auto gc = OCIO::HueCurveTransform::Create(); + //gc->setValue(curves); + //gc->setDirection(dir); + //if (dynamic) + //{ + // gc->makeDynamic(); + //} + + //test.setProcessor(gc); + + //test.setErrorThreshold(1.5e-4f); + //test.setExpectedMinimalValue(1.0f); + //test.setRelativeComparison(true); + //test.setTestWideRange(true); + //test.setTestInfinity(false); + //test.setTestNaN(true); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_fwd) +{ + HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, false); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_fwd_dynamic) +{ + HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, true); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_rev) +{ + HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, false); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_rev_dynamic) +{ + HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, true); +} diff --git a/tests/osl/HueCurveOp_test.cpp b/tests/osl/HueCurveOp_test.cpp new file mode 100644 index 0000000000..bb5eb62b2f --- /dev/null +++ b/tests/osl/HueCurveOp_test.cpp @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#include + +#include "GPUUnitTest.h" + +namespace OCIO = OCIO_NAMESPACE; + +void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +{ + //auto r = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.785f, 0.231f }, + // { 0.809f, 0.631f },{ 0.948f, 0.704f }, + // { 1.0f, 1.0f } }); + //auto g = OCIO::GradingBSplineCurve::Create({ { 0.1f, 0.15f }, { 0.55f, 0.35f },{ 0.9f, 1.1f } }); + //auto b = OCIO::GradingBSplineCurve::Create({ { -6.f, -8.f }, { -2.f, -5.f }, + // { 2.f, 4.f }, { 5.f, 6.f } }); + //auto m = OCIO::GradingBSplineCurve::Create({ { -0.1f, 0.1f },{ 1.1f, 1.3f } }); + //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(r, g, b, m); + + //auto gc = OCIO::HueCurveTransform::Create(); + //gc->setValue(curves); + //if(!gc.get()) + //{ + // throw OCIO::Exception("Cannot create HueCurveTransform."); + //} + + //gc->setDirection(dir); + //if (dynamic) + //{ + // gc->makeDynamic(); + //} + + //test.setProcessor(gc); +// + //test.setErrorThreshold(2e-5f); + //test.setExpectedMinimalValue(1.0f); + //test.setRelativeComparison(true); + //test.setTestWideRange(true); + //test.setTestInfinity(true); + //test.setTestNaN(true); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, style_log_fwd) +{ + GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_FORWARD, false); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, style_log_fwd_dynamic) +{ + GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_FORWARD, true); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, style_log_rev) +{ + GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, false); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, style_log_rev_dynamic) +{ + GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, true); +} + +void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +{ + //auto r = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f },{ 0.785f, 0.231f }, + // { 0.809f, 0.631f },{ 0.948f, 0.704f }, + // { 1.0f, 1.0f } }); + //auto g = OCIO::GradingBSplineCurve::Create({ { 0.1f, 0.15f },{ 0.55f, 0.35f },{ 0.9f, 0.8f } }); + //auto b = OCIO::GradingBSplineCurve::Create({ { -6.f, -4.f },{ -2.f, -1.f }, + // { 2.f, 2.f },{ 5.f, 4.f } }); + //auto m = OCIO::GradingBSplineCurve::Create({ { -0.1f, 0.1f },{ 1.1f, 0.9f } }); + //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(r, g, b, m); + + //auto gc = OCIO::HueCurveTransform::Create(); + //gc->setValue(curves); + //gc->setDirection(dir); + //if (dynamic) + //{ + // gc->makeDynamic(); + //} + + //test.setProcessor(gc); +// + //test.setErrorThreshold(1.5e-4f); + //test.setExpectedMinimalValue(1.0f); + //test.setRelativeComparison(true); + //test.setTestWideRange(true); + //test.setTestInfinity(false); + //test.setTestNaN(true); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, style_lin_fwd) +{ + HueCurveLin(test, OCIO::TRANSFORM_DIR_FORWARD, false); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, style_lin_fwd_dynamic) +{ + HueCurveLin(test, OCIO::TRANSFORM_DIR_FORWARD, true); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, style_lin_rev) +{ + HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, false); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, style_lin_rev_dynamic) +{ + HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, true); +} + +void HueSCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +{ + // Create an S-curve with 0 slope at each end. + //auto curve = OCIO::GradingBSplineCurve::Create({ + // {-5.26017743f, -4.f}, + // {-3.75502745f, -3.57868829f}, + // {-2.24987747f, -1.82131329f}, + // {-0.74472749f, 0.68124124f}, + // { 1.06145248f, 2.87457742f}, + // { 2.86763245f, 3.83406206f}, + // { 4.67381243f, 4.f} + // }); + //float slopes[] = { 0.f, 0.55982688f, 1.77532247f, 1.55f, 0.8787017f, 0.18374463f, 0.f }; + //for (size_t i = 0; i < 7; ++i) + //{ + // curve->setSlope( i, slopes[i] ); + //} +// + //OCIO::ConstGradingBSplineCurveRcPtr m = curve; + //// Adjust scaling to ensure the test vector for the inverse hits the flat areas. + //auto scaling = OCIO::GradingBSplineCurve::Create({ { -5.f, 0.f }, { 5.f, 1.f } }); + //OCIO::ConstGradingBSplineCurveRcPtr z = scaling; + //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(m, m, m, z); + + //auto gc = OCIO::HueCurveTransform::Create(); + //if(!gc.get()) + //{ + // throw OCIO::Exception("Cannot create HueCurveTransform."); + //} + //gc->setValue(curves); + //gc->setDirection(dir); + //if (dynamic) + //{ + // gc->makeDynamic(); + //} + + //test.setProcessor(gc); +// + //test.setErrorThreshold(1.5e-4f); + //test.setExpectedMinimalValue(1.0f); + //test.setRelativeComparison(true); + //test.setTestWideRange(true); + //test.setTestInfinity(false); + //test.setTestNaN(true); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_fwd) +{ + HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, false); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_fwd_dynamic) +{ + HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, true); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_rev) +{ + HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, false); +} + +OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_rev_dynamic) +{ + HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, true); +} From 225e064f39b1b8ff17c2979c618be1b0bcbf6ec2 Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Fri, 7 Jun 2024 10:53:00 -0400 Subject: [PATCH 02/30] Review Fixes P1 (cherry picked from commit 5b9ff85439070c5ac1bf2be36d28971d82efb204) Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTransforms.h | 52 ++++++++++++------- include/OpenColorIO/OpenColorTypes.h | 10 ++-- src/OpenColorIO/CMakeLists.txt | 8 +-- src/OpenColorIO/DynamicProperty.cpp | 10 ++-- src/OpenColorIO/DynamicProperty.h | 8 +-- src/OpenColorIO/Op.cpp | 4 +- src/OpenColorIO/Transform.cpp | 2 +- .../fileformats/ctf/CTFReaderUtils.h | 2 +- .../fileformats/ctf/CTFTransform.cpp | 6 +-- .../ops/fixedfunction/FixedFunctionOpGPU.cpp | 38 +++++++------- .../GradingHueCurve.cpp} | 48 ++++++++--------- .../GradingHueCurve.h} | 8 +-- .../GradingHueCurveOp.cpp} | 4 +- .../GradingHueCurveOp.h} | 2 +- .../GradingHueCurveOpData.cpp} | 16 +++--- .../GradingHueCurveOpData.h} | 6 +-- .../GradingHueCurveOpGPU.cpp} | 30 +++++------ .../GradingHueCurveOpGPU.h} | 2 +- .../gradingrgbcurve/GradingBSplineCurve.cpp | 30 +++++++++++ .../ops/gradingrgbcurve/GradingBSplineCurve.h | 6 ++- .../ops/gradingrgbcurve/GradingRGBCurveOp.cpp | 1 + .../transforms/HueCurveTransform.cpp | 27 ++-------- .../transforms/HueCurveTransform.h | 8 +-- tests/cpu/CMakeLists.txt | 2 +- .../gradingrgbcurve/HueCurveOpData_tests.cpp | 2 +- .../ops/gradingrgbcurve/HueCurveOp_tests.cpp | 2 +- .../ops/gradingrgbcurve/HueCurve_tests.cpp | 8 +-- tests/gpu/HueCurveOp_test.cpp | 26 +++++----- 28 files changed, 197 insertions(+), 171 deletions(-) rename src/OpenColorIO/ops/{gradingrgbcurve/HueCurve.cpp => gradinghuecurve/GradingHueCurve.cpp} (84%) rename src/OpenColorIO/ops/{gradingrgbcurve/HueCurve.h => gradinghuecurve/GradingHueCurve.h} (88%) rename src/OpenColorIO/ops/{gradingrgbcurve/HueCurveOp.cpp => gradinghuecurve/GradingHueCurveOp.cpp} (98%) rename src/OpenColorIO/ops/{gradingrgbcurve/HueCurveOp.h => gradinghuecurve/GradingHueCurveOp.h} (92%) rename src/OpenColorIO/ops/{gradingrgbcurve/HueCurveOpData.cpp => gradinghuecurve/GradingHueCurveOpData.cpp} (90%) rename src/OpenColorIO/ops/{gradingrgbcurve/HueCurveOpData.h => gradinghuecurve/GradingHueCurveOpData.h} (90%) rename src/OpenColorIO/ops/{gradingrgbcurve/HueCurveOpGPU.cpp => gradinghuecurve/GradingHueCurveOpGPU.cpp} (94%) rename src/OpenColorIO/ops/{gradingrgbcurve/HueCurveOpGPU.h => gradinghuecurve/GradingHueCurveOpGPU.h} (89%) diff --git a/include/OpenColorIO/OpenColorTransforms.h b/include/OpenColorIO/OpenColorTransforms.h index a9df79e6a4..3d9c0c5fdc 100644 --- a/include/OpenColorIO/OpenColorTransforms.h +++ b/include/OpenColorIO/OpenColorTransforms.h @@ -512,9 +512,11 @@ class OCIOEXPORT GradingBSplineCurve { public: /// Create a BSpline curve with a specified number of control points. - static GradingBSplineCurveRcPtr Create(size_t size, BSplineCurveType curveType = B_SPLINE); + static GradingBSplineCurveRcPtr Create(size_t size); + static GradingBSplineCurveRcPtr Create(size_t size, BSplineCurveType curveType); /// Create a BSpline curve with a list of control points. - static GradingBSplineCurveRcPtr Create(std::initializer_list values, BSplineCurveType curveType = B_SPLINE); + static GradingBSplineCurveRcPtr Create(std::initializer_list values); + static GradingBSplineCurveRcPtr Create(std::initializer_list values, BSplineCurveType curveType); virtual GradingBSplineCurveRcPtr createEditableCopy() const = 0; virtual size_t getNumControlPoints() const noexcept = 0; @@ -573,33 +575,45 @@ extern OCIOEXPORT bool operator==(const GradingRGBCurve & lhs, const GradingRGBC extern OCIOEXPORT bool operator!=(const GradingRGBCurve & lhs, const GradingRGBCurve & rhs); extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingRGBCurve &); +struct OCIOEXPORT GradingHueCurves +{ + ConstGradingBSplineCurveRcPtr hueHue; + ConstGradingBSplineCurveRcPtr hueSat; + ConstGradingBSplineCurveRcPtr hueLum; + ConstGradingBSplineCurveRcPtr lumSat; + ConstGradingBSplineCurveRcPtr satSat; + ConstGradingBSplineCurveRcPtr lumLum; + ConstGradingBSplineCurveRcPtr satLum; + ConstGradingBSplineCurveRcPtr hueFx; +}; + /** * A set of HUE/SAT/LUM curves. It is used by HueCurveTransform and can be used as * a dynamic property (see \ref DynamicPropertyHueCurve). */ -class OCIOEXPORT HueCurve +class OCIOEXPORT GradingHueCurve { public: - static HueCurveRcPtr Create(GradingStyle style); - static HueCurveRcPtr Create(const ConstHueCurveRcPtr & rhs); - static HueCurveRcPtr Create(const std::array & curves); + static GradingHueCurveRcPtr Create(GradingStyle style); + static GradingHueCurveRcPtr Create(const ConstGradingHueCurveRcPtr & rhs); + static GradingHueCurveRcPtr Create(const GradingHueCurves & curves); - virtual HueCurveRcPtr createEditableCopy() const = 0; + virtual GradingHueCurveRcPtr createEditableCopy() const = 0; virtual void validate() const = 0; virtual bool isIdentity() const = 0; virtual ConstGradingBSplineCurveRcPtr getCurve(HueCurveType c) const = 0; virtual GradingBSplineCurveRcPtr getCurve(HueCurveType c) = 0; /// Do not use (needed only for pybind11). - virtual ~HueCurve() = default; + virtual ~GradingHueCurve() = default; protected: - HueCurve() = default; + GradingHueCurve() = default; }; -extern OCIOEXPORT bool operator==(const HueCurve & lhs, const HueCurve & rhs); -extern OCIOEXPORT bool operator!=(const HueCurve & lhs, const HueCurve & rhs); -extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const HueCurve &); +extern OCIOEXPORT bool operator==(const GradingHueCurve & lhs, const GradingHueCurve & rhs); +extern OCIOEXPORT bool operator!=(const GradingHueCurve & lhs, const GradingHueCurve & rhs); +extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingHueCurve &); /** * Used by the grading tone transforms to hold the red, green, blue, master, start, @@ -765,7 +779,7 @@ extern OCIOEXPORT DynamicPropertyGradingPrimaryRcPtr AsGradingPrimary(DynamicPro */ extern OCIOEXPORT DynamicPropertyGradingRGBCurveRcPtr AsGradingRGBCurve(DynamicPropertyRcPtr & prop); /** - * Get the property as DynamicPropertyHueCurveRcPtr to access the HueCurveRcPtr + * Get the property as DynamicPropertyHueCurveRcPtr to access the GradingHueCurveRcPtr * value. Will throw if property type is not DYNAMIC_PROPERTY_HUE_CURVE. */ extern OCIOEXPORT DynamicPropertyHueCurveRcPtr AsHueCurve(DynamicPropertyRcPtr & prop); @@ -825,12 +839,14 @@ class OCIOEXPORT DynamicPropertyGradingRGBCurve protected: DynamicPropertyGradingRGBCurve() = default; }; + +/// Interface used to access dynamic property ConstGradingHueCurveRcPtr value. class OCIOEXPORT DynamicPropertyHueCurve { public: - virtual const ConstHueCurveRcPtr & getValue() const = 0; + virtual const ConstGradingHueCurveRcPtr & getValue() const = 0; /// Will throw if value is not valid. - virtual void setValue(const ConstHueCurveRcPtr & value) = 0; + virtual void setValue(const ConstGradingHueCurveRcPtr & value) = 0; DynamicPropertyHueCurve(const DynamicPropertyHueCurve &) = delete; DynamicPropertyHueCurve & operator=(const DynamicPropertyHueCurve &) = delete; @@ -1257,7 +1273,7 @@ class OCIOEXPORT HueCurveTransform : public Transform /// Creates an instance of HueCurveTransform. static HueCurveTransformRcPtr Create(GradingStyle style); - TransformType getTransformType() const noexcept override { return TRANSFORM_TYPE_HUE_CURVE; } + TransformType getTransformType() const noexcept override { return TRANSFORM_TYPE_GRADING_HUE_CURVE; } // virtual const FormatMetadata & getFormatMetadata() const noexcept = 0; virtual FormatMetadata & getFormatMetadata() noexcept = 0; @@ -1270,10 +1286,10 @@ class OCIOEXPORT HueCurveTransform : public Transform ///// Will reset value to style's defaults if style is not the current style. virtual void setStyle(GradingStyle style) noexcept = 0; // - virtual const ConstHueCurveRcPtr getValue() const = 0; + virtual const ConstGradingHueCurveRcPtr getValue() const = 0; ///// Throws if value is not valid. //virtual - virtual void setValue(const ConstHueCurveRcPtr & values) = 0; + virtual void setValue(const ConstGradingHueCurveRcPtr & values) = 0; /** * It is possible to provide a desired slope value for each control point. The number of slopes is diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 4001b394b6..0149662889 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -114,9 +114,9 @@ class OCIOEXPORT GradingRGBCurve; typedef OCIO_SHARED_PTR ConstGradingRGBCurveRcPtr; typedef OCIO_SHARED_PTR GradingRGBCurveRcPtr; -class OCIOEXPORT HueCurve; -typedef OCIO_SHARED_PTR ConstHueCurveRcPtr; -typedef OCIO_SHARED_PTR HueCurveRcPtr; +class OCIOEXPORT GradingHueCurve; +typedef OCIO_SHARED_PTR ConstGradingHueCurveRcPtr; +typedef OCIO_SHARED_PTR GradingHueCurveRcPtr; class OCIOEXPORT ConfigIOProxy; typedef OCIO_SHARED_PTR ConstConfigIOProxyRcPtr; @@ -363,6 +363,7 @@ enum TransformType TRANSFORM_TYPE_EXPOSURE_CONTRAST, TRANSFORM_TYPE_FILE, TRANSFORM_TYPE_FIXED_FUNCTION, + TRANSFORM_TYPE_GRADING_HUE_CURVE, TRANSFORM_TYPE_GRADING_PRIMARY, TRANSFORM_TYPE_GRADING_RGB_CURVE, TRANSFORM_TYPE_GRADING_TONE, @@ -375,7 +376,6 @@ enum TransformType TRANSFORM_TYPE_LUT3D, TRANSFORM_TYPE_MATRIX, TRANSFORM_TYPE_RANGE, - TRANSFORM_TYPE_HUE_CURVE }; /** @@ -576,7 +576,7 @@ enum RGBCurveType RGB_NUM_CURVES }; -/// Types for HueCurve. +/// Types for GradingHueCurve. enum HueCurveType { HUE_HUE = 0, diff --git a/src/OpenColorIO/CMakeLists.txt b/src/OpenColorIO/CMakeLists.txt index 81fe096f5d..1c53cab1d3 100755 --- a/src/OpenColorIO/CMakeLists.txt +++ b/src/OpenColorIO/CMakeLists.txt @@ -104,11 +104,11 @@ set(SOURCES ops/gradingrgbcurve/GradingRGBCurveOpData.cpp ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp ops/gradingrgbcurve/GradingRGBCurveOp.cpp - ops/gradingrgbcurve/HueCurveOpData.cpp - ops/gradingrgbcurve/HueCurveOp.cpp - ops/gradingrgbcurve/HueCurveOpGPU.cpp + ops/gradinghuecurve/GradingHueCurveOpData.cpp + ops/gradinghuecurve/GradingHueCurveOp.cpp + ops/gradinghuecurve/GradingHueCurveOpGPU.cpp ops/gradingrgbcurve/GradingRGBCurve.cpp - ops/gradingrgbcurve/HueCurve.cpp + ops/gradinghuecurve/GradingHueCurve.cpp ops/gradingtone/GradingTone.cpp ops/gradingtone/GradingToneOpCPU.cpp ops/gradingtone/GradingToneOpData.cpp diff --git a/src/OpenColorIO/DynamicProperty.cpp b/src/OpenColorIO/DynamicProperty.cpp index c3cc6550d5..2ec1469fd7 100644 --- a/src/OpenColorIO/DynamicProperty.cpp +++ b/src/OpenColorIO/DynamicProperty.cpp @@ -8,7 +8,7 @@ #include "ops/gradingprimary/GradingPrimaryOpData.h" #include "ops/gradingrgbcurve/GradingRGBCurve.h" #include "ops/gradingtone/GradingToneOpData.h" -#include "ops/gradingrgbcurve/HueCurve.h" +#include "ops/gradinghuecurve/GradingHueCurve.h" namespace OCIO_NAMESPACE { @@ -290,20 +290,20 @@ DynamicPropertyGradingRGBCurveImplRcPtr DynamicPropertyGradingRGBCurveImpl::crea DynamicPropertyHueCurveImpl::DynamicPropertyHueCurveImpl( - const ConstHueCurveRcPtr & value, bool dynamic) + const ConstGradingHueCurveRcPtr & value, bool dynamic) : DynamicPropertyImpl(DYNAMIC_PROPERTY_HUE_CURVE, dynamic) { - m_hueCurve = HueCurve::Create(value); + m_hueCurve = GradingHueCurve::Create(value); // Convert control points from the UI into knots and coefficients for the apply. precompute(); } -const ConstHueCurveRcPtr & DynamicPropertyHueCurveImpl::getValue() const +const ConstGradingHueCurveRcPtr & DynamicPropertyHueCurveImpl::getValue() const { return m_hueCurve; } -void DynamicPropertyHueCurveImpl::setValue(const ConstHueCurveRcPtr & value) +void DynamicPropertyHueCurveImpl::setValue(const ConstGradingHueCurveRcPtr & value) { value->validate(); diff --git a/src/OpenColorIO/DynamicProperty.h b/src/OpenColorIO/DynamicProperty.h index a23a32466c..af48963d49 100644 --- a/src/OpenColorIO/DynamicProperty.h +++ b/src/OpenColorIO/DynamicProperty.h @@ -181,10 +181,10 @@ class DynamicPropertyHueCurveImpl : public DynamicPropertyImpl, { public: DynamicPropertyHueCurveImpl() = delete; - DynamicPropertyHueCurveImpl(const ConstHueCurveRcPtr & value, bool dynamic); + DynamicPropertyHueCurveImpl(const ConstGradingHueCurveRcPtr & value, bool dynamic); ~DynamicPropertyHueCurveImpl() = default; - const ConstHueCurveRcPtr & getValue() const override; - void setValue(const ConstHueCurveRcPtr & value) override; + const ConstGradingHueCurveRcPtr & getValue() const override; + void setValue(const ConstGradingHueCurveRcPtr & value) override; bool getLocalBypass() const; int getNumKnots() const; @@ -205,7 +205,7 @@ class DynamicPropertyHueCurveImpl : public DynamicPropertyImpl, private: void precompute(); - ConstHueCurveRcPtr m_hueCurve; + ConstGradingHueCurveRcPtr m_hueCurve; // Holds curve data as knots and coefs. There is 8 curve. GradingBSplineCurveImpl::KnotsCoefs m_knotsCoefs{ 8 }; diff --git a/src/OpenColorIO/Op.cpp b/src/OpenColorIO/Op.cpp index e761d6ad3f..191b8eaed6 100755 --- a/src/OpenColorIO/Op.cpp +++ b/src/OpenColorIO/Op.cpp @@ -17,7 +17,7 @@ #include "ops/gamma/GammaOp.h" #include "ops/gradingprimary/GradingPrimaryOp.h" #include "ops/gradingrgbcurve/GradingRGBCurveOp.h" -#include "ops/gradingrgbcurve/HueCurveOp.h" +#include "ops/gradinghuecurve/GradingHueCurveOp.h" #include "ops/gradingtone/GradingToneOp.h" #include "ops/log/LogOp.h" #include "ops/lut1d/Lut1DOp.h" @@ -122,7 +122,7 @@ const char * GetTypeName(OpData::Type type) case OpData::GradingRGBCurveType: return "GradingRGBCurve"; case OpData::GradingHueCurveType : - return "HueCurve"; + return "GradingHueCurve"; case OpData::GradingToneType: return "GradingTone"; case OpData::LogType: diff --git a/src/OpenColorIO/Transform.cpp b/src/OpenColorIO/Transform.cpp index b006d2cc34..af3f0c9086 100755 --- a/src/OpenColorIO/Transform.cpp +++ b/src/OpenColorIO/Transform.cpp @@ -15,7 +15,7 @@ #include "ops/gamma/GammaOp.h" #include "ops/gradingprimary/GradingPrimaryOp.h" #include "ops/gradingrgbcurve/GradingRGBCurveOp.h" -#include "ops/gradingrgbcurve/HueCurveOp.h" +#include "ops/gradinghuecurve/GradingHueCurveOp.h" #include "ops/gradingtone/GradingToneOp.h" #include "ops/log/LogOp.h" #include "ops/lut1d/Lut1DOp.h" diff --git a/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h b/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h index b7fc5ba416..45c9163e00 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h +++ b/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h @@ -69,7 +69,7 @@ static constexpr char TAG_PRIMARY_SATURATION[] = "Saturation"; static constexpr char TAG_PROCESS_LIST[] = "ProcessList"; static constexpr char TAG_RANGE[] = "Range"; static constexpr char TAG_REFERENCE[] = "Reference"; -static constexpr char TAG_HUE_CURVE[] = "HueCurve"; +static constexpr char TAG_HUE_CURVE[] = "GradingHueCurve"; static constexpr char TAG_HUE_CURVE_HUE_HUE[] = "hue_hue"; static constexpr char TAG_HUE_CURVE_HUE_SAT[] = "hue_sat"; static constexpr char TAG_HUE_CURVE_HUE_LUM[] = "hue_lum"; diff --git a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp index 5ea20c92b8..87654bf392 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp @@ -16,8 +16,8 @@ #include "ops/gradingprimary/GradingPrimaryOpData.h" #include "ops/gradingrgbcurve/GradingRGBCurve.h" #include "ops/gradingrgbcurve/GradingRGBCurveOpData.h" -#include "ops/gradingrgbcurve/HueCurve.h" -#include "ops/gradingrgbcurve/HueCurveOpData.h" +#include "ops/gradinghuecurve/GradingHueCurve.h" +#include "ops/gradinghuecurve/GradingHueCurveOpData.h" #include "ops/gradingtone/GradingToneOpData.h" #include "ops/log/LogOpData.h" #include "ops/log/LogUtils.h" @@ -2837,7 +2837,7 @@ void TransformWriter::writeOps(const CTFVersion & version) const { if (m_isCLF) { - ThrowWriteOp("HueCurve"); + ThrowWriteOp("GradingHueCurve"); } auto hue = OCIO_DYNAMIC_POINTER_CAST(op); diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp index dcd103a1e0..7bbd742d70 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp @@ -1694,16 +1694,18 @@ void Add_RGB_TO_HSV(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, float min0) { + const std::string pxl(shaderCreator->getPixelName()); + ss.newLine() << ss.float3Decl("lumaWeights") << " = " << ss.float3Const(0.2126f, 0.7152f, 0.0722f) << ";"; ss.newLine() << ss.float3Decl("ones") << " = " << ss.float3Const(1.f, 1.f, 1.f) << ";"; - ss.newLine() << "float luma = dot(outColor.rgb, lumaWeights);"; - ss.newLine() << "float minRGB = min( outColor.x, min( outColor.y, outColor.z ) );"; - ss.newLine() << "float maxRGB = max( outColor.x, max( outColor.y, outColor.z ) );"; - ss.newLine() << ss.float3Decl("RGBm") << " = " << "outColor.rgb - luma;"; + ss.newLine() << "float luma = dot(" << pxl << ".rgb, lumaWeights);"; + ss.newLine() << "float minRGB = min( " << pxl << ".x, min( " << pxl << ".y, " << pxl << ".z ) );"; + ss.newLine() << "float maxRGB = max( " << pxl << ".x, max( " << pxl << ".y, " << pxl << ".z ) );"; + ss.newLine() << ss.float3Decl("RGBm") << " = " << pxl << ".rgb - luma;"; ss.newLine() << "float distRGB = dot( abs(RGBm), ones );"; - if (min0 > 0.) + if (min0 > 0.f) { - ss.newLine() << "float sumRGB = dot( outColor.rgb, ones );"; + ss.newLine() << "float sumRGB = dot( " << pxl << ".rgb, ones );"; ss.newLine() << "float sat_hi = distRGB / (0.15 + sumRGB);"; ss.newLine() << "float sat_lo = distRGB * 5.;"; ss.newLine() << "float alpha = clamp( (luma - 0.001) / (0.01 - 0.001), 0., 1.);"; @@ -1711,7 +1713,7 @@ void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "float sat = sat_lo + alpha * (sat_hi - sat_lo);"; ss.newLine() << "sat *= 1.4;"; } - else if (min0 < 0.) + else if (min0 < 0.f) { ss.newLine() << "float sat = distRGB * 4.;"; } @@ -1722,19 +1724,19 @@ void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "float hue = 0.0;"; ss.newLine() << "if (minRGB != maxRGB) {"; ss.newLine() << " float OneOverMaxMinusMin = 1.0 / (maxRGB - minRGB);"; - ss.newLine() << " if ( maxRGB == outColor.r ) hue = 1.0 + (outColor.g - outColor.b) * OneOverMaxMinusMin;"; - ss.newLine() << " else if ( maxRGB == outColor.g ) hue = 3.0 + (outColor.b - outColor.r) * OneOverMaxMinusMin;"; - ss.newLine() << " else hue = 5.0 + (outColor.r - outColor.g) * OneOverMaxMinusMin;"; + ss.newLine() << " if ( maxRGB == " << pxl << ".r ) hue = 1.0 + (" << pxl << ".g - " << pxl << ".b) * OneOverMaxMinusMin;"; + ss.newLine() << " else if ( maxRGB == " << pxl << ".g ) hue = 3.0 + (" << pxl << ".b - " << pxl << ".r) * OneOverMaxMinusMin;"; + ss.newLine() << " else hue = 5.0 + (" << pxl << ".r - " << pxl << ".g) * OneOverMaxMinusMin;"; ss.newLine() << "}"; - ss.newLine() << "outColor.r = hue * 1./6.; outColor.g = sat; outColor.b = luma;"; + ss.newLine() << "" << pxl << ".r = hue * 1./6.; " << pxl << ".g = sat; " << pxl << ".b = luma;"; } void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, float min0) { - //ss.setIndent(2); + const std::string pxl(shaderCreator->getPixelName()); - ss.newLine() << "float luma = outColor.z;"; - ss.newLine() << "float Hue = outColor.x - 1./6.;"; + ss.newLine() << "float luma = " << pxl << ".z;"; + ss.newLine() << "float Hue = " << pxl << ".x - 1./6.;"; ss.newLine() << "Hue = (luma < 0.) ? Hue + 0.5 : Hue;"; ss.newLine() << "Hue = ( Hue - floor( Hue ) ) * 6.0;"; ss.newLine() << "float R = abs(Hue - 3.0) - 1.0;"; @@ -1748,9 +1750,9 @@ void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "float currY = dot(RGB0, lumaWeights);"; ss.newLine() << "RGB0 *= luma / currY;"; - ss.newLine() << "float sat = outColor.y;"; + ss.newLine() << "float sat = " << pxl << ".y;"; ss.newLine() << "float distRGB = dot( abs(RGB0 - luma), ones );"; - if (min0 > 0.) + if (min0 > 0.f) { ss.newLine() << "float sumRGB = dot( RGB0, ones );"; ss.newLine() << "float k = 0.15;"; @@ -1769,7 +1771,7 @@ void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "sm = (sm >= 0.) ? sm : (2. * c) / (denom + discrim * 2.);"; ss.newLine() << "float gainS = (alpha == 1.) ? s1 : (alpha == 0.) ? s0 : sm;"; } - else if (min0 < 0.) + else if (min0 < 0.f) { ss.newLine() << "float gainS = sat / max(1e-10, distRGB * 4.);"; } @@ -1777,7 +1779,7 @@ void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f { ss.newLine() << "float gainS = sat / max(1e-10, distRGB * 1.25);"; } - ss.newLine() << "outColor.rgb = luma + gainS * (RGB0 - luma);"; + ss.newLine() << "" << pxl << ".rgb = luma + gainS * (RGB0 - luma);"; } void Add_RGB_TO_HSY_LOG(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurve.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp similarity index 84% rename from src/OpenColorIO/ops/gradingrgbcurve/HueCurve.cpp rename to src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp index cd945990fc..4d6bceadf7 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/HueCurve.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp @@ -6,7 +6,7 @@ #include -#include "ops/gradingrgbcurve/HueCurve.h" +#include "ops/gradinghuecurve/GradingHueCurve.h" namespace OCIO_NAMESPACE { @@ -90,23 +90,19 @@ HueCurveImpl::HueCurveImpl(GradingStyle style) } } -HueCurveImpl::HueCurveImpl(const std::array & curves) +HueCurveImpl::HueCurveImpl(const GradingHueCurves & curves) { - if (curves.size() != static_cast(HUE_NUM_CURVES)) - { - throw Exception("All curves have to be defined"); - } - - for (int c = 0; c < HUE_NUM_CURVES; ++c) - { - if(curves[c]) - { - m_curves[c] = curves[c]->createEditableCopy(); - } - } + m_curves[HUE_HUE] = curves.hueHue->createEditableCopy(); + m_curves[HUE_SAT] = curves.hueSat->createEditableCopy(); + m_curves[HUE_LUM] = curves.hueLum->createEditableCopy(); + m_curves[LUM_SAT] = curves.lumSat->createEditableCopy(); + m_curves[SAT_SAT] = curves.satSat->createEditableCopy(); + m_curves[LUM_LUM] = curves.lumLum->createEditableCopy(); + m_curves[SAT_LUM] = curves.satLum->createEditableCopy(); + m_curves[HUE_FX] = curves.hueFx->createEditableCopy(); } -HueCurveImpl::HueCurveImpl(const ConstHueCurveRcPtr & rhs) +HueCurveImpl::HueCurveImpl(const ConstGradingHueCurveRcPtr & rhs) { auto impl = dynamic_cast(rhs.get()); if (impl) @@ -118,7 +114,7 @@ HueCurveImpl::HueCurveImpl(const ConstHueCurveRcPtr & rhs) } } -HueCurveRcPtr HueCurveImpl::createEditableCopy() const +GradingHueCurveRcPtr HueCurveImpl::createEditableCopy() const { auto newCurve = std::make_shared(); for (int c = 0; c < HUE_NUM_CURVES; ++c) @@ -126,7 +122,7 @@ HueCurveRcPtr HueCurveImpl::createEditableCopy() const newCurve->m_curves[c] = m_curves[c]->createEditableCopy(); } - HueCurveRcPtr res = newCurve; + GradingHueCurveRcPtr res = newCurve; return res; } @@ -172,7 +168,7 @@ void HueCurveImpl::validate() const catch (Exception & e) { std::ostringstream oss; - oss << "HueCurve validation failed for curve: " << CurveType(c) << "' curve " + oss << "GradingHueCurve validation failed for curve: " << CurveType(c) << "' curve " << "with: " << e.what(); throw Exception(oss.str().c_str()); } @@ -201,28 +197,28 @@ GradingBSplineCurveRcPtr HueCurveImpl::getCurve(HueCurveType c) return m_curves[c]; } -HueCurveRcPtr HueCurve::Create(GradingStyle style) +GradingHueCurveRcPtr GradingHueCurve::Create(GradingStyle style) { auto newCurve = std::make_shared(style); - HueCurveRcPtr res = newCurve; + GradingHueCurveRcPtr res = newCurve; return res; } -HueCurveRcPtr HueCurve::Create(const ConstHueCurveRcPtr & rhs) +GradingHueCurveRcPtr GradingHueCurve::Create(const ConstGradingHueCurveRcPtr & rhs) { auto newCurve = std::make_shared(rhs); - HueCurveRcPtr res = newCurve; + GradingHueCurveRcPtr res = newCurve; return res; } -HueCurveRcPtr HueCurve::Create(const std::array & curves) +GradingHueCurveRcPtr GradingHueCurve::Create(const GradingHueCurves & curves) { auto newCurve = std::make_shared(curves); - HueCurveRcPtr res = newCurve; + GradingHueCurveRcPtr res = newCurve; return res; } -bool operator==(const HueCurve & lhs, const HueCurve & rhs) +bool operator==(const GradingHueCurve & lhs, const GradingHueCurve & rhs) { for (int c = 0; c < HUE_NUM_CURVES; ++c) @@ -235,7 +231,7 @@ bool operator==(const HueCurve & lhs, const HueCurve & rhs) return true; } -bool operator!=(const HueCurve & lhs, const HueCurve & rhs) +bool operator!=(const GradingHueCurve & lhs, const GradingHueCurve & rhs) { return !(lhs == rhs); } diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurve.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h similarity index 88% rename from src/OpenColorIO/ops/gradingrgbcurve/HueCurve.h rename to src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h index fed4defb89..37fe892308 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/HueCurve.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h @@ -16,15 +16,15 @@ namespace OCIO_NAMESPACE // Class to hold the RGB curve data that is used in the corresponding dynamic property and in // the CTF reader.. This allows moving some of the code from DynamicProperty to here. The // dynamic property is then used by the OpData, which is then used by the Op and Transform. -class HueCurveImpl : public HueCurve +class HueCurveImpl : public GradingHueCurve { public: HueCurveImpl(); HueCurveImpl(GradingStyle style); - HueCurveImpl(const std::array & curves ); - HueCurveImpl(const ConstHueCurveRcPtr & rhs); + HueCurveImpl(const GradingHueCurves & curves ); + HueCurveImpl(const ConstGradingHueCurveRcPtr & rhs); - HueCurveRcPtr createEditableCopy() const override; + GradingHueCurveRcPtr createEditableCopy() const override; void validate() const override; bool isIdentity() const override; diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp similarity index 98% rename from src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.cpp rename to src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp index 58e4380da4..64e12190a0 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp @@ -8,8 +8,8 @@ #include "GpuShaderUtils.h" //#include "ops/gradingrgbcurve/GradingRGBCurveOpCPU.h" -#include "ops/gradingrgbcurve/HueCurveOpGPU.h" -#include "ops/gradingrgbcurve/HueCurveOp.h" +#include "ops/gradinghuecurve/GradingHueCurveOpGPU.h" +#include "ops/gradinghuecurve/GradingHueCurveOp.h" #include "transforms/HueCurveTransform.h" namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.h similarity index 92% rename from src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.h rename to src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.h index d06380ac8f..4070cc6193 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOp.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.h @@ -11,7 +11,7 @@ #include #include "Op.h" -#include "ops/gradingrgbcurve/HueCurveOpData.h" +#include "ops/gradinghuecurve/GradingHueCurveOpData.h" namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp similarity index 90% rename from src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.cpp rename to src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp index 27b668450d..7edece3a64 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp @@ -5,8 +5,8 @@ #include -#include "ops/gradingrgbcurve/HueCurve.h" -#include "ops/gradingrgbcurve/HueCurveOpData.h" +#include "ops/gradinghuecurve/GradingHueCurve.h" +#include "ops/gradinghuecurve/GradingHueCurveOpData.h" #include "ops/range/RangeOpData.h" #include "Platform.h" @@ -22,7 +22,7 @@ HueCurveOpData::HueCurveOpData(GradingStyle style) : OpData() , m_style(style) { - ConstHueCurveRcPtr hueCurve = HueCurve::Create(style); + ConstGradingHueCurveRcPtr hueCurve = GradingHueCurve::Create(style); m_value = std::make_shared(hueCurve, false); } @@ -30,18 +30,18 @@ HueCurveOpData::HueCurveOpData(const HueCurveOpData & rhs) : OpData(rhs) , m_style(rhs.m_style) { - ConstHueCurveRcPtr hueCurve = HueCurve::Create(rhs.m_style); + ConstGradingHueCurveRcPtr hueCurve = GradingHueCurve::Create(rhs.m_style); m_value = std::make_shared(hueCurve, false); *this = rhs; } HueCurveOpData::HueCurveOpData(GradingStyle style, - const std::array & curves) + const GradingHueCurves & curves) : OpData() , m_style(style) { - ConstHueCurveRcPtr hueCurve = HueCurve::Create(curves); + ConstGradingHueCurveRcPtr hueCurve = GradingHueCurve::Create(curves); m_value = std::make_shared(hueCurve, false); } @@ -150,7 +150,7 @@ void HueCurveOpData::setStyle(GradingStyle style) noexcept { m_style = style; // Reset value to default when style is changing. - ConstHueCurveRcPtr reset = HueCurve::Create(style); + ConstGradingHueCurveRcPtr reset = GradingHueCurve::Create(style); m_value->setValue(reset); } } @@ -163,7 +163,7 @@ float HueCurveOpData::getSlope(HueCurveType c, size_t index) const void HueCurveOpData::setSlope(HueCurveType c, size_t index, float slope) { - HueCurveRcPtr hueCurve( m_value->getValue()->createEditableCopy() ); + GradingHueCurveRcPtr hueCurve( m_value->getValue()->createEditableCopy() ); GradingBSplineCurveRcPtr curve = hueCurve->getCurve(c); curve->setSlope(index, slope); m_value->setValue(hueCurve); diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h similarity index 90% rename from src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.h rename to src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h index 53d24e9890..1aa458ac9e 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpData.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h @@ -26,7 +26,7 @@ class HueCurveOpData : public OpData HueCurveOpData(GradingStyle style); HueCurveOpData(const HueCurveOpData & rhs); HueCurveOpData(GradingStyle style, - const std::array & curve); + const GradingHueCurves & curve); HueCurveOpData & operator=(const HueCurveOpData & rhs); virtual ~HueCurveOpData(); @@ -49,8 +49,8 @@ class HueCurveOpData : public OpData GradingStyle getStyle() const noexcept { return m_style; } void setStyle(GradingStyle style) noexcept; - const ConstHueCurveRcPtr getValue() const { return m_value->getValue(); } - void setValue(const ConstHueCurveRcPtr & values) { m_value->setValue(values); } + const ConstGradingHueCurveRcPtr getValue() const { return m_value->getValue(); } + void setValue(const ConstGradingHueCurveRcPtr & values) { m_value->setValue(values); } float getSlope(HueCurveType c, size_t index) const; void setSlope(HueCurveType c, size_t index, float slope); diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp similarity index 94% rename from src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.cpp rename to src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp index e1ef812c5d..957fbed755 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp @@ -5,7 +5,7 @@ #include #include "Logging.h" -#include "ops/gradingrgbcurve/HueCurveOpGPU.h" +#include "ops/gradinghuecurve/GradingHueCurveOpGPU.h" #include "ops/fixedfunction/FixedFunctionOpGPU.h" #include "ops/fixedfunction/FixedFunctionOpData.h" #include "utils/StringUtils.h" @@ -303,15 +303,15 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, const std::string pix(shaderCreator->getPixelName()); st.newLine() << ""; - st.newLine() << "float hueSatGain = max(0., " << props.m_eval << "(1, outColor.r, 1.));"; // HUE-SAT - st.newLine() << "float hueLumGain = max(0., " << props.m_eval << "(2, outColor.r, 1.));"; // HUE-LUM - st.newLine() << "outColor.r = " << props.m_eval << "(0, outColor.r, outColor.r);"; // HUE-HUE - st.newLine() << "outColor.g = max(0., " << props.m_eval << "(4, outColor.g, outColor.g));"; // SAT-SAT - st.newLine() << "float lumSatGain = max(0., " << props.m_eval << "(3, outColor.b, 1.));"; // LUM-SAT + st.newLine() << "float hueSatGain = max(0., " << props.m_eval << "(1, " << pix << ".r, 1.));"; // HUE-SAT + st.newLine() << "float hueLumGain = max(0., " << props.m_eval << "(2, " << pix << ".r, 1.));"; // HUE-LUM + st.newLine() << pix << ".r = " << props.m_eval << "(0, " << pix << ".r, " << pix << ".r);"; // HUE-HUE + st.newLine() << "" << pix << ".g = max(0., " << props.m_eval << "(4, " << pix << ".g, " << pix << ".g));"; // SAT-SAT + st.newLine() << "float lumSatGain = max(0., " << props.m_eval << "(3, " << pix << ".b, 1.));"; // LUM-SAT st.newLine() << "float satGain = lumSatGain * hueSatGain;"; - st.newLine() << "outColor.g = satGain * outColor.g;"; - st.newLine() << "float satLumGain = max(0., " << props.m_eval << "(6, outColor.g, 1.));"; // SAT-LUM - st.newLine() << "outColor.b = " << props.m_eval << "(5, outColor.b, outColor.b);"; // LUM-LUM + st.newLine() << "" << pix << ".g = satGain * " << pix << ".g;"; + st.newLine() << "float satLumGain = max(0., " << props.m_eval << "(6, " << pix << ".g, 1.));"; // SAT-LUM + st.newLine() << pix << ".b = " << props.m_eval << "(5, " << pix << ".b, " << pix << ".b);"; // LUM-LUM st.newLine() << ""; if (doLinToLog) @@ -322,17 +322,17 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, } st.newLine() << ""; - st.newLine() << "hueLumGain = 1. - (1. - hueLumGain) * min( 1., outColor.g );"; + st.newLine() << "hueLumGain = 1. - (1. - hueLumGain) * min( 1., " << pix << ".g );"; if (style == GRADING_LOG) // Use shift rather than scale for log mode. - st.newLine() << "outColor.b = outColor.b + (hueLumGain + satLumGain - 2.) * 0.1;"; + st.newLine() << pix << ".b = " << pix << ".b + (hueLumGain + satLumGain - 2.) * 0.1;"; else // Note this is applied in linear space, for linear style. - st.newLine() << "outColor.b = outColor.b * hueLumGain * satLumGain;"; + st.newLine() << pix << ".b = " << pix << ".b * hueLumGain * satLumGain;"; st.newLine() << ""; - st.newLine() << "outColor.r = outColor.r - floor( outColor.r );"; - st.newLine() << "outColor.r = outColor.r + " << props.m_eval << "(7, outColor.r, 0.);"; // HUE-FX + st.newLine() << pix << ".r = " << pix << ".r - floor( " << pix << ".r );"; + st.newLine() << pix << ".r = " << pix << ".r + " << props.m_eval << "(7, " << pix << ".r, 0.);"; // HUE-FX { FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; @@ -444,7 +444,7 @@ void GetHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, st.indent(); st.newLine() << ""; - st.newLine() << "// Add HueCurve '" + st.newLine() << "// Add GradingHueCurve '" << TransformDirectionToString(dir) << " processing"; st.newLine() << ""; st.newLine() << "{"; diff --git a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h similarity index 89% rename from src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.h rename to src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h index 148f1d1339..f56734948f 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/HueCurveOpGPU.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h @@ -7,7 +7,7 @@ #include #include "GpuShaderUtils.h" -#include "ops/gradingrgbcurve/HueCurveOpData.h" +#include "ops/gradinghuecurve/GradingHueCurveOpData.h" namespace OCIO_NAMESPACE { diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index f04ab82498..726374b625 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -14,6 +14,13 @@ namespace OCIO_NAMESPACE { +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size) +{ + auto newSpline = std::make_shared(size); + GradingBSplineCurveRcPtr res = newSpline; + return res; +} + GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size, BSplineCurveType curveType) { auto newSpline = std::make_shared(size, curveType); @@ -21,6 +28,19 @@ GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size, BSplineCurveTy return res; } +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values) +{ + auto newSpline = std::make_shared(values.size()); + size_t i = 0; + for (const auto & c : values) + { + newSpline->getControlPoint(i++) = c; + } + GradingBSplineCurveRcPtr res; + res = newSpline; + return res; +} + GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values, BSplineCurveType curveType) { auto newSpline = std::make_shared(values.size(), curveType); @@ -34,11 +54,21 @@ GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list & controlPoints) + : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_curveType(B_SPLINE) +{ +} + GradingBSplineCurveImpl::GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineCurveType curveType) : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_curveType(curveType) { diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h index 4de6d12e25..bb75e80cbf 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h @@ -17,8 +17,10 @@ class GpuShaderText; class GradingBSplineCurveImpl : public GradingBSplineCurve { public: - explicit GradingBSplineCurveImpl(size_t size, BSplineCurveType curveType = B_SPLINE); - GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineCurveType curveType = B_SPLINE); + explicit GradingBSplineCurveImpl(size_t size); + explicit GradingBSplineCurveImpl(size_t size, BSplineCurveType curveType); + GradingBSplineCurveImpl(const std::vector & controlPoints); + GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineCurveType curveType); ~GradingBSplineCurveImpl() = default; GradingBSplineCurveRcPtr createEditableCopy() const override; diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOp.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOp.cpp index 6722c02aa1..598ae28e67 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOp.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOp.cpp @@ -248,5 +248,6 @@ void BuildGradingRGBCurveOp(OpRcPtrVec & ops, CreateGradingRGBCurveOp(ops, curveData, dir); } + } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/transforms/HueCurveTransform.cpp b/src/OpenColorIO/transforms/HueCurveTransform.cpp index 496803c288..cea323b444 100644 --- a/src/OpenColorIO/transforms/HueCurveTransform.cpp +++ b/src/OpenColorIO/transforms/HueCurveTransform.cpp @@ -70,7 +70,6 @@ const FormatMetadata & HueCurveTransformImpl::getFormatMetadata() const noexcept return data().getFormatMetadata(); } - bool HueCurveTransformImpl::equals(const HueCurveTransform & other) const noexcept { if (this == &other) return true; @@ -87,13 +86,12 @@ void HueCurveTransformImpl::setStyle(GradingStyle style) noexcept data().setStyle(style); } -const ConstHueCurveRcPtr HueCurveTransformImpl::getValue() const +const ConstGradingHueCurveRcPtr HueCurveTransformImpl::getValue() const { return data().getValue(); } - -void HueCurveTransformImpl::setValue(const ConstHueCurveRcPtr & values) +void HueCurveTransformImpl::setValue(const ConstGradingHueCurveRcPtr & values) { data().setValue(values); } @@ -152,26 +150,7 @@ std::ostream& operator<< (std::ostream & os, const HueCurveTransform & t) noexce return os; } -// TODO: Duplicated from GradingRGB, should it be accessible here? -//std::ostream & operator<<(std::ostream & os, const GradingControlPoint & cp) -//{ -// os << ""; -// return os; -//} -// -//std::ostream & operator<<(std::ostream & os, const GradingBSplineCurve & bspline) -//{ -// os << ""; -// return os; -//} - -std::ostream & operator<<(std::ostream & os, const HueCurve & hueCurve) +std::ostream & operator<<(std::ostream & os, const GradingHueCurve & hueCurve) { os << "isIdentity()); @@ -107,7 +107,7 @@ OCIO_ADD_TEST(HueCurve, curves) //OCIO_CHECK_EQUAL(curves->getCurve(OCIO::RGB_GREEN)->getNumControlPoints(), 4); } -OCIO_ADD_TEST(HueCurve, max_ctrl_pnts) +OCIO_ADD_TEST(GradingHueCurve, max_ctrl_pnts) { //auto curveR = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, // { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); diff --git a/tests/gpu/HueCurveOp_test.cpp b/tests/gpu/HueCurveOp_test.cpp index 534b4a92a1..03aaf7a882 100644 --- a/tests/gpu/HueCurveOp_test.cpp +++ b/tests/gpu/HueCurveOp_test.cpp @@ -9,13 +9,13 @@ namespace OCIO = OCIO_NAMESPACE; void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) { - // TODO: Put back the HueCurve test once the CPU implementation is available.' + // TODO: Put back the GradingHueCurve test once the CPU implementation is available.' // GPU test compares the result of the GPU processor to the CPU processor. //auto c = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.785f, 0.231f }, // { 0.809f, 0.631f },{ 0.948f, 0.704f }, // { 1.0f, 1.0f } }); - //auto curve = OCIO::HueCurve::Create(c); + //auto curve = OCIO::GradingHueCurve::Create(c); //auto hc = OCIO::HueCurveTransform::Create(OCIO::GRADING_LOG); //if(!hc.get()) //{ @@ -38,35 +38,35 @@ void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool d //test.setTestNaN(true); } -OCIO_ADD_GPU_TEST(HueCurve, style_log_fwd) +OCIO_ADD_GPU_TEST(GradingHueCurve, style_log_fwd) { GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_FORWARD, false); } -OCIO_ADD_GPU_TEST(HueCurve, style_log_fwd_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurve, style_log_fwd_dynamic) { GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_FORWARD, true); } -OCIO_ADD_GPU_TEST(HueCurve, style_log_rev) +OCIO_ADD_GPU_TEST(GradingHueCurve, style_log_rev) { GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, false); } -OCIO_ADD_GPU_TEST(HueCurve, style_log_rev_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurve, style_log_rev_dynamic) { GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, true); } void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) { - // TODO: Put back the HueCurve test once the CPU implementation is available.' + // TODO: Put back the GradingHueCurve test once the CPU implementation is available.' // GPU test compares the result of the GPU processor to the CPU processor. //auto c = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.785f, 0.231f }, // { 0.809f, 0.631f },{ 0.948f, 0.704f }, // { 1.0f, 1.0f } }); - //auto curve = OCIO::HueCurve::Create(c); + //auto curve = OCIO::GradingHueCurve::Create(c); //auto hc = OCIO::HueCurveTransform::Create(OCIO::GRADING_LIN); //if(!hc.get()) //{ @@ -89,29 +89,29 @@ void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) //test.setTestNaN(true); } -OCIO_ADD_GPU_TEST(HueCurve, style_lin_fwd) +OCIO_ADD_GPU_TEST(GradingHueCurve, style_lin_fwd) { HueCurveLin(test, OCIO::TRANSFORM_DIR_FORWARD, false); } -OCIO_ADD_GPU_TEST(HueCurve, style_lin_fwd_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurve, style_lin_fwd_dynamic) { HueCurveLin(test, OCIO::TRANSFORM_DIR_FORWARD, true); } -OCIO_ADD_GPU_TEST(HueCurve, style_lin_rev) +OCIO_ADD_GPU_TEST(GradingHueCurve, style_lin_rev) { HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, false); } -OCIO_ADD_GPU_TEST(HueCurve, style_lin_rev_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurve, style_lin_rev_dynamic) { HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, true); } void HueSCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) { - // TODO: Implement this HueCurve test once the CPU implementation is available.' + // TODO: Implement this GradingHueCurve test once the CPU implementation is available.' // GPU test compares the result of the GPU processor to the CPU processor. // Create an S-curve with 0 slope at each end. From ccddff666d0c9690bac9ca1990bdc8764fa6905f Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Mon, 10 Jun 2024 11:20:09 -0400 Subject: [PATCH 03/30] Review Fixes P2 (cherry picked from commit d1a5d7a1dca62c0d07fb7158bca578556af7a2ee) Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTransforms.h | 63 +++---- include/OpenColorIO/OpenColorTypes.h | 20 +-- src/OpenColorIO/CMakeLists.txt | 10 +- src/OpenColorIO/DynamicProperty.cpp | 56 +++--- src/OpenColorIO/DynamicProperty.h | 18 +- src/OpenColorIO/Op.cpp | 10 +- src/OpenColorIO/Op.h | 5 + src/OpenColorIO/OpBuilders.h | 2 +- src/OpenColorIO/Transform.cpp | 12 +- .../fileformats/ctf/CTFReaderUtils.h | 16 +- .../fileformats/ctf/CTFTransform.cpp | 38 ++-- .../ExposureContrastOpCPU.cpp | 4 +- .../ExposureContrastOpData.cpp | 6 +- .../ops/gradinghuecurve/GradingHueCurve.cpp | 72 +++++--- .../ops/gradinghuecurve/GradingHueCurve.h | 26 +-- .../ops/gradinghuecurve/GradingHueCurveOp.cpp | 115 ++++++------ .../ops/gradinghuecurve/GradingHueCurveOp.h | 8 +- .../gradinghuecurve/GradingHueCurveOpData.cpp | 61 ++++--- .../gradinghuecurve/GradingHueCurveOpData.h | 32 ++-- .../gradinghuecurve/GradingHueCurveOpGPU.cpp | 30 ++-- .../gradinghuecurve/GradingHueCurveOpGPU.h | 2 +- .../gradingrgbcurve/GradingBSplineCurve.cpp | 16 +- .../ops/gradingrgbcurve/GradingBSplineCurve.h | 4 +- .../transforms/GradingHueCurveTransform.cpp | 168 ++++++++++++++++++ ...Transform.h => GradingHueCurveTransform.h} | 22 +-- .../transforms/HueCurveTransform.cpp | 168 ------------------ tests/cpu/CMakeLists.txt | 8 +- .../GradingHueCurveOpData_tests.cpp} | 12 +- .../GradingHueCurveOp_tests.cpp} | 22 +-- .../GradingHueCurve_tests.cpp} | 0 .../transforms/HueCurveTransform_tests.cpp | 20 +-- tests/gpu/CMakeLists.txt | 2 +- ...Op_test.cpp => GradingHueCurveOp_test.cpp} | 18 +- ...Op_test.cpp => GradingHueCurveOp_test.cpp} | 34 ++-- 34 files changed, 563 insertions(+), 537 deletions(-) create mode 100644 src/OpenColorIO/transforms/GradingHueCurveTransform.cpp rename src/OpenColorIO/transforms/{HueCurveTransform.h => GradingHueCurveTransform.h} (67%) delete mode 100644 src/OpenColorIO/transforms/HueCurveTransform.cpp rename tests/cpu/ops/{gradingrgbcurve/HueCurveOpData_tests.cpp => gradinghuecurve/GradingHueCurveOpData_tests.cpp} (95%) rename tests/cpu/ops/{gradingrgbcurve/HueCurveOp_tests.cpp => gradinghuecurve/GradingHueCurveOp_tests.cpp} (88%) rename tests/cpu/ops/{gradingrgbcurve/HueCurve_tests.cpp => gradinghuecurve/GradingHueCurve_tests.cpp} (100%) rename tests/gpu/{HueCurveOp_test.cpp => GradingHueCurveOp_test.cpp} (89%) rename tests/osl/{HueCurveOp_test.cpp => GradingHueCurveOp_test.cpp} (81%) diff --git a/include/OpenColorIO/OpenColorTransforms.h b/include/OpenColorIO/OpenColorTransforms.h index 3d9c0c5fdc..75f943dce5 100644 --- a/include/OpenColorIO/OpenColorTransforms.h +++ b/include/OpenColorIO/OpenColorTransforms.h @@ -588,8 +588,8 @@ struct OCIOEXPORT GradingHueCurves }; /** - * A set of HUE/SAT/LUM curves. It is used by HueCurveTransform and can be used as - * a dynamic property (see \ref DynamicPropertyHueCurve). + * A set of HUE/SAT/LUM curves. It is used by GradingHueCurveTransform and can be used as + * a dynamic property (see \ref DynamicPropertyGradingHueCurve). */ class OCIOEXPORT GradingHueCurve { @@ -779,10 +779,10 @@ extern OCIOEXPORT DynamicPropertyGradingPrimaryRcPtr AsGradingPrimary(DynamicPro */ extern OCIOEXPORT DynamicPropertyGradingRGBCurveRcPtr AsGradingRGBCurve(DynamicPropertyRcPtr & prop); /** - * Get the property as DynamicPropertyHueCurveRcPtr to access the GradingHueCurveRcPtr - * value. Will throw if property type is not DYNAMIC_PROPERTY_HUE_CURVE. + * Get the property as DynamicPropertyGradingHueCurveRcPtr to access the GradingHueCurveRcPtr + * value. Will throw if property type is not DYNAMIC_PROPERTY_GRADING_HUECURVE. */ -extern OCIOEXPORT DynamicPropertyHueCurveRcPtr AsHueCurve(DynamicPropertyRcPtr & prop); +extern OCIOEXPORT DynamicPropertyGradingHueCurveRcPtr AsGradingHueCurve(DynamicPropertyRcPtr & prop); /** * Get the property as DynamicPropertyGradingToneRcPtr to access the GradingTone value. Will throw * if property type is not DYNAMIC_PROPERTY_GRADING_TONE. @@ -841,20 +841,20 @@ class OCIOEXPORT DynamicPropertyGradingRGBCurve }; /// Interface used to access dynamic property ConstGradingHueCurveRcPtr value. -class OCIOEXPORT DynamicPropertyHueCurve +class OCIOEXPORT DynamicPropertyGradingHueCurve { public: virtual const ConstGradingHueCurveRcPtr & getValue() const = 0; /// Will throw if value is not valid. virtual void setValue(const ConstGradingHueCurveRcPtr & value) = 0; - DynamicPropertyHueCurve(const DynamicPropertyHueCurve &) = delete; - DynamicPropertyHueCurve & operator=(const DynamicPropertyHueCurve &) = delete; + DynamicPropertyGradingHueCurve(const DynamicPropertyGradingHueCurve &) = delete; + DynamicPropertyGradingHueCurve & operator=(const DynamicPropertyGradingHueCurve &) = delete; /// Do not use (needed only for pybind11). - virtual ~DynamicPropertyHueCurve() = default; + virtual ~DynamicPropertyGradingHueCurve() = default; protected: - DynamicPropertyHueCurve() = default; + DynamicPropertyGradingHueCurve() = default; }; /// Interface used to access dynamic property GradingTone value. @@ -1267,28 +1267,29 @@ extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingPrimary * * TODO: Description. */ -class OCIOEXPORT HueCurveTransform : public Transform +class OCIOEXPORT GradingHueCurveTransform : public Transform { public: - /// Creates an instance of HueCurveTransform. - static HueCurveTransformRcPtr Create(GradingStyle style); + /// Creates an instance of GradingHueCurveTransform. + static GradingHueCurveTransformRcPtr Create(GradingStyle style); TransformType getTransformType() const noexcept override { return TRANSFORM_TYPE_GRADING_HUE_CURVE; } -// + virtual const FormatMetadata & getFormatMetadata() const noexcept = 0; virtual FormatMetadata & getFormatMetadata() noexcept = 0; -// - ///// Checks if this equals other. - virtual bool equals(const HueCurveTransform & other) const noexcept = 0; -// - ///// Adjusts the behavior of the transform for log, linear, or video color space encodings. + + /// Checks if this equals other. + virtual bool equals(const GradingHueCurveTransform & other) const noexcept = 0; + + /// Adjusts the behavior of the transform for log, linear, or video color space encodings. virtual GradingStyle getStyle() const noexcept = 0; - ///// Will reset value to style's defaults if style is not the current style. + + /// Will reset value to style's defaults if style is not the current style. virtual void setStyle(GradingStyle style) noexcept = 0; -// + virtual const ConstGradingHueCurveRcPtr getValue() const = 0; - ///// Throws if value is not valid. - //virtual + + /// Throws if value is not valid. virtual void setValue(const ConstGradingHueCurveRcPtr & values) = 0; /** @@ -1310,26 +1311,26 @@ class OCIOEXPORT HueCurveTransform : public Transform */ virtual bool getBypassLinToLog() const = 0; virtual void setBypassLinToLog(bool bypass) = 0; -// + ///** // * Parameters can be made dynamic so the values can be changed through the CPU or GPU processor, - // * but if there are several GradingPrimaryTransform only one can have dynamic parameters. + // * but if there are several GradingHueCurveTransform only one can have dynamic parameters. // */ virtual bool isDynamic() const noexcept = 0; virtual void makeDynamic() noexcept = 0; virtual void makeNonDynamic() noexcept = 0; - HueCurveTransform(const HueCurveTransform &) = delete; - HueCurveTransform & operator= (const HueCurveTransform &) = delete; + GradingHueCurveTransform(const GradingHueCurveTransform &) = delete; + GradingHueCurveTransform & operator= (const GradingHueCurveTransform &) = delete; + /// Do not use (needed only for pybind11). - virtual ~HueCurveTransform() = default; + virtual ~GradingHueCurveTransform() = default; protected: - HueCurveTransform() = default; + GradingHueCurveTransform() = default; }; -extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const HueCurveTransform &) noexcept; - +extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingHueCurveTransform &) noexcept; /** * RGB curve color correction controls. diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 0149662889..dca9a02723 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -167,9 +167,9 @@ class OCIOEXPORT DynamicPropertyGradingRGBCurve; typedef OCIO_SHARED_PTR ConstDynamicPropertyGradingRGBCurveRcPtr; typedef OCIO_SHARED_PTR DynamicPropertyGradingRGBCurveRcPtr; -class OCIOEXPORT DynamicPropertyHueCurve; -typedef OCIO_SHARED_PTR ConstDynamicPropertyHueCurveRcPtr; -typedef OCIO_SHARED_PTR DynamicPropertyHueCurveRcPtr; +class OCIOEXPORT DynamicPropertyGradingHueCurve; +typedef OCIO_SHARED_PTR ConstDynamicPropertyGradingHueCurveRcPtr; +typedef OCIO_SHARED_PTR DynamicPropertyGradingHueCurveRcPtr; class OCIOEXPORT DynamicPropertyGradingTone; typedef OCIO_SHARED_PTR ConstDynamicPropertyGradingToneRcPtr; @@ -199,9 +199,9 @@ class OCIOEXPORT GradingPrimaryTransform; typedef OCIO_SHARED_PTR ConstGradingPrimaryTransformRcPtr; typedef OCIO_SHARED_PTR GradingPrimaryTransformRcPtr; -class OCIOEXPORT HueCurveTransform; -typedef OCIO_SHARED_PTR ConstHueCurveTransformRcPtr; -typedef OCIO_SHARED_PTR HueCurveTransformRcPtr; +class OCIOEXPORT GradingHueCurveTransform; +typedef OCIO_SHARED_PTR ConstGradingHueCurveTransformRcPtr; +typedef OCIO_SHARED_PTR GradingHueCurveTransformRcPtr; class OCIOEXPORT GradingRGBCurveTransform; typedef OCIO_SHARED_PTR ConstGradingRGBCurveTransformRcPtr; @@ -375,7 +375,7 @@ enum TransformType TRANSFORM_TYPE_LUT1D, TRANSFORM_TYPE_LUT3D, TRANSFORM_TYPE_MATRIX, - TRANSFORM_TYPE_RANGE, + TRANSFORM_TYPE_RANGE }; /** @@ -562,8 +562,8 @@ enum DynamicPropertyType DYNAMIC_PROPERTY_GAMMA, ///< Image gamma value (double floating point value) DYNAMIC_PROPERTY_GRADING_PRIMARY, ///< Used by GradingPrimaryTransform DYNAMIC_PROPERTY_GRADING_RGBCURVE, ///< Used by GradingRGBCurveTransform - DYNAMIC_PROPERTY_GRADING_TONE, ///< Used by GradingToneTransform - DYNAMIC_PROPERTY_HUE_CURVE ///< Used by GradingToneTransform + DYNAMIC_PROPERTY_GRADING_TONE, ///< Used by GradingToneTransform + DYNAMIC_PROPERTY_GRADING_HUECURVE ///< Used by GradingToneTransform }; /// Types for GradingRGBCurve. @@ -596,7 +596,7 @@ enum BSplineCurveType DIAGONAL_B_SPLINE, //!< Monotonic quadratic B-spline based function (newer algorithm). HUE_HUE_B_SPLINE, //!< Special B-spline used for the hue vs. hue curve (monotonic and periodic). PERIODIC_B_SPLINE, //!< Periodic non-monotonic B-spline used for some of the hue curves. - HORIZONTAL_B_SPLINE + HORIZONTAL_B_SPLINE //!< Non-monotonic B-spline used for some of the lum vs. sat-gain curve. }; /// Types for uniform data. diff --git a/src/OpenColorIO/CMakeLists.txt b/src/OpenColorIO/CMakeLists.txt index 1c53cab1d3..db813b5769 100755 --- a/src/OpenColorIO/CMakeLists.txt +++ b/src/OpenColorIO/CMakeLists.txt @@ -94,6 +94,10 @@ set(SOURCES ops/gamma/GammaOpGPU.cpp ops/gamma/GammaOpUtils.cpp ops/gamma/GammaOp.cpp + ops/gradinghuecurve/GradingHueCurve.cpp + ops/gradinghuecurve/GradingHueCurveOpData.cpp + ops/gradinghuecurve/GradingHueCurveOpGPU.cpp + ops/gradinghuecurve/GradingHueCurveOp.cpp ops/gradingprimary/GradingPrimary.cpp ops/gradingprimary/GradingPrimaryOpCPU.cpp ops/gradingprimary/GradingPrimaryOpData.cpp @@ -104,11 +108,7 @@ set(SOURCES ops/gradingrgbcurve/GradingRGBCurveOpData.cpp ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp ops/gradingrgbcurve/GradingRGBCurveOp.cpp - ops/gradinghuecurve/GradingHueCurveOpData.cpp - ops/gradinghuecurve/GradingHueCurveOp.cpp - ops/gradinghuecurve/GradingHueCurveOpGPU.cpp ops/gradingrgbcurve/GradingRGBCurve.cpp - ops/gradinghuecurve/GradingHueCurve.cpp ops/gradingtone/GradingTone.cpp ops/gradingtone/GradingToneOpCPU.cpp ops/gradingtone/GradingToneOpData.cpp @@ -173,8 +173,8 @@ set(SOURCES transforms/ExposureContrastTransform.cpp transforms/FileTransform.cpp transforms/FixedFunctionTransform.cpp + transforms/GradingHueCurveTransform.cpp transforms/GradingPrimaryTransform.cpp - transforms/HueCurveTransform.cpp transforms/GradingRGBCurveTransform.cpp transforms/GradingToneTransform.cpp transforms/GroupTransform.cpp diff --git a/src/OpenColorIO/DynamicProperty.cpp b/src/OpenColorIO/DynamicProperty.cpp index 2ec1469fd7..5ea1f9021a 100644 --- a/src/OpenColorIO/DynamicProperty.cpp +++ b/src/OpenColorIO/DynamicProperty.cpp @@ -33,11 +33,11 @@ DynamicPropertyGradingRGBCurveRcPtr AsGradingRGBCurve(DynamicPropertyRcPtr & pro if (res) return res; throw Exception("Dynamic property value is not a grading RGB curve."); } -DynamicPropertyHueCurveRcPtr AsHueCurve(DynamicPropertyRcPtr & prop) +DynamicPropertyGradingHueCurveRcPtr AsGradingHueCurve(DynamicPropertyRcPtr & prop) { - auto res = OCIO_DYNAMIC_POINTER_CAST(prop); + auto res = OCIO_DYNAMIC_POINTER_CAST(prop); if (res) return res; - throw Exception("Dynamic property value is not a hue curve."); + throw Exception("Dynamic property value is not a grading hue curve."); } DynamicPropertyGradingToneRcPtr AsGradingTone(DynamicPropertyRcPtr & prop) { @@ -103,10 +103,10 @@ bool DynamicPropertyImpl::equals(const DynamicPropertyImpl & rhs) const auto rhst = dynamic_cast(&rhs); return lhst && rhst && (lhst->getValue() == rhst->getValue()); } - case DYNAMIC_PROPERTY_HUE_CURVE: + case DYNAMIC_PROPERTY_GRADING_HUECURVE: { - auto lhst = dynamic_cast(this); - auto rhst = dynamic_cast(&rhs); + auto lhst = dynamic_cast(this); + auto rhst = dynamic_cast(&rhs); return lhst && rhst && (*lhst->getValue() == *rhst->getValue());} } // Different values. @@ -266,8 +266,8 @@ unsigned int DynamicPropertyGradingRGBCurveImpl::GetMaxCoefs() void DynamicPropertyGradingRGBCurveImpl::precompute() { m_knotsCoefs.m_localBypass = false; - m_knotsCoefs.m_nCoefs = 0; - m_knotsCoefs.m_nKnots = 0; + m_knotsCoefs.m_numCoefs = 0; + m_knotsCoefs.m_numKnots = 0; // Compute knots and coefficients for each control point and pack all knots and coefs of // all curves in one knots array and one coef array, using an offset array to find specific @@ -278,7 +278,7 @@ void DynamicPropertyGradingRGBCurveImpl::precompute() auto curveImpl = dynamic_cast(curve.get()); curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c)); } - if (m_knotsCoefs.m_nKnots <= 0) m_knotsCoefs.m_localBypass = true; + if (m_knotsCoefs.m_numKnots <= 0) m_knotsCoefs.m_localBypass = true; } DynamicPropertyGradingRGBCurveImplRcPtr DynamicPropertyGradingRGBCurveImpl::createEditableCopy() const @@ -289,21 +289,21 @@ DynamicPropertyGradingRGBCurveImplRcPtr DynamicPropertyGradingRGBCurveImpl::crea } -DynamicPropertyHueCurveImpl::DynamicPropertyHueCurveImpl( +DynamicPropertyGradingHueCurveImpl::DynamicPropertyGradingHueCurveImpl( const ConstGradingHueCurveRcPtr & value, bool dynamic) - : DynamicPropertyImpl(DYNAMIC_PROPERTY_HUE_CURVE, dynamic) + : DynamicPropertyImpl(DYNAMIC_PROPERTY_GRADING_HUECURVE, dynamic) { m_hueCurve = GradingHueCurve::Create(value); // Convert control points from the UI into knots and coefficients for the apply. precompute(); } -const ConstGradingHueCurveRcPtr & DynamicPropertyHueCurveImpl::getValue() const +const ConstGradingHueCurveRcPtr & DynamicPropertyGradingHueCurveImpl::getValue() const { return m_hueCurve; } -void DynamicPropertyHueCurveImpl::setValue(const ConstGradingHueCurveRcPtr & value) +void DynamicPropertyGradingHueCurveImpl::setValue(const ConstGradingHueCurveRcPtr & value) { value->validate(); @@ -312,56 +312,56 @@ void DynamicPropertyHueCurveImpl::setValue(const ConstGradingHueCurveRcPtr & val precompute(); } -bool DynamicPropertyHueCurveImpl::getLocalBypass() const +bool DynamicPropertyGradingHueCurveImpl::getLocalBypass() const { return m_knotsCoefs.m_localBypass; } -int DynamicPropertyHueCurveImpl::getNumKnots() const +int DynamicPropertyGradingHueCurveImpl::getNumKnots() const { return static_cast(m_knotsCoefs.m_knotsArray.size()); } -int DynamicPropertyHueCurveImpl::getNumCoefs() const +int DynamicPropertyGradingHueCurveImpl::getNumCoefs() const { return static_cast(m_knotsCoefs.m_coefsArray.size()); } -const int * DynamicPropertyHueCurveImpl::getKnotsOffsetsArray() const +const int * DynamicPropertyGradingHueCurveImpl::getKnotsOffsetsArray() const { return m_knotsCoefs.m_knotsOffsetsArray.data(); } -const int * DynamicPropertyHueCurveImpl::getCoefsOffsetsArray() const +const int * DynamicPropertyGradingHueCurveImpl::getCoefsOffsetsArray() const { return m_knotsCoefs.m_coefsOffsetsArray.data(); } -const float * DynamicPropertyHueCurveImpl::getKnotsArray() const +const float * DynamicPropertyGradingHueCurveImpl::getKnotsArray() const { return m_knotsCoefs.m_knotsArray.data(); } -const float * DynamicPropertyHueCurveImpl::getCoefsArray() const +const float * DynamicPropertyGradingHueCurveImpl::getCoefsArray() const { return m_knotsCoefs.m_coefsArray.data(); } -unsigned int DynamicPropertyHueCurveImpl::GetMaxKnots() +unsigned int DynamicPropertyGradingHueCurveImpl::GetMaxKnots() { return GradingBSplineCurveImpl::KnotsCoefs::MAX_NUM_KNOTS; } -unsigned int DynamicPropertyHueCurveImpl::GetMaxCoefs() +unsigned int DynamicPropertyGradingHueCurveImpl::GetMaxCoefs() { return GradingBSplineCurveImpl::KnotsCoefs::MAX_NUM_COEFS; } -void DynamicPropertyHueCurveImpl::precompute() +void DynamicPropertyGradingHueCurveImpl::precompute() { m_knotsCoefs.m_localBypass = false; - m_knotsCoefs.m_nCoefs = 0; - m_knotsCoefs.m_nKnots = 0; + m_knotsCoefs.m_numCoefs = 0; + m_knotsCoefs.m_numKnots = 0; // Compute knots and coefficients for each control point and pack all knots and coefs of // all curves in one knots array and one coef array, using an offset array to find specific @@ -372,12 +372,12 @@ void DynamicPropertyHueCurveImpl::precompute() auto curveImpl = dynamic_cast(curve.get()); curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c)); } - if (m_knotsCoefs.m_nKnots == 0) m_knotsCoefs.m_localBypass = true; + if (m_knotsCoefs.m_numKnots == 0) m_knotsCoefs.m_localBypass = true; } -DynamicPropertyHueCurveImplRcPtr DynamicPropertyHueCurveImpl::createEditableCopy() const +DynamicPropertyGradingHueCurveImplRcPtr DynamicPropertyGradingHueCurveImpl::createEditableCopy() const { - auto res = std::make_shared(getValue(), isDynamic()); + auto res = std::make_shared(getValue(), isDynamic()); res->m_knotsCoefs = m_knotsCoefs; return res; } diff --git a/src/OpenColorIO/DynamicProperty.h b/src/OpenColorIO/DynamicProperty.h index af48963d49..0480cc8a1e 100644 --- a/src/OpenColorIO/DynamicProperty.h +++ b/src/OpenColorIO/DynamicProperty.h @@ -173,16 +173,16 @@ class DynamicPropertyGradingRGBCurveImpl : public DynamicPropertyImpl, GradingBSplineCurveImpl::KnotsCoefs m_knotsCoefs{ 4 }; }; -class DynamicPropertyHueCurveImpl; -typedef OCIO_SHARED_PTR DynamicPropertyHueCurveImplRcPtr; +class DynamicPropertyGradingHueCurveImpl; +typedef OCIO_SHARED_PTR DynamicPropertyGradingHueCurveImplRcPtr; -class DynamicPropertyHueCurveImpl : public DynamicPropertyImpl, - public DynamicPropertyHueCurve +class DynamicPropertyGradingHueCurveImpl : public DynamicPropertyImpl, + public DynamicPropertyGradingHueCurve { public: - DynamicPropertyHueCurveImpl() = delete; - DynamicPropertyHueCurveImpl(const ConstGradingHueCurveRcPtr & value, bool dynamic); - ~DynamicPropertyHueCurveImpl() = default; + DynamicPropertyGradingHueCurveImpl() = delete; + DynamicPropertyGradingHueCurveImpl(const ConstGradingHueCurveRcPtr & value, bool dynamic); + ~DynamicPropertyGradingHueCurveImpl() = default; const ConstGradingHueCurveRcPtr & getValue() const override; void setValue(const ConstGradingHueCurveRcPtr & value) override; @@ -200,14 +200,14 @@ class DynamicPropertyHueCurveImpl : public DynamicPropertyImpl, static unsigned int GetMaxKnots(); static unsigned int GetMaxCoefs(); - DynamicPropertyHueCurveImplRcPtr createEditableCopy() const; + DynamicPropertyGradingHueCurveImplRcPtr createEditableCopy() const; private: void precompute(); ConstGradingHueCurveRcPtr m_hueCurve; - // Holds curve data as knots and coefs. There is 8 curve. + // Holds curve data as knots and coefs. There are 8 curves. GradingBSplineCurveImpl::KnotsCoefs m_knotsCoefs{ 8 }; }; diff --git a/src/OpenColorIO/Op.cpp b/src/OpenColorIO/Op.cpp index 191b8eaed6..b9d5ac9440 100755 --- a/src/OpenColorIO/Op.cpp +++ b/src/OpenColorIO/Op.cpp @@ -407,8 +407,8 @@ void ValidateDynamicProperty(OpRcPtr op, std::shared_ptr & prop, DynamicPrope case DYNAMIC_PROPERTY_GRADING_TONE: os << "Grading tone"; break; - case DYNAMIC_PROPERTY_HUE_CURVE: - os << "Hue curve"; + case DYNAMIC_PROPERTY_GRADING_HUECURVE: + os << "Grading hue curve"; break; } os << " dynamic property can only be there once."; @@ -550,9 +550,9 @@ void CreateOpVecFromOpData(OpRcPtrVec & ops, case OpData::GradingHueCurveType: { - auto hueSrc = std::dynamic_pointer_cast(opData); - auto hue = std::make_shared(*hueSrc); - CreateHueCurveOp(ops, hue, dir); + auto hueSrc = std::dynamic_pointer_cast(opData); + auto hue = std::make_shared(*hueSrc); + CreateGradingHueCurveOp(ops, hue, dir); break; } diff --git a/src/OpenColorIO/Op.h b/src/OpenColorIO/Op.h index b688c26764..aac79a1239 100644 --- a/src/OpenColorIO/Op.h +++ b/src/OpenColorIO/Op.h @@ -268,6 +268,11 @@ class Op { throw Exception("Op does not implement grading rgb curve dynamic property."); } + virtual void replaceDynamicProperty(DynamicPropertyType /* type */, + DynamicPropertyGradingHueCurveImplRcPtr & /* prop */) + { + throw Exception("Op does not implement grading rgb curve dynamic property."); + } virtual void replaceDynamicProperty(DynamicPropertyType /* type */, DynamicPropertyGradingToneImplRcPtr & /* prop */) { diff --git a/src/OpenColorIO/OpBuilders.h b/src/OpenColorIO/OpBuilders.h index 81c6b4620e..4639be212b 100644 --- a/src/OpenColorIO/OpBuilders.h +++ b/src/OpenColorIO/OpBuilders.h @@ -103,7 +103,7 @@ void BuildGradingPrimaryOp(OpRcPtrVec & ops, void BuildHueCurveOp(OpRcPtrVec & ops, const Config & config, const ConstContextRcPtr & context, - const HueCurveTransform & transform, + const GradingHueCurveTransform & transform, TransformDirection dir); void BuildGradingRGBCurveOp(OpRcPtrVec & ops, diff --git a/src/OpenColorIO/Transform.cpp b/src/OpenColorIO/Transform.cpp index af3f0c9086..7b05bd392d 100755 --- a/src/OpenColorIO/Transform.cpp +++ b/src/OpenColorIO/Transform.cpp @@ -109,8 +109,8 @@ void BuildOps(OpRcPtrVec & ops, { BuildGradingRGBCurveOp(ops, config, context, *gradingCurveTransform, dir); } - else if (ConstHueCurveTransformRcPtr hueCurveTransform = \ - DynamicPtrCast(transform)) + else if (ConstGradingHueCurveTransformRcPtr hueCurveTransform = \ + DynamicPtrCast(transform)) { BuildHueCurveOp(ops, config, context, *hueCurveTransform, dir); } @@ -238,8 +238,8 @@ std::ostream& operator<< (std::ostream & os, const Transform & transform) { os << *gradingRGBCurveTransform; } - else if (const HueCurveTransform * hueCurveTransform = \ - dynamic_cast(t)) + else if (const GradingHueCurveTransform * hueCurveTransform = \ + dynamic_cast(t)) { os << *hueCurveTransform; } @@ -339,6 +339,10 @@ void CreateTransform(GroupTransformRcPtr & group, ConstOpRcPtr & op) { CreateGradingPrimaryTransform(group, op); } + else if (DynamicPtrCast(data)) + { + CreateGradingHueCurveTransform(group, op); + } else if (DynamicPtrCast(data)) { CreateGradingRGBCurveTransform(group, op); diff --git a/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h b/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h index 45c9163e00..efb45e0ff2 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h +++ b/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h @@ -70,14 +70,14 @@ static constexpr char TAG_PROCESS_LIST[] = "ProcessList"; static constexpr char TAG_RANGE[] = "Range"; static constexpr char TAG_REFERENCE[] = "Reference"; static constexpr char TAG_HUE_CURVE[] = "GradingHueCurve"; -static constexpr char TAG_HUE_CURVE_HUE_HUE[] = "hue_hue"; -static constexpr char TAG_HUE_CURVE_HUE_SAT[] = "hue_sat"; -static constexpr char TAG_HUE_CURVE_HUE_LUM[] = "hue_lum"; -static constexpr char TAG_HUE_CURVE_LUM_SAT[] = "lum_sat"; -static constexpr char TAG_HUE_CURVE_SAT_SAT[] = "sat_sat"; -static constexpr char TAG_HUE_CURVE_LUM_LUM[] = "lum_lum"; -static constexpr char TAG_HUE_CURVE_SAT_LUM[] = "sat_lum"; -static constexpr char TAG_HUE_CURVE_HUE_FX[] = "hue_fx"; +static constexpr char TAG_HUE_CURVE_HUE_HUE[] = "HueHue"; +static constexpr char TAG_HUE_CURVE_HUE_SAT[] = "HueSat"; +static constexpr char TAG_HUE_CURVE_HUE_LUM[] = "HueLum"; +static constexpr char TAG_HUE_CURVE_LUM_SAT[] = "LumSat"; +static constexpr char TAG_HUE_CURVE_SAT_SAT[] = "SatSat"; +static constexpr char TAG_HUE_CURVE_LUM_LUM[] = "LumLum"; +static constexpr char TAG_HUE_CURVE_SAT_LUM[] = "SatLum"; +static constexpr char TAG_HUE_CURVE_HUE_FX[] = "HueFx"; static constexpr char TAG_RGB_CURVE[] = "GradingRGBCurve"; static constexpr char TAG_RGB_CURVE_BLUE[] = "Blue"; static constexpr char TAG_RGB_CURVE_GREEN[] = "Green"; diff --git a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp index 87654bf392..a5a63f7836 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp @@ -1618,14 +1618,14 @@ void GradingRGBCurveWriter::writeContent() const } /////////////////////////////////////////////////////////////////////////////// -class HueCurveWriter : public OpWriter +class GradingHueCurveWriter : public OpWriter { public: - HueCurveWriter() = delete; - HueCurveWriter(const HueCurveWriter&) = delete; - HueCurveWriter& operator=(const HueCurveWriter&) = delete; - HueCurveWriter(XmlFormatter & formatter, ConstHueCurveOpDataRcPtr primary); - virtual ~HueCurveWriter(); + GradingHueCurveWriter() = delete; + GradingHueCurveWriter(const GradingHueCurveWriter&) = delete; + GradingHueCurveWriter& operator=(const GradingHueCurveWriter&) = delete; + GradingHueCurveWriter(XmlFormatter & formatter, ConstGradingHueCurveOpDataRcPtr primary); + virtual ~GradingHueCurveWriter(); protected: ConstOpDataRcPtr getOp() const override; @@ -1635,31 +1635,31 @@ class HueCurveWriter : public OpWriter private: void writeCurve(const char * tag, const ConstGradingBSplineCurveRcPtr & curve) const; - ConstHueCurveOpDataRcPtr m_curves; + ConstGradingHueCurveOpDataRcPtr m_curves; }; -HueCurveWriter::HueCurveWriter(XmlFormatter & formatter, - ConstHueCurveOpDataRcPtr curves) +GradingHueCurveWriter::GradingHueCurveWriter(XmlFormatter & formatter, + ConstGradingHueCurveOpDataRcPtr curves) : OpWriter(formatter) , m_curves(curves) { } -HueCurveWriter::~HueCurveWriter() +GradingHueCurveWriter::~GradingHueCurveWriter() { } -ConstOpDataRcPtr HueCurveWriter::getOp() const +ConstOpDataRcPtr GradingHueCurveWriter::getOp() const { return m_curves; } -const char * HueCurveWriter::getTagName() const +const char * GradingHueCurveWriter::getTagName() const { return TAG_HUE_CURVE; } -void HueCurveWriter::getAttributes(XmlFormatter::Attributes& attributes) const +void GradingHueCurveWriter::getAttributes(XmlFormatter::Attributes& attributes) const { OpWriter::getAttributes(attributes); @@ -1675,7 +1675,7 @@ void HueCurveWriter::getAttributes(XmlFormatter::Attributes& attributes) const } } -void HueCurveWriter::writeCurve(const char * tag, +void GradingHueCurveWriter::writeCurve(const char * tag, const ConstGradingBSplineCurveRcPtr & curve) const { m_formatter.writeStartTag(tag, XmlFormatter::Attributes()); @@ -1721,12 +1721,12 @@ void HueCurveWriter::writeCurve(const char * tag, m_formatter.writeEndTag(tag); } -void HueCurveWriter::writeContent() const +void GradingHueCurveWriter::writeContent() const { const auto & vals = m_curves->getValue(); - auto & defCurve = (m_curves->getStyle() == GRADING_LIN) ? HueCurveImpl::DefaultCurvesLin: - HueCurveImpl::DefaultCurves; + auto & defCurve = (m_curves->getStyle() == GRADING_LIN) ? GradingHueCurveImpl::DefaultCurvesLin: + GradingHueCurveImpl::DefaultCurves; static const std::vector curveTags = { TAG_HUE_CURVE_HUE_HUE, TAG_HUE_CURVE_HUE_SAT, @@ -2840,8 +2840,8 @@ void TransformWriter::writeOps(const CTFVersion & version) const ThrowWriteOp("GradingHueCurve"); } - auto hue = OCIO_DYNAMIC_POINTER_CAST(op); - HueCurveWriter opWriter(m_formatter, hue); + auto hue = OCIO_DYNAMIC_POINTER_CAST(op); + GradingHueCurveWriter opWriter(m_formatter, hue); opWriter.setInputBitdepth(inBD); opWriter.setOutputBitdepth(outBD); opWriter.write(); diff --git a/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpCPU.cpp b/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpCPU.cpp index de35d72967..6a3e7383a5 100644 --- a/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpCPU.cpp +++ b/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpCPU.cpp @@ -86,7 +86,7 @@ bool ECRendererBase::hasDynamicProperty(DynamicPropertyType type) const break; case DYNAMIC_PROPERTY_GRADING_PRIMARY: case DYNAMIC_PROPERTY_GRADING_RGBCURVE: - case DYNAMIC_PROPERTY_HUE_CURVE: + case DYNAMIC_PROPERTY_GRADING_HUECURVE: case DYNAMIC_PROPERTY_GRADING_TONE: default: break; @@ -119,7 +119,7 @@ DynamicPropertyRcPtr ECRendererBase::getDynamicProperty(DynamicPropertyType type break; case DYNAMIC_PROPERTY_GRADING_PRIMARY: case DYNAMIC_PROPERTY_GRADING_RGBCURVE: - case DYNAMIC_PROPERTY_HUE_CURVE: + case DYNAMIC_PROPERTY_GRADING_HUECURVE: case DYNAMIC_PROPERTY_GRADING_TONE: default: throw Exception("Dynamic property type not supported by ExposureContrast."); diff --git a/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpData.cpp b/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpData.cpp index 4c3179cbd7..869b28e607 100644 --- a/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpData.cpp +++ b/src/OpenColorIO/ops/exposurecontrast/ExposureContrastOpData.cpp @@ -311,7 +311,7 @@ bool ExposureContrastOpData::hasDynamicProperty(DynamicPropertyType type) const break; case DYNAMIC_PROPERTY_GRADING_PRIMARY: case DYNAMIC_PROPERTY_GRADING_RGBCURVE: - case DYNAMIC_PROPERTY_HUE_CURVE: + case DYNAMIC_PROPERTY_GRADING_HUECURVE: case DYNAMIC_PROPERTY_GRADING_TONE: default: break; @@ -345,7 +345,7 @@ ExposureContrastOpData::getDynamicProperty(DynamicPropertyType type) const break; case DYNAMIC_PROPERTY_GRADING_PRIMARY: case DYNAMIC_PROPERTY_GRADING_RGBCURVE: - case DYNAMIC_PROPERTY_HUE_CURVE: + case DYNAMIC_PROPERTY_GRADING_HUECURVE: case DYNAMIC_PROPERTY_GRADING_TONE: default: throw Exception("Dynamic property type not supported by ExposureContrast."); @@ -385,7 +385,7 @@ void ExposureContrastOpData::replaceDynamicProperty(DynamicPropertyType type, break; case DYNAMIC_PROPERTY_GRADING_PRIMARY: case DYNAMIC_PROPERTY_GRADING_RGBCURVE: - case DYNAMIC_PROPERTY_HUE_CURVE: + case DYNAMIC_PROPERTY_GRADING_HUECURVE: case DYNAMIC_PROPERTY_GRADING_TONE: default: throw Exception("Dynamic property type not supported by ExposureContrast."); diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp index 4d6bceadf7..cdf8e9081c 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp @@ -44,18 +44,18 @@ static const std::vector DefaultLumLumLinCtrl{ { -7.0f, -7. } -const GradingBSplineCurveImpl HueCurveImpl::DefaultHueHue(DefaultHueHueCtrl, BSplineCurveType::HUE_HUE_B_SPLINE ); -const GradingBSplineCurveImpl HueCurveImpl::DefaultHueSat(DefaultHueSatCtrl, BSplineCurveType::PERIODIC_B_SPLINE ); -const GradingBSplineCurveImpl HueCurveImpl::DefaultHueFx(DefaultHueFxCtrl, BSplineCurveType::PERIODIC_B_SPLINE ); -const GradingBSplineCurveImpl HueCurveImpl::DefaultLumSat(DefaultLumSatCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); -const GradingBSplineCurveImpl HueCurveImpl::DefaultLumSatLin(DefaultLumSatLinCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); -const GradingBSplineCurveImpl HueCurveImpl::DefaultSatSat(DefaultSatSatCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); -const GradingBSplineCurveImpl HueCurveImpl::DefaultSatLum(DefaultSatLumCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); -const GradingBSplineCurveImpl HueCurveImpl::DefaultLumLum(DefaultLumLumCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); -const GradingBSplineCurveImpl HueCurveImpl::DefaultLumLumLin(DefaultLumLumLinCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueHue(DefaultHueHueCtrl, BSplineCurveType::HUE_HUE_B_SPLINE ); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueSat(DefaultHueSatCtrl, BSplineCurveType::PERIODIC_B_SPLINE ); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueFx(DefaultHueFxCtrl, BSplineCurveType::PERIODIC_B_SPLINE ); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSat(DefaultLumSatCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSatLin(DefaultLumSatLinCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultSatSat(DefaultSatSatCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultSatLum(DefaultSatLumCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumLum(DefaultLumLumCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumLumLin(DefaultLumLumLinCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); const std::array, static_cast(HUE_NUM_CURVES)> - HueCurveImpl::DefaultCurvesLin( { std::ref(DefaultHueHue), + GradingHueCurveImpl::DefaultCurvesLin( { std::ref(DefaultHueHue), std::ref(DefaultHueSat), std::ref(DefaultHueSat), std::ref(DefaultLumSatLin), @@ -66,7 +66,7 @@ const std::array, static_c const std::array, static_cast(HUE_NUM_CURVES)> - HueCurveImpl::DefaultCurves( { std::ref(DefaultHueHue), + GradingHueCurveImpl::DefaultCurves( { std::ref(DefaultHueHue), std::ref(DefaultHueSat), std::ref(DefaultHueSat), std::ref(DefaultLumSat), @@ -75,14 +75,14 @@ const std::array, static_c std::ref(DefaultSatLum), std::ref(DefaultHueFx) }); -HueCurveImpl::HueCurveImpl() : - HueCurveImpl(GRADING_LOG) +GradingHueCurveImpl::GradingHueCurveImpl() : + GradingHueCurveImpl(GRADING_LOG) {} -HueCurveImpl::HueCurveImpl(GradingStyle style) +GradingHueCurveImpl::GradingHueCurveImpl(GradingStyle style) { - auto & curves = (style == GRADING_LIN) ? HueCurveImpl::DefaultCurvesLin: - HueCurveImpl::DefaultCurves; + auto & curves = (style == GRADING_LIN) ? GradingHueCurveImpl::DefaultCurvesLin: + GradingHueCurveImpl::DefaultCurves; for (int c = 0; c < HUE_NUM_CURVES; ++c) { @@ -90,7 +90,7 @@ HueCurveImpl::HueCurveImpl(GradingStyle style) } } -HueCurveImpl::HueCurveImpl(const GradingHueCurves & curves) +GradingHueCurveImpl::GradingHueCurveImpl(const GradingHueCurves & curves) { m_curves[HUE_HUE] = curves.hueHue->createEditableCopy(); m_curves[HUE_SAT] = curves.hueSat->createEditableCopy(); @@ -102,9 +102,9 @@ HueCurveImpl::HueCurveImpl(const GradingHueCurves & curves) m_curves[HUE_FX] = curves.hueFx->createEditableCopy(); } -HueCurveImpl::HueCurveImpl(const ConstGradingHueCurveRcPtr & rhs) +GradingHueCurveImpl::GradingHueCurveImpl(const ConstGradingHueCurveRcPtr & rhs) { - auto impl = dynamic_cast(rhs.get()); + auto impl = dynamic_cast(rhs.get()); if (impl) { for (int c = 0; c < HUE_NUM_CURVES; ++c) @@ -114,9 +114,9 @@ HueCurveImpl::HueCurveImpl(const ConstGradingHueCurveRcPtr & rhs) } } -GradingHueCurveRcPtr HueCurveImpl::createEditableCopy() const +GradingHueCurveRcPtr GradingHueCurveImpl::createEditableCopy() const { - auto newCurve = std::make_shared(); + auto newCurve = std::make_shared(); for (int c = 0; c < HUE_NUM_CURVES; ++c) { newCurve->m_curves[c] = m_curves[c]->createEditableCopy(); @@ -157,7 +157,7 @@ const char * CurveType(int c) } } -void HueCurveImpl::validate() const +void GradingHueCurveImpl::validate() const { for (int c = 0; c < HUE_NUM_CURVES; ++c) { @@ -175,7 +175,7 @@ void HueCurveImpl::validate() const } } -bool HueCurveImpl::isIdentity() const +bool GradingHueCurveImpl::isIdentity() const { for (int c = 0; c < HUE_NUM_CURVES; ++c) { @@ -187,33 +187,49 @@ bool HueCurveImpl::isIdentity() const return true; } -ConstGradingBSplineCurveRcPtr HueCurveImpl::getCurve(HueCurveType c) const +bool GradingHueCurveImpl::isHueCurveTypeValid(HueCurveType c) const { + return ( c >= HUE_HUE && + c < HUE_NUM_CURVES ); +} + +ConstGradingBSplineCurveRcPtr GradingHueCurveImpl::getCurve(HueCurveType c) const +{ + if(!isHueCurveTypeValid(c)) + { + throw Exception("The HueCurveType provided is invalid"); + } + return m_curves[c]; } -GradingBSplineCurveRcPtr HueCurveImpl::getCurve(HueCurveType c) +GradingBSplineCurveRcPtr GradingHueCurveImpl::getCurve(HueCurveType c) { + if(!isHueCurveTypeValid(c)) + { + throw Exception("The HueCurveType provided is invalid"); + } + return m_curves[c]; } GradingHueCurveRcPtr GradingHueCurve::Create(GradingStyle style) { - auto newCurve = std::make_shared(style); + auto newCurve = std::make_shared(style); GradingHueCurveRcPtr res = newCurve; return res; } GradingHueCurveRcPtr GradingHueCurve::Create(const ConstGradingHueCurveRcPtr & rhs) { - auto newCurve = std::make_shared(rhs); + auto newCurve = std::make_shared(rhs); GradingHueCurveRcPtr res = newCurve; return res; } GradingHueCurveRcPtr GradingHueCurve::Create(const GradingHueCurves & curves) { - auto newCurve = std::make_shared(curves); + auto newCurve = std::make_shared(curves); GradingHueCurveRcPtr res = newCurve; return res; } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h index 37fe892308..1d2dd0db8f 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h @@ -2,8 +2,8 @@ // Copyright Contributors to the OpenColorIO Project. -#ifndef INCLUDED_OCIO_HUECURVE_H -#define INCLUDED_OCIO_HUECURVE_H +#ifndef INCLUDED_OCIO_GRADINGHUECURVE_H +#define INCLUDED_OCIO_GRADINGHUECURVE_H #include @@ -13,16 +13,16 @@ namespace OCIO_NAMESPACE { -// Class to hold the RGB curve data that is used in the corresponding dynamic property and in -// the CTF reader.. This allows moving some of the code from DynamicProperty to here. The +// Class to hold the hue curve data that is used in the corresponding dynamic property and in +// the CTF reader. This allows moving some of the code from DynamicProperty to here. The // dynamic property is then used by the OpData, which is then used by the Op and Transform. -class HueCurveImpl : public GradingHueCurve +class GradingHueCurveImpl : public GradingHueCurve { public: - HueCurveImpl(); - HueCurveImpl(GradingStyle style); - HueCurveImpl(const GradingHueCurves & curves ); - HueCurveImpl(const ConstGradingHueCurveRcPtr & rhs); + GradingHueCurveImpl(); + GradingHueCurveImpl(GradingStyle style); + GradingHueCurveImpl(const GradingHueCurves & curves ); + GradingHueCurveImpl(const ConstGradingHueCurveRcPtr & rhs); GradingHueCurveRcPtr createEditableCopy() const override; @@ -45,12 +45,14 @@ class HueCurveImpl : public GradingHueCurve static const std::array, static_cast(HUE_NUM_CURVES)> DefaultCurves; private: + bool isHueCurveTypeValid(HueCurveType c) const; + std::array(HUE_NUM_CURVES)> m_curves; }; -typedef OCIO_SHARED_PTR ConstHueCurveImplRcPtr; -typedef OCIO_SHARED_PTR HueCurveImplRcPtr; +typedef OCIO_SHARED_PTR ConstHueCurveImplRcPtr; +typedef OCIO_SHARED_PTR HueCurveImplRcPtr; } -#endif //INCLUDED_OCIO_GRADINGRGBCURVE_H +#endif //INCLUDED_OCIO_GRADINGHUECURVE_H diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp index 64e12190a0..c34ee5fc33 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp @@ -7,10 +7,9 @@ #include #include "GpuShaderUtils.h" -//#include "ops/gradingrgbcurve/GradingRGBCurveOpCPU.h" #include "ops/gradinghuecurve/GradingHueCurveOpGPU.h" #include "ops/gradinghuecurve/GradingHueCurveOp.h" -#include "transforms/HueCurveTransform.h" +#include "transforms/GradingHueCurveTransform.h" namespace OCIO_NAMESPACE { @@ -18,18 +17,18 @@ namespace OCIO_NAMESPACE namespace { -class HueCurveOp; -typedef OCIO_SHARED_PTR HueCurveOpRcPtr; -typedef OCIO_SHARED_PTR ConstHueCurveOpRcPtr; +class GradingHueCurveOp; +typedef OCIO_SHARED_PTR GradingHueCurveOpRcPtr; +typedef OCIO_SHARED_PTR ConstGradingHueCurveOpRcPtr; -class HueCurveOp : public Op +class GradingHueCurveOp : public Op { public: - HueCurveOp() = delete; - HueCurveOp(const HueCurveOp &) = delete; - explicit HueCurveOp(HueCurveOpDataRcPtr & data); + GradingHueCurveOp() = delete; + GradingHueCurveOp(const GradingHueCurveOp &) = delete; + explicit GradingHueCurveOp(GradingHueCurveOpDataRcPtr & data); - virtual ~HueCurveOp(); + virtual ~GradingHueCurveOp(); OpRcPtr clone() const override; @@ -47,7 +46,7 @@ class HueCurveOp : public Op bool hasDynamicProperty(DynamicPropertyType type) const override; DynamicPropertyRcPtr getDynamicProperty(DynamicPropertyType type) const override; void replaceDynamicProperty(DynamicPropertyType type, - DynamicPropertyGradingRGBCurveImplRcPtr & prop) override; + DynamicPropertyGradingHueCurveImplRcPtr & prop) override; void removeDynamicProperties() override; ConstOpCPURcPtr getCPUOp(bool fastLogExpPow) const override; @@ -55,91 +54,91 @@ class HueCurveOp : public Op void extractGpuShaderInfo(GpuShaderCreatorRcPtr & shaderCreator) const override; protected: - ConstHueCurveOpDataRcPtr hueCurveData() const + ConstGradingHueCurveOpDataRcPtr hueCurveData() const { - return DynamicPtrCast(data()); + return DynamicPtrCast(data()); } - HueCurveOpDataRcPtr hueCurveData() + GradingHueCurveOpDataRcPtr hueCurveData() { - return DynamicPtrCast(data()); + return DynamicPtrCast(data()); } }; -HueCurveOp::HueCurveOp(HueCurveOpDataRcPtr & hueCurveData) +GradingHueCurveOp::GradingHueCurveOp(GradingHueCurveOpDataRcPtr & hueCurveData) : Op() { data() = hueCurveData; } -OpRcPtr HueCurveOp::clone() const +OpRcPtr GradingHueCurveOp::clone() const { - HueCurveOpDataRcPtr p = hueCurveData()->clone(); - return std::make_shared(p); + GradingHueCurveOpDataRcPtr p = hueCurveData()->clone(); + return std::make_shared(p); } -HueCurveOp::~HueCurveOp() +GradingHueCurveOp::~GradingHueCurveOp() { } -std::string HueCurveOp::getInfo() const +std::string GradingHueCurveOp::getInfo() const { - return ""; + return ""; } -bool HueCurveOp::isIdentity() const +bool GradingHueCurveOp::isIdentity() const { return hueCurveData()->isIdentity(); } -bool HueCurveOp::isSameType(ConstOpRcPtr & op) const +bool GradingHueCurveOp::isSameType(ConstOpRcPtr & op) const { - ConstHueCurveOpRcPtr typedRcPtr = DynamicPtrCast(op); + ConstGradingHueCurveOpRcPtr typedRcPtr = DynamicPtrCast(op); return (bool)typedRcPtr; } -bool HueCurveOp::isInverse(ConstOpRcPtr & op) const +bool GradingHueCurveOp::isInverse(ConstOpRcPtr & op) const { - ConstHueCurveOpRcPtr typedRcPtr = DynamicPtrCast(op); + ConstGradingHueCurveOpRcPtr typedRcPtr = DynamicPtrCast(op); if (!typedRcPtr) return false; - ConstHueCurveOpDataRcPtr hueOpData = typedRcPtr->hueCurveData(); + ConstGradingHueCurveOpDataRcPtr hueOpData = typedRcPtr->hueCurveData(); return hueCurveData()->isInverse(hueOpData); } -bool HueCurveOp::canCombineWith(ConstOpRcPtr & /*op*/) const +bool GradingHueCurveOp::canCombineWith(ConstOpRcPtr & /*op*/) const { return false; } -void HueCurveOp::combineWith(OpRcPtrVec & /*ops*/, ConstOpRcPtr & secondOp) const +void GradingHueCurveOp::combineWith(OpRcPtrVec & /*ops*/, ConstOpRcPtr & secondOp) const { if (!canCombineWith(secondOp)) { - throw Exception("HueCurveOp: canCombineWith must be checked " + throw Exception("GradingHueCurveOp: canCombineWith must be checked " "before calling combineWith."); } } -std::string HueCurveOp::getCacheID() const +std::string GradingHueCurveOp::getCacheID() const { // Create the cacheID. std::ostringstream cacheIDStream; - cacheIDStream << "getCacheID(); cacheIDStream << ">"; return cacheIDStream.str(); } -bool HueCurveOp::isDynamic() const +bool GradingHueCurveOp::isDynamic() const { return hueCurveData()->isDynamic(); } -bool HueCurveOp::hasDynamicProperty(DynamicPropertyType type) const +bool GradingHueCurveOp::hasDynamicProperty(DynamicPropertyType type) const { - if (type != DYNAMIC_PROPERTY_HUE_CURVE) + if (type != DYNAMIC_PROPERTY_GRADING_HUECURVE) { return false; } @@ -147,9 +146,9 @@ bool HueCurveOp::hasDynamicProperty(DynamicPropertyType type) const return hueCurveData()->isDynamic(); } -DynamicPropertyRcPtr HueCurveOp::getDynamicProperty(DynamicPropertyType type) const +DynamicPropertyRcPtr GradingHueCurveOp::getDynamicProperty(DynamicPropertyType type) const { - if (type != DYNAMIC_PROPERTY_HUE_CURVE) + if (type != DYNAMIC_PROPERTY_GRADING_HUECURVE) { throw Exception("Dynamic property type not supported by hue curve op."); } @@ -161,10 +160,10 @@ DynamicPropertyRcPtr HueCurveOp::getDynamicProperty(DynamicPropertyType type) co return hueCurveData()->getDynamicProperty(); } -void HueCurveOp::replaceDynamicProperty(DynamicPropertyType type, - DynamicPropertyGradingRGBCurveImplRcPtr & prop) +void GradingHueCurveOp::replaceDynamicProperty(DynamicPropertyType type, + DynamicPropertyGradingHueCurveImplRcPtr & prop) { - if (type != DYNAMIC_PROPERTY_HUE_CURVE) + if (type != DYNAMIC_PROPERTY_GRADING_HUECURVE) { throw Exception("Dynamic property type not supported by hue curve op."); } @@ -172,7 +171,7 @@ void HueCurveOp::replaceDynamicProperty(DynamicPropertyType type, { throw Exception("Hue curve property is not dynamic."); } - auto propGC = OCIO_DYNAMIC_POINTER_CAST(prop); + auto propGC = OCIO_DYNAMIC_POINTER_CAST(prop); if (!propGC) { throw Exception("Dynamic property type not supported by hue curve op."); @@ -181,12 +180,12 @@ void HueCurveOp::replaceDynamicProperty(DynamicPropertyType type, hueCurveData()->replaceDynamicProperty(propGC); } -void HueCurveOp::removeDynamicProperties() +void GradingHueCurveOp::removeDynamicProperties() { hueCurveData()->removeDynamicProperty(); } -ConstOpCPURcPtr HueCurveOp::getCPUOp(bool /*fastLogExpPow*/) const +ConstOpCPURcPtr GradingHueCurveOp::getCPUOp(bool /*fastLogExpPow*/) const { // TODO: Add CPU renderer before merging back the adsk fork //ConstGradingRGBCurveOpDataRcPtr data = rgbCurveData(); @@ -194,9 +193,9 @@ ConstOpCPURcPtr HueCurveOp::getCPUOp(bool /*fastLogExpPow*/) const return ConstOpCPURcPtr(); } -void HueCurveOp::extractGpuShaderInfo(GpuShaderCreatorRcPtr & shaderCreator) const +void GradingHueCurveOp::extractGpuShaderInfo(GpuShaderCreatorRcPtr & shaderCreator) const { - ConstHueCurveOpDataRcPtr data = hueCurveData(); + ConstGradingHueCurveOpDataRcPtr data = hueCurveData(); GetHueCurveGPUShaderProgram(shaderCreator, data); } @@ -209,8 +208,8 @@ void HueCurveOp::extractGpuShaderInfo(GpuShaderCreatorRcPtr & shaderCreator) con /////////////////////////////////////////////////////////////////////////// -void CreateHueCurveOp(OpRcPtrVec & ops, - HueCurveOpDataRcPtr & curveData, +void CreateGradingHueCurveOp(OpRcPtrVec & ops, + GradingHueCurveOpDataRcPtr & curveData, TransformDirection direction) { auto curve = curveData; @@ -219,21 +218,21 @@ void CreateHueCurveOp(OpRcPtrVec & ops, curve = curve->inverse(); } - ops.push_back(std::make_shared(curve)); + ops.push_back(std::make_shared(curve)); } /////////////////////////////////////////////////////////////////////////// -void CreateHueCurveTransform(GroupTransformRcPtr & group, ConstOpRcPtr & op) +void CreateGradingHueCurveTransform(GroupTransformRcPtr & group, ConstOpRcPtr & op) { - auto gc = DynamicPtrCast(op); + auto gc = DynamicPtrCast(op); if (!gc) { - throw Exception("CreateHueCurveTransform: op has to be a HueCurveOp."); + throw Exception("CreateGradingHueCurveTransform: op has to be a GradingHueCurveOp."); } - auto gcData = DynamicPtrCast(op->data()); - auto gcTransform = HueCurveTransform::Create(gcData->getStyle()); - auto & data = dynamic_cast(gcTransform.get())->data(); + auto gcData = DynamicPtrCast(op->data()); + auto gcTransform = GradingHueCurveTransform::Create(gcData->getStyle()); + auto & data = dynamic_cast(gcTransform.get())->data(); data = *gcData; group->appendTransform(gcTransform); @@ -242,14 +241,14 @@ void CreateHueCurveTransform(GroupTransformRcPtr & group, ConstOpRcPtr & op) void BuildHueCurveOp(OpRcPtrVec & ops, const Config & /*config*/, const ConstContextRcPtr & /*context*/, - const HueCurveTransform & transform, + const GradingHueCurveTransform & transform, TransformDirection dir) { - const auto & data = dynamic_cast(transform).data(); + const auto & data = dynamic_cast(transform).data(); data.validate(); auto curveData = data.clone(); - CreateHueCurveOp(ops, curveData, dir); + CreateGradingHueCurveOp(ops, curveData, dir); } } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.h index 4070cc6193..02181e0b54 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.h @@ -17,13 +17,13 @@ namespace OCIO_NAMESPACE { -void CreateHueCurveOp(OpRcPtrVec & ops, - HueCurveOpDataRcPtr & gpData, +void CreateGradingHueCurveOp(OpRcPtrVec & ops, + GradingHueCurveOpDataRcPtr & gpData, TransformDirection direction); -// Create a copy of the rgb curve transform in the op and append it to +// Create a copy of the hue curve transform in the op and append it to // the GroupTransform. -void CreateHueCurveTransform(GroupTransformRcPtr & group, ConstOpRcPtr & op); +void CreateGradingHueCurveTransform(GroupTransformRcPtr & group, ConstOpRcPtr & op); } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp index 7edece3a64..1f82b61d27 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp @@ -7,7 +7,6 @@ #include "ops/gradinghuecurve/GradingHueCurve.h" #include "ops/gradinghuecurve/GradingHueCurveOpData.h" -#include "ops/range/RangeOpData.h" #include "Platform.h" namespace OCIO_NAMESPACE @@ -18,41 +17,41 @@ namespace DefaultValues const std::streamsize FLOAT_DECIMALS = 7; } -HueCurveOpData::HueCurveOpData(GradingStyle style) +GradingHueCurveOpData::GradingHueCurveOpData(GradingStyle style) : OpData() , m_style(style) { ConstGradingHueCurveRcPtr hueCurve = GradingHueCurve::Create(style); - m_value = std::make_shared(hueCurve, false); + m_value = std::make_shared(hueCurve, false); } -HueCurveOpData::HueCurveOpData(const HueCurveOpData & rhs) +GradingHueCurveOpData::GradingHueCurveOpData(const GradingHueCurveOpData & rhs) : OpData(rhs) , m_style(rhs.m_style) { ConstGradingHueCurveRcPtr hueCurve = GradingHueCurve::Create(rhs.m_style); - m_value = std::make_shared(hueCurve, false); + m_value = std::make_shared(hueCurve, false); *this = rhs; } -HueCurveOpData::HueCurveOpData(GradingStyle style, +GradingHueCurveOpData::GradingHueCurveOpData(GradingStyle style, const GradingHueCurves & curves) : OpData() , m_style(style) { ConstGradingHueCurveRcPtr hueCurve = GradingHueCurve::Create(curves); - m_value = std::make_shared(hueCurve, false); + m_value = std::make_shared(hueCurve, false); } -HueCurveOpData & HueCurveOpData::operator=(const HueCurveOpData & rhs) +GradingHueCurveOpData & GradingHueCurveOpData::operator=(const GradingHueCurveOpData & rhs) { if (this == &rhs) return *this; OpData::operator=(rhs); m_direction = rhs.m_direction; - m_style = rhs.m_style; + m_style = rhs.m_style; m_bypassLinToLog = rhs.m_bypassLinToLog; // Copy dynamic properties. Sharing happens when needed, with CPUOp for instance. @@ -65,35 +64,35 @@ HueCurveOpData & HueCurveOpData::operator=(const HueCurveOpData & rhs) return *this; } -HueCurveOpData::~HueCurveOpData() +GradingHueCurveOpData::~GradingHueCurveOpData() { } -HueCurveOpDataRcPtr HueCurveOpData::clone() const +GradingHueCurveOpDataRcPtr GradingHueCurveOpData::clone() const { - return std::make_shared(*this); + return std::make_shared(*this); } -void HueCurveOpData::validate() const +void GradingHueCurveOpData::validate() const { // This should already be valid. m_value->getValue()->validate(); return; } -bool HueCurveOpData::isNoOp() const +bool GradingHueCurveOpData::isNoOp() const { return isIdentity(); } -bool HueCurveOpData::isIdentity() const +bool GradingHueCurveOpData::isIdentity() const { if (isDynamic()) return false; return m_value->getValue()->isIdentity(); } -bool HueCurveOpData::isInverse(ConstHueCurveOpDataRcPtr & r) const +bool GradingHueCurveOpData::isInverse(ConstGradingHueCurveOpDataRcPtr & r) const { if (isDynamic() || r->isDynamic()) { @@ -112,14 +111,14 @@ bool HueCurveOpData::isInverse(ConstHueCurveOpDataRcPtr & r) const return false; } -HueCurveOpDataRcPtr HueCurveOpData::inverse() const +GradingHueCurveOpDataRcPtr GradingHueCurveOpData::inverse() const { auto res = clone(); res->m_direction = GetInverseTransformDirection(m_direction); return res; } -std::string HueCurveOpData::getCacheID() const +std::string GradingHueCurveOpData::getCacheID() const { AutoMutex lock(m_mutex); @@ -144,7 +143,7 @@ std::string HueCurveOpData::getCacheID() const return cacheIDStream.str(); } -void HueCurveOpData::setStyle(GradingStyle style) noexcept +void GradingHueCurveOpData::setStyle(GradingStyle style) noexcept { if (style != m_style) { @@ -155,13 +154,13 @@ void HueCurveOpData::setStyle(GradingStyle style) noexcept } } -float HueCurveOpData::getSlope(HueCurveType c, size_t index) const +float GradingHueCurveOpData::getSlope(HueCurveType c, size_t index) const { ConstGradingBSplineCurveRcPtr curve = m_value->getValue()->getCurve(c); return curve->getSlope(index); } -void HueCurveOpData::setSlope(HueCurveType c, size_t index, float slope) +void GradingHueCurveOpData::setSlope(HueCurveType c, size_t index, float slope) { GradingHueCurveRcPtr hueCurve( m_value->getValue()->createEditableCopy() ); GradingBSplineCurveRcPtr curve = hueCurve->getCurve(c); @@ -169,47 +168,47 @@ void HueCurveOpData::setSlope(HueCurveType c, size_t index, float slope) m_value->setValue(hueCurve); } -bool HueCurveOpData::slopesAreDefault(HueCurveType c) const +bool GradingHueCurveOpData::slopesAreDefault(HueCurveType c) const { ConstGradingBSplineCurveRcPtr curve = m_value->getValue()->getCurve(c); return curve->slopesAreDefault(); } -TransformDirection HueCurveOpData::getDirection() const noexcept +TransformDirection GradingHueCurveOpData::getDirection() const noexcept { return m_direction; } -void HueCurveOpData::setDirection(TransformDirection dir) noexcept +void GradingHueCurveOpData::setDirection(TransformDirection dir) noexcept { m_direction = dir; } -bool HueCurveOpData::isDynamic() const noexcept +bool GradingHueCurveOpData::isDynamic() const noexcept { return m_value->isDynamic(); } -DynamicPropertyRcPtr HueCurveOpData::getDynamicProperty() const noexcept +DynamicPropertyRcPtr GradingHueCurveOpData::getDynamicProperty() const noexcept { return m_value; } -void HueCurveOpData::replaceDynamicProperty(DynamicPropertyHueCurveImplRcPtr prop) noexcept +void GradingHueCurveOpData::replaceDynamicProperty(DynamicPropertyGradingHueCurveImplRcPtr prop) noexcept { m_value = prop; } -void HueCurveOpData::removeDynamicProperty() noexcept +void GradingHueCurveOpData::removeDynamicProperty() noexcept { m_value->makeNonDynamic(); } -bool HueCurveOpData::equals(const OpData & other) const +bool GradingHueCurveOpData::equals(const OpData & other) const { if (!OpData::equals(other)) return false; - const HueCurveOpData* rop = static_cast(&other); + const GradingHueCurveOpData* rop = static_cast(&other); if (m_direction != rop->m_direction || m_style != rop->m_style || @@ -222,7 +221,7 @@ bool HueCurveOpData::equals(const OpData & other) const return true; } -bool operator==(const HueCurveOpData & lhs, const HueCurveOpData & rhs) +bool operator==(const GradingHueCurveOpData & lhs, const GradingHueCurveOpData & rhs) { return lhs.equals(rhs); } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h index 1aa458ac9e..d8df8f65a7 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h @@ -14,23 +14,23 @@ namespace OCIO_NAMESPACE { -class HueCurveOpData; -typedef OCIO_SHARED_PTR HueCurveOpDataRcPtr; -typedef OCIO_SHARED_PTR ConstHueCurveOpDataRcPtr; +class GradingHueCurveOpData; +typedef OCIO_SHARED_PTR GradingHueCurveOpDataRcPtr; +typedef OCIO_SHARED_PTR ConstGradingHueCurveOpDataRcPtr; -class HueCurveOpData : public OpData +class GradingHueCurveOpData : public OpData { public: - HueCurveOpData(GradingStyle style); - HueCurveOpData(const HueCurveOpData & rhs); - HueCurveOpData(GradingStyle style, + GradingHueCurveOpData(GradingStyle style); + GradingHueCurveOpData(const GradingHueCurveOpData & rhs); + GradingHueCurveOpData(GradingStyle style, const GradingHueCurves & curve); - HueCurveOpData & operator=(const HueCurveOpData & rhs); - virtual ~HueCurveOpData(); + GradingHueCurveOpData & operator=(const GradingHueCurveOpData & rhs); + virtual ~GradingHueCurveOpData(); - HueCurveOpDataRcPtr clone() const; + GradingHueCurveOpDataRcPtr clone() const; void validate() const override; @@ -41,8 +41,8 @@ class HueCurveOpData : public OpData bool hasChannelCrosstalk() const override { return false; } - bool isInverse(ConstHueCurveOpDataRcPtr & r) const; - HueCurveOpDataRcPtr inverse() const; + bool isInverse(ConstGradingHueCurveOpDataRcPtr & r) const; + GradingHueCurveOpDataRcPtr inverse() const; std::string getCacheID() const override; @@ -64,10 +64,10 @@ class HueCurveOpData : public OpData bool isDynamic() const noexcept; DynamicPropertyRcPtr getDynamicProperty() const noexcept; - void replaceDynamicProperty(DynamicPropertyHueCurveImplRcPtr prop) noexcept; + void replaceDynamicProperty(DynamicPropertyGradingHueCurveImplRcPtr prop) noexcept; void removeDynamicProperty() noexcept; - DynamicPropertyHueCurveImplRcPtr getDynamicPropertyInternal() const noexcept + DynamicPropertyGradingHueCurveImplRcPtr getDynamicPropertyInternal() const noexcept { return m_value; } @@ -76,12 +76,12 @@ class HueCurveOpData : public OpData private: GradingStyle m_style; - DynamicPropertyHueCurveImplRcPtr m_value; + DynamicPropertyGradingHueCurveImplRcPtr m_value; bool m_bypassLinToLog{ false }; TransformDirection m_direction{ TRANSFORM_DIR_FORWARD }; }; -bool operator==(const HueCurveOpData & lhs, const HueCurveOpData & rhs); +bool operator==(const GradingHueCurveOpData & lhs, const GradingHueCurveOpData & rhs); } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp index 957fbed755..7fea51c815 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp @@ -138,7 +138,7 @@ std::string BuildResourceNameIndexed(GpuShaderCreatorRcPtr & shaderCreator, cons return name; } -static const std::string opPrefix{ "huecurve" }; +static const std::string opPrefix{ "grading_huecurve" }; void SetGCProperties(GpuShaderCreatorRcPtr & shaderCreator, bool dynamic, GCProperties & propNames) { @@ -175,7 +175,7 @@ void SetGCProperties(GpuShaderCreatorRcPtr & shaderCreator, bool dynamic, GCProp // Only called once for dynamic ops. void AddGCPropertiesUniforms(GpuShaderCreatorRcPtr & shaderCreator, - DynamicPropertyHueCurveImplRcPtr & shaderProp, + DynamicPropertyGradingHueCurveImplRcPtr & shaderProp, const GCProperties & propNames) { // Use the shader dynamic property to bind the uniforms. @@ -184,31 +184,31 @@ void AddGCPropertiesUniforms(GpuShaderCreatorRcPtr & shaderCreator, // Note: No need to add an index to the name to avoid collisions as the dynamic properties // are unique. - auto getNK = std::bind(&DynamicPropertyHueCurveImpl::getNumKnots, curveProp); - auto getKO = std::bind(&DynamicPropertyHueCurveImpl::getKnotsOffsetsArray, + auto getNK = std::bind(&DynamicPropertyGradingHueCurveImpl::getNumKnots, curveProp); + auto getKO = std::bind(&DynamicPropertyGradingHueCurveImpl::getKnotsOffsetsArray, curveProp); - auto getK = std::bind(&DynamicPropertyHueCurveImpl::getKnotsArray, curveProp); - auto getNC = std::bind(&DynamicPropertyHueCurveImpl::getNumCoefs, curveProp); - auto getCO = std::bind(&DynamicPropertyHueCurveImpl::getCoefsOffsetsArray, + auto getK = std::bind(&DynamicPropertyGradingHueCurveImpl::getKnotsArray, curveProp); + auto getNC = std::bind(&DynamicPropertyGradingHueCurveImpl::getNumCoefs, curveProp); + auto getCO = std::bind(&DynamicPropertyGradingHueCurveImpl::getCoefsOffsetsArray, curveProp); - auto getC = std::bind(&DynamicPropertyHueCurveImpl::getCoefsArray, curveProp); - auto getLB = std::bind(&DynamicPropertyHueCurveImpl::getLocalBypass, curveProp); + auto getC = std::bind(&DynamicPropertyGradingHueCurveImpl::getCoefsArray, curveProp); + auto getLB = std::bind(&DynamicPropertyGradingHueCurveImpl::getLocalBypass, curveProp); // Uniforms are added if they are not already there (added by another op). - AddUniform(shaderCreator, DynamicPropertyHueCurveImpl::GetNumOffsetValues, + AddUniform(shaderCreator, DynamicPropertyGradingHueCurveImpl::GetNumOffsetValues, getKO, propNames.m_knotsOffsets); AddUniform(shaderCreator, getNK, getK, - DynamicPropertyHueCurveImpl::GetMaxKnots(), + DynamicPropertyGradingHueCurveImpl::GetMaxKnots(), propNames.m_knots); - AddUniform(shaderCreator, DynamicPropertyHueCurveImpl::GetNumOffsetValues, + AddUniform(shaderCreator, DynamicPropertyGradingHueCurveImpl::GetNumOffsetValues, getCO, propNames.m_coefsOffsets); AddUniform(shaderCreator, getNC, getC, - DynamicPropertyHueCurveImpl::GetMaxCoefs(), + DynamicPropertyGradingHueCurveImpl::GetMaxCoefs(), propNames.m_coefs); AddUniform(shaderCreator, getLB, propNames.m_localBypass); } void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, - ConstHueCurveOpDataRcPtr & gcData, + ConstGradingHueCurveOpDataRcPtr & gcData, const GCProperties & props, bool dyn) { @@ -415,7 +415,7 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, } void GetHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, - ConstHueCurveOpDataRcPtr & gcData) + ConstGradingHueCurveOpDataRcPtr & gcData) { const bool dyn = gcData->isDynamic() && shaderCreator->getLanguage() != LANGUAGE_OSL_1; if (!dyn) diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h index f56734948f..217e3f30b2 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h @@ -13,7 +13,7 @@ namespace OCIO_NAMESPACE { void GetHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, - ConstHueCurveOpDataRcPtr & gpData); + ConstGradingHueCurveOpDataRcPtr & gpData); } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index 726374b625..5ba6fff574 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -412,9 +412,9 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsBSpline(KnotsCoefs & knotsCoef FitSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); } - const int numKnots = static_cast(knotsCoefs.m_nKnots); + const int numKnots = static_cast(knotsCoefs.m_numKnots); const int newKnots = static_cast(knots.size()); - const int numCoefs = static_cast(knotsCoefs.m_nCoefs); + const int numCoefs = static_cast(knotsCoefs.m_numCoefs); const int newCoefs = static_cast(coefsA.size() * 3); if (numKnots + newKnots > KnotsCoefs::MAX_NUM_KNOTS || @@ -434,8 +434,8 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsBSpline(KnotsCoefs & knotsCoef std::copy(coefsB.begin(), coefsB.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize); std::copy(coefsC.begin(), coefsC.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize * 2); - knotsCoefs.m_nKnots += newKnots; - knotsCoefs.m_nCoefs += newCoefs; + knotsCoefs.m_numKnots += newKnots; + knotsCoefs.m_numCoefs += newCoefs; } } @@ -795,9 +795,9 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsHueCurves(KnotsCoefs & knotsCo std::vector coefsC; fitHueSpline(resultCtrlPnts, slopes, knots, coefsA, coefsB, coefsC); - const int numKnots = static_cast(knotsCoefs.m_nKnots); + const int numKnots = static_cast(knotsCoefs.m_numKnots); const int newKnots = static_cast(knots.size()); - const int numCoefs = static_cast(knotsCoefs.m_nCoefs); + const int numCoefs = static_cast(knotsCoefs.m_numCoefs); const int newCoefs = static_cast(coefsA.size() * 3); if (numKnots + newKnots > KnotsCoefs::MAX_NUM_KNOTS || @@ -817,8 +817,8 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsHueCurves(KnotsCoefs & knotsCo std::copy(coefsB.begin(), coefsB.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize); std::copy(coefsC.begin(), coefsC.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize * 2); - knotsCoefs.m_nKnots += newKnots; - knotsCoefs.m_nCoefs += newCoefs; + knotsCoefs.m_numKnots += newKnots; + knotsCoefs.m_numCoefs += newCoefs; } diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h index bb75e80cbf..48940f971b 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h @@ -107,8 +107,8 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve std::vector m_coefsArray; // Contains packed coefs of ALL curves. std::vector m_knotsArray; // Contains packed knots of ALL curves. - int m_nCoefs = 0; - int m_nKnots = 0; + int m_numCoefs = 0; + int m_numKnots = 0; float evalCurve(int curveIdx, float x) const; float evalCurveRev(int curveIdx, float x) const; diff --git a/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp b/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp new file mode 100644 index 0000000000..cf0d79c9ed --- /dev/null +++ b/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#include + +#include + +#include "transforms/GradingHueCurveTransform.h" + +namespace OCIO_NAMESPACE +{ + +GradingHueCurveTransformRcPtr GradingHueCurveTransform::Create(GradingStyle style) +{ + return GradingHueCurveTransformRcPtr(new GradingHueCurveTransformImpl(style), + &GradingHueCurveTransformImpl::deleter); +} + +GradingHueCurveTransformImpl::GradingHueCurveTransformImpl(GradingStyle style) : + m_data(style) +{ +} + +void GradingHueCurveTransformImpl::deleter(GradingHueCurveTransform* t) +{ + delete static_cast(t); +} + +TransformRcPtr GradingHueCurveTransformImpl::createEditableCopy() const +{ + GradingHueCurveTransformRcPtr transform = GradingHueCurveTransform::Create(getStyle()); + dynamic_cast(transform.get())->data() = data(); + return transform; +} + +TransformDirection GradingHueCurveTransformImpl::getDirection() const noexcept +{ + return data().getDirection(); +} + +void GradingHueCurveTransformImpl::setDirection(TransformDirection dir) noexcept +{ + data().setDirection(dir); +} + +void GradingHueCurveTransformImpl::validate() const +{ + try + { + Transform::validate(); + data().validate(); + } + catch(Exception & ex) + { + std::string errMsg("GradingHueCurveTransform validation failed: "); + errMsg += ex.what(); + throw Exception(errMsg.c_str()); + } + + return; +} + +FormatMetadata & GradingHueCurveTransformImpl::getFormatMetadata() noexcept +{ + return data().getFormatMetadata(); +} + +const FormatMetadata & GradingHueCurveTransformImpl::getFormatMetadata() const noexcept +{ + return data().getFormatMetadata(); +} + +bool GradingHueCurveTransformImpl::equals(const GradingHueCurveTransform & other) const noexcept +{ + if (this == &other) return true; + return data() == dynamic_cast(&other)->data(); +} + +GradingStyle GradingHueCurveTransformImpl::getStyle() const noexcept +{ + return data().getStyle(); +} + +void GradingHueCurveTransformImpl::setStyle(GradingStyle style) noexcept +{ + data().setStyle(style); +} + +const ConstGradingHueCurveRcPtr GradingHueCurveTransformImpl::getValue() const +{ + return data().getValue(); +} + +void GradingHueCurveTransformImpl::setValue(const ConstGradingHueCurveRcPtr & values) +{ + data().setValue(values); +} + +float GradingHueCurveTransformImpl::getSlope(HueCurveType c, size_t index) const +{ + return data().getSlope(c, index); +} + +void GradingHueCurveTransformImpl::setSlope(HueCurveType c, size_t index, float slope) +{ + data().setSlope(c, index, slope); +} + +bool GradingHueCurveTransformImpl::slopesAreDefault(HueCurveType c) const +{ + return data().slopesAreDefault(c); +} + +bool GradingHueCurveTransformImpl::getBypassLinToLog() const noexcept +{ + return data().getBypassLinToLog(); +} + +void GradingHueCurveTransformImpl::setBypassLinToLog(bool bypass) noexcept +{ + data().setBypassLinToLog(bypass); +} + +bool GradingHueCurveTransformImpl::isDynamic() const noexcept +{ + return data().isDynamic(); +} + +void GradingHueCurveTransformImpl::makeDynamic() noexcept +{ + data().getDynamicPropertyInternal()->makeDynamic(); +} + +void GradingHueCurveTransformImpl::makeNonDynamic() noexcept +{ + data().getDynamicPropertyInternal()->makeNonDynamic(); +} + +std::ostream& operator<< (std::ostream & os, const GradingHueCurveTransform & t) noexcept +{ + os << ""; + return os; +} + +std::ostream & operator<<(std::ostream & os, const GradingHueCurve & hueCurve) +{ + os << ""; + + return os; +} + +} // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/transforms/HueCurveTransform.h b/src/OpenColorIO/transforms/GradingHueCurveTransform.h similarity index 67% rename from src/OpenColorIO/transforms/HueCurveTransform.h rename to src/OpenColorIO/transforms/GradingHueCurveTransform.h index 4a8ba3c8a8..ce9cd67117 100644 --- a/src/OpenColorIO/transforms/HueCurveTransform.h +++ b/src/OpenColorIO/transforms/GradingHueCurveTransform.h @@ -13,14 +13,14 @@ namespace OCIO_NAMESPACE { -class HueCurveTransformImpl : public HueCurveTransform +class GradingHueCurveTransformImpl : public GradingHueCurveTransform { public: - HueCurveTransformImpl(GradingStyle style); - HueCurveTransformImpl() = delete; - HueCurveTransformImpl(const HueCurveTransformImpl &) = delete; - HueCurveTransformImpl& operator=(const HueCurveTransformImpl &) = delete; - ~HueCurveTransformImpl() override = default; + GradingHueCurveTransformImpl(GradingStyle style); + GradingHueCurveTransformImpl() = delete; + GradingHueCurveTransformImpl(const GradingHueCurveTransformImpl &) = delete; + GradingHueCurveTransformImpl& operator=(const GradingHueCurveTransformImpl &) = delete; + ~GradingHueCurveTransformImpl() override = default; TransformRcPtr createEditableCopy() const override; @@ -32,7 +32,7 @@ class HueCurveTransformImpl : public HueCurveTransform void validate() const override; - bool equals(const HueCurveTransform & other) const noexcept override; + bool equals(const GradingHueCurveTransform & other) const noexcept override; GradingStyle getStyle() const noexcept override; @@ -53,13 +53,13 @@ class HueCurveTransformImpl : public HueCurveTransform void makeDynamic() noexcept override; void makeNonDynamic() noexcept override; - HueCurveOpData & data() noexcept { return m_data; } - const HueCurveOpData & data() const noexcept { return m_data; } + GradingHueCurveOpData & data() noexcept { return m_data; } + const GradingHueCurveOpData & data() const noexcept { return m_data; } - static void deleter(HueCurveTransform* t); + static void deleter(GradingHueCurveTransform* t); private: - HueCurveOpData m_data; + GradingHueCurveOpData m_data; }; diff --git a/src/OpenColorIO/transforms/HueCurveTransform.cpp b/src/OpenColorIO/transforms/HueCurveTransform.cpp deleted file mode 100644 index cea323b444..0000000000 --- a/src/OpenColorIO/transforms/HueCurveTransform.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright Contributors to the OpenColorIO Project. - -#include - -#include - -#include "transforms/HueCurveTransform.h" - -namespace OCIO_NAMESPACE -{ - -HueCurveTransformRcPtr HueCurveTransform::Create(GradingStyle style) -{ - return HueCurveTransformRcPtr(new HueCurveTransformImpl(style), - &HueCurveTransformImpl::deleter); -} - -HueCurveTransformImpl::HueCurveTransformImpl(GradingStyle style) : - m_data(style) -{ -} - -void HueCurveTransformImpl::deleter(HueCurveTransform* t) -{ - delete static_cast(t); -} - -TransformRcPtr HueCurveTransformImpl::createEditableCopy() const -{ - HueCurveTransformRcPtr transform = HueCurveTransform::Create(getStyle()); - dynamic_cast(transform.get())->data() = data(); - return transform; -} - -TransformDirection HueCurveTransformImpl::getDirection() const noexcept -{ - return data().getDirection(); -} - -void HueCurveTransformImpl::setDirection(TransformDirection dir) noexcept -{ - data().setDirection(dir); -} - -void HueCurveTransformImpl::validate() const -{ - try - { - Transform::validate(); - data().validate(); - } - catch(Exception & ex) - { - std::string errMsg("HueCurveTransform validation failed: "); - errMsg += ex.what(); - throw Exception(errMsg.c_str()); - } - - return; -} - -FormatMetadata & HueCurveTransformImpl::getFormatMetadata() noexcept -{ - return data().getFormatMetadata(); -} - -const FormatMetadata & HueCurveTransformImpl::getFormatMetadata() const noexcept -{ - return data().getFormatMetadata(); -} - -bool HueCurveTransformImpl::equals(const HueCurveTransform & other) const noexcept -{ - if (this == &other) return true; - return data() == dynamic_cast(&other)->data(); -} - -GradingStyle HueCurveTransformImpl::getStyle() const noexcept -{ - return data().getStyle(); -} - -void HueCurveTransformImpl::setStyle(GradingStyle style) noexcept -{ - data().setStyle(style); -} - -const ConstGradingHueCurveRcPtr HueCurveTransformImpl::getValue() const -{ - return data().getValue(); -} - -void HueCurveTransformImpl::setValue(const ConstGradingHueCurveRcPtr & values) -{ - data().setValue(values); -} - -float HueCurveTransformImpl::getSlope(HueCurveType c, size_t index) const -{ - return data().getSlope(c, index); -} - -void HueCurveTransformImpl::setSlope(HueCurveType c, size_t index, float slope) -{ - data().setSlope(c, index, slope); -} - -bool HueCurveTransformImpl::slopesAreDefault(HueCurveType c) const -{ - return data().slopesAreDefault(c); -} - -bool HueCurveTransformImpl::getBypassLinToLog() const noexcept -{ - return data().getBypassLinToLog(); -} - -void HueCurveTransformImpl::setBypassLinToLog(bool bypass) noexcept -{ - data().setBypassLinToLog(bypass); -} - -bool HueCurveTransformImpl::isDynamic() const noexcept -{ - return data().isDynamic(); -} - -void HueCurveTransformImpl::makeDynamic() noexcept -{ - data().getDynamicPropertyInternal()->makeDynamic(); -} - -void HueCurveTransformImpl::makeNonDynamic() noexcept -{ - data().getDynamicPropertyInternal()->makeNonDynamic(); -} - -std::ostream& operator<< (std::ostream & os, const HueCurveTransform & t) noexcept -{ - os << ""; - return os; -} - -std::ostream & operator<<(std::ostream & os, const GradingHueCurve & hueCurve) -{ - os << ""; - - return os; -} - -} // namespace OCIO_NAMESPACE diff --git a/tests/cpu/CMakeLists.txt b/tests/cpu/CMakeLists.txt index f44c81a9be..baf7245636 100755 --- a/tests/cpu/CMakeLists.txt +++ b/tests/cpu/CMakeLists.txt @@ -151,9 +151,9 @@ set(SOURCES ops/fixedfunction/ACES2/Transform.cpp ops/fixedfunction/FixedFunctionOpGPU.cpp ops/gamma/GammaOpGPU.cpp + ops/gradinghuecurve/GradingHueCurveOpGPU.cpp ops/gradingprimary/GradingPrimaryOpGPU.cpp ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp - ops/gradinghuecurve/GradingHueCurveOpGPU.cpp ops/gradingtone/GradingToneOpGPU.cpp ops/log/LogOpGPU.cpp ops/lut1d/Lut1DOpCPU_SSE2.cpp @@ -254,11 +254,11 @@ set(TESTS ops/gradingprimary/GradingPrimaryOpData_tests.cpp ops/gradingprimary/GradingPrimaryOp_tests.cpp ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp + ops/gradinghuecurve/GradingHueCurve_tests.cpp + ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp + ops/gradinghuecurve/GradingHueCurveOp_tests.cpp ops/gradingrgbcurve/GradingRGBCurve_tests.cpp - ops/gradingrgbcurve/HueCurve_tests.cpp ops/gradingrgbcurve/GradingRGBCurveOpCPU_tests.cpp - ops/gradingrgbcurve/HueCurveOp_tests.cpp - ops/gradingrgbcurve/HueCurveOpData_tests.cpp ops/gradingrgbcurve/GradingRGBCurveOpData_tests.cpp ops/gradingrgbcurve/GradingRGBCurveOp_tests.cpp ops/gradingtone/GradingTone_tests.cpp diff --git a/tests/cpu/ops/gradingrgbcurve/HueCurveOpData_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp similarity index 95% rename from tests/cpu/ops/gradingrgbcurve/HueCurveOpData_tests.cpp rename to tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp index a93f3bd77f..e04298f6d1 100644 --- a/tests/cpu/ops/gradingrgbcurve/HueCurveOpData_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp @@ -8,10 +8,10 @@ namespace OCIO = OCIO_NAMESPACE; -OCIO_ADD_TEST(HueCurveOpData, accessors) +OCIO_ADD_TEST(GradingHueCurveOpData, accessors) { // Create GradingRGBCurveOpData and check values. Changes them and check. - //OCIO::HueCurveOpData gc; + //OCIO::GradingHueCurveOpData gc; //static constexpr char expected[]{ "log forward " // "]>, " @@ -52,8 +52,8 @@ OCIO_ADD_TEST(HueCurveOpData, accessors) //OCIO_CHECK_EQUAL(gc.getDirection(), OCIO::TRANSFORM_DIR_INVERSE); // Test operator==. - //OCIO::HueCurveOpData gc1{}; - //OCIO::HueCurveOpData gc2{}; + //OCIO::GradingHueCurveOpData gc1{}; + //OCIO::GradingHueCurveOpData gc2{}; // //OCIO_CHECK_ASSERT(gc1 == gc2); //gc1.setDirection(OCIO::TRANSFORM_DIR_INVERSE); @@ -110,10 +110,10 @@ OCIO_ADD_TEST(HueCurveOpData, accessors) //OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); } -OCIO_ADD_TEST(HueCurveOpData, validate) +OCIO_ADD_TEST(GradingHueCurveOpData, validate) { // Default is valid. - //OCIO::HueCurveOpData gc; + //OCIO::GradingHueCurveOpData gc; //OCIO_CHECK_NO_THROW(gc.validate()); // Curves with a single control point are not valid. diff --git a/tests/cpu/ops/gradingrgbcurve/HueCurveOp_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOp_tests.cpp similarity index 88% rename from tests/cpu/ops/gradingrgbcurve/HueCurveOp_tests.cpp rename to tests/cpu/ops/gradinghuecurve/GradingHueCurveOp_tests.cpp index 39f3a92fd0..41dee69048 100644 --- a/tests/cpu/ops/gradingrgbcurve/HueCurveOp_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOp_tests.cpp @@ -10,17 +10,17 @@ namespace OCIO = OCIO_NAMESPACE; -OCIO_ADD_TEST(HueCurveOp, create) +OCIO_ADD_TEST(GradingHueCurveOp, create) { //OCIO::TransformDirection direction = OCIO::TRANSFORM_DIR_FORWARD; - //OCIO::HueCurveOpDataRcPtr data = - // std::make_shared(); + //OCIO::GradingHueCurveOpDataRcPtr data = + // std::make_shared(); //OCIO::OpRcPtrVec ops; // - //OCIO_CHECK_NO_THROW(OCIO::CreateHueCurveOp(ops, data, direction)); + //OCIO_CHECK_NO_THROW(OCIO::CreateGradingHueCurveOp(ops, data, direction)); //OCIO_REQUIRE_EQUAL(ops.size(), 1); //OCIO_REQUIRE_ASSERT(ops[0]); - //OCIO_CHECK_EQUAL(ops[0]->getInfo(), ""); + //OCIO_CHECK_EQUAL(ops[0]->getInfo(), ""); //OCIO_CHECK_ASSERT(ops[0]->isIdentity()); //OCIO_CHECK_ASSERT(ops[0]->isNoOp()); @@ -33,15 +33,15 @@ OCIO_ADD_TEST(HueCurveOp, create) //OCIO_CHECK_ASSERT(!ops[1]->isNoOp()); } -OCIO_ADD_TEST(HueCurveOp, create_transform) +OCIO_ADD_TEST(GradingHueCurveOp, create_transform) { //OCIO::TransformDirection direction = OCIO::TRANSFORM_DIR_FORWARD; - //OCIO::HueCurveOpDataRcPtr data = - // std::make_shared(); + //OCIO::GradingHueCurveOpDataRcPtr data = + // std::make_shared(); ////data->getDynamicPropertyInternal()->makeDynamic(); //OCIO::OpRcPtrVec ops; // - //OCIO_CHECK_NO_THROW(OCIO::CreateHueCurveOp(ops, data, direction)); + //OCIO_CHECK_NO_THROW(OCIO::CreateGradingHueCurveOp(ops, data, direction)); //OCIO_REQUIRE_EQUAL(ops.size(), 1); //OCIO::GroupTransformRcPtr group = OCIO::GroupTransform::Create(); @@ -58,11 +58,11 @@ OCIO_ADD_TEST(HueCurveOp, create_transform) //OCIO_CHECK_ASSERT(gcTransform->isDynamic()); } -OCIO_ADD_TEST(HueCurveOp, build_ops) +OCIO_ADD_TEST(GradingHueCurveOp, build_ops) { //OCIO::ConstConfigRcPtr config = OCIO::Config::CreateRaw(); // - //auto gcTransform = OCIO::HueCurveTransform::Create(); + //auto gcTransform = OCIO::GradingHueCurveTransform::Create(); //OCIO_CHECK_ASSERT(gcTransform.get()); // //// Identity does create an op. diff --git a/tests/cpu/ops/gradingrgbcurve/HueCurve_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurve_tests.cpp similarity index 100% rename from tests/cpu/ops/gradingrgbcurve/HueCurve_tests.cpp rename to tests/cpu/ops/gradinghuecurve/GradingHueCurve_tests.cpp diff --git a/tests/cpu/transforms/HueCurveTransform_tests.cpp b/tests/cpu/transforms/HueCurveTransform_tests.cpp index 9d2212b465..fdcf664461 100644 --- a/tests/cpu/transforms/HueCurveTransform_tests.cpp +++ b/tests/cpu/transforms/HueCurveTransform_tests.cpp @@ -3,18 +3,18 @@ #include "ops/gradingrgbcurve/GradingBSplineCurve.h" -#include "transforms/HueCurveTransform.cpp" +#include "transforms/GradingHueCurveTransform.cpp" #include "testutils/UnitTest.h" #include "UnitTestLogUtils.h" namespace OCIO = OCIO_NAMESPACE; -OCIO_ADD_TEST(HueCurveTransform, basic) +OCIO_ADD_TEST(GradingHueCurveTransform, basic) { // Create transform and validate default values for all styles. - auto gctLin = OCIO::HueCurveTransform::Create(OCIO::GRADING_LOG); + auto gctLin = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); //OCIO_CHECK_EQUAL(gctLin->getStyle(), OCIO::GRADING_LIN); OCIO_CHECK_EQUAL(gctLin->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); gctLin->setDirection(OCIO::TRANSFORM_DIR_INVERSE); @@ -100,7 +100,7 @@ OCIO_ADD_TEST(HueCurveTransform, basic) //OCIO_CHECK_ASSERT(!gct->slopesAreDefault(OCIO::RGB_BLUE)); } -OCIO_ADD_TEST(HueCurveTransform, processor_several_transforms) +OCIO_ADD_TEST(GradingHueCurveTransform, processor_several_transforms) { //OCIO::ConfigRcPtr config = OCIO::Config::Create(); //const float srcPixel[3] = { 0.2f, 0.3f, 0.4f }; @@ -119,7 +119,7 @@ OCIO_ADD_TEST(HueCurveTransform, processor_several_transforms) //auto rgbCurveA = OCIO::GradingRGBCurve::Create(c1, c2, c3, c5); - //auto gcta = OCIO::HueCurveTransform::Create(); + //auto gcta = OCIO::GradingHueCurveTransform::Create(); //OCIO_CHECK_ASSERT(gcta.get()); //OCIO_CHECK_NO_THROW(gcta->validate()); //gcta->setValue(rgbCurveA); @@ -227,7 +227,7 @@ OCIO_ADD_TEST(HueCurveTransform, processor_several_transforms) //} } -OCIO_ADD_TEST(HueCurveTransform, serialization) +OCIO_ADD_TEST(GradingHueCurveTransform, serialization) { // Test the serialization of the transform. @@ -243,7 +243,7 @@ OCIO_ADD_TEST(HueCurveTransform, serialization) // //auto data = OCIO::GradingRGBCurve::Create(c1, c2, c3, c4); - //auto curve = OCIO::HueCurveTransform::Create(); + //auto curve = OCIO::GradingHueCurveTransform::Create(); //OCIO_CHECK_ASSERT(curve.get()); //OCIO_CHECK_NO_THROW(curve->validate()); @@ -280,12 +280,12 @@ OCIO_ADD_TEST(HueCurveTransform, serialization) } -OCIO_ADD_TEST(HueCurveTransform, local_bypass) +OCIO_ADD_TEST(GradingHueCurveTransform, local_bypass) { // Test that the GPU is empty for an identity transform. - OCIO::HueCurveTransformRcPtr transform - = OCIO::HueCurveTransform::Create(OCIO::GRADING_LOG); + OCIO::GradingHueCurveTransformRcPtr transform + = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); //std::cout << "local_bypass HOMEMADE\n"; OCIO_CHECK_ASSERT(transform.get()); diff --git a/tests/gpu/CMakeLists.txt b/tests/gpu/CMakeLists.txt index 5181c18095..5f1c0379cf 100644 --- a/tests/gpu/CMakeLists.txt +++ b/tests/gpu/CMakeLists.txt @@ -15,7 +15,7 @@ set(SOURCES GPUUnitTest.cpp GradingPrimaryOp_test.cpp GradingRGBCurveOp_test.cpp - HueCurveOp_test.cpp + GradingHueCurveOp_test.cpp GradingToneOp_test.cpp LogOp_test.cpp Lut1DOp_test.cpp diff --git a/tests/gpu/HueCurveOp_test.cpp b/tests/gpu/GradingHueCurveOp_test.cpp similarity index 89% rename from tests/gpu/HueCurveOp_test.cpp rename to tests/gpu/GradingHueCurveOp_test.cpp index 03aaf7a882..fa8ecef16e 100644 --- a/tests/gpu/HueCurveOp_test.cpp +++ b/tests/gpu/GradingHueCurveOp_test.cpp @@ -16,10 +16,10 @@ void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool d // { 0.809f, 0.631f },{ 0.948f, 0.704f }, // { 1.0f, 1.0f } }); //auto curve = OCIO::GradingHueCurve::Create(c); - //auto hc = OCIO::HueCurveTransform::Create(OCIO::GRADING_LOG); + //auto hc = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); //if(!hc.get()) //{ - // throw OCIO::Exception("Cannot create HueCurveTransform."); + // throw OCIO::Exception("Cannot create GradingHueCurveTransform."); //} //hc->setValue(curve); //hc->setDirection(dir); @@ -67,10 +67,10 @@ void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) // { 0.809f, 0.631f },{ 0.948f, 0.704f }, // { 1.0f, 1.0f } }); //auto curve = OCIO::GradingHueCurve::Create(c); - //auto hc = OCIO::HueCurveTransform::Create(OCIO::GRADING_LIN); + //auto hc = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LIN); //if(!hc.get()) //{ - // throw OCIO::Exception("Cannot create HueCurveTransform."); + // throw OCIO::Exception("Cannot create GradingHueCurveTransform."); //} //hc->setValue(curve); //hc->setDirection(dir); @@ -136,7 +136,7 @@ void HueSCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) //OCIO::ConstGradingBSplineCurveRcPtr z = scaling; //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(m, m, m, z); - //auto gc = OCIO::HueCurveTransform::Create(); + //auto gc = OCIO::GradingHueCurveTransform::Create(); //gc->setValue(curves); //gc->setDirection(dir); //if (dynamic) @@ -154,22 +154,22 @@ void HueSCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) //test.setTestNaN(true); } -OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_fwd) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_fwd) { HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, false); } -OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_fwd_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_fwd_dynamic) { HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, true); } -OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_rev) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_rev) { HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, false); } -OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_rev_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_rev_dynamic) { HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, true); } diff --git a/tests/osl/HueCurveOp_test.cpp b/tests/osl/GradingHueCurveOp_test.cpp similarity index 81% rename from tests/osl/HueCurveOp_test.cpp rename to tests/osl/GradingHueCurveOp_test.cpp index bb5eb62b2f..803dc7a904 100644 --- a/tests/osl/HueCurveOp_test.cpp +++ b/tests/osl/GradingHueCurveOp_test.cpp @@ -18,11 +18,11 @@ void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool d //auto m = OCIO::GradingBSplineCurve::Create({ { -0.1f, 0.1f },{ 1.1f, 1.3f } }); //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(r, g, b, m); - //auto gc = OCIO::HueCurveTransform::Create(); + //auto gc = OCIO::GradingHueCurveTransform::Create(); //gc->setValue(curves); //if(!gc.get()) //{ - // throw OCIO::Exception("Cannot create HueCurveTransform."); + // throw OCIO::Exception("Cannot create GradingHueCurveTransform."); //} //gc->setDirection(dir); @@ -41,22 +41,22 @@ void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool d //test.setTestNaN(true); } -OCIO_ADD_GPU_TEST(HueCurveTransform, style_log_fwd) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_log_fwd) { GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_FORWARD, false); } -OCIO_ADD_GPU_TEST(HueCurveTransform, style_log_fwd_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_log_fwd_dynamic) { GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_FORWARD, true); } -OCIO_ADD_GPU_TEST(HueCurveTransform, style_log_rev) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_log_rev) { GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, false); } -OCIO_ADD_GPU_TEST(HueCurveTransform, style_log_rev_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_log_rev_dynamic) { GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, true); } @@ -72,7 +72,7 @@ void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) //auto m = OCIO::GradingBSplineCurve::Create({ { -0.1f, 0.1f },{ 1.1f, 0.9f } }); //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(r, g, b, m); - //auto gc = OCIO::HueCurveTransform::Create(); + //auto gc = OCIO::GradingHueCurveTransform::Create(); //gc->setValue(curves); //gc->setDirection(dir); //if (dynamic) @@ -90,22 +90,22 @@ void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) //test.setTestNaN(true); } -OCIO_ADD_GPU_TEST(HueCurveTransform, style_lin_fwd) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_lin_fwd) { HueCurveLin(test, OCIO::TRANSFORM_DIR_FORWARD, false); } -OCIO_ADD_GPU_TEST(HueCurveTransform, style_lin_fwd_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_lin_fwd_dynamic) { HueCurveLin(test, OCIO::TRANSFORM_DIR_FORWARD, true); } -OCIO_ADD_GPU_TEST(HueCurveTransform, style_lin_rev) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_lin_rev) { HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, false); } -OCIO_ADD_GPU_TEST(HueCurveTransform, style_lin_rev_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_lin_rev_dynamic) { HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, true); } @@ -134,10 +134,10 @@ void HueSCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) //OCIO::ConstGradingBSplineCurveRcPtr z = scaling; //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(m, m, m, z); - //auto gc = OCIO::HueCurveTransform::Create(); + //auto gc = OCIO::GradingHueCurveTransform::Create(); //if(!gc.get()) //{ - // throw OCIO::Exception("Cannot create HueCurveTransform."); + // throw OCIO::Exception("Cannot create GradingHueCurveTransform."); //} //gc->setValue(curves); //gc->setDirection(dir); @@ -156,22 +156,22 @@ void HueSCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) //test.setTestNaN(true); } -OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_fwd) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_fwd) { HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, false); } -OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_fwd_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_fwd_dynamic) { HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, true); } -OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_rev) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_rev) { HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, false); } -OCIO_ADD_GPU_TEST(HueCurveTransform, scurve_rev_dynamic) +OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_rev_dynamic) { HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, true); } From 41cdac2ed2ef6845e2be34f6800cdc49584f3e94 Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Thu, 13 Jun 2024 09:39:22 -0400 Subject: [PATCH 04/30] Review Fixes P3 (cherry picked from commit 83340c595fae4c130df105fc83b31270f66f58a2) Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTypes.h | 14 +- src/OpenColorIO/DynamicProperty.h | 2 +- src/OpenColorIO/Op.h | 2 +- src/OpenColorIO/OpBuilders.h | 2 +- src/OpenColorIO/Transform.cpp | 2 +- .../ops/gradinghuecurve/GradingHueCurve.cpp | 16 +- .../ops/gradinghuecurve/GradingHueCurveOp.cpp | 4 +- .../gradinghuecurve/GradingHueCurveOpGPU.cpp | 4 +- .../gradingrgbcurve/GradingBSplineCurve.cpp | 1100 ++++++++--------- .../ops/gradingrgbcurve/GradingBSplineCurve.h | 17 +- .../gradingrgbcurve/GradingRGBCurveOpGPU.cpp | 28 +- tests/cpu/CMakeLists.txt | 2 +- ...cpp => GradingHueCurveTransform_tests.cpp} | 0 13 files changed, 581 insertions(+), 612 deletions(-) rename tests/cpu/transforms/{HueCurveTransform_tests.cpp => GradingHueCurveTransform_tests.cpp} (100%) diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index dca9a02723..cfd0672125 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -563,7 +563,7 @@ enum DynamicPropertyType DYNAMIC_PROPERTY_GRADING_PRIMARY, ///< Used by GradingPrimaryTransform DYNAMIC_PROPERTY_GRADING_RGBCURVE, ///< Used by GradingRGBCurveTransform DYNAMIC_PROPERTY_GRADING_TONE, ///< Used by GradingToneTransform - DYNAMIC_PROPERTY_GRADING_HUECURVE ///< Used by GradingToneTransform + DYNAMIC_PROPERTY_GRADING_HUECURVE ///< Used by GradingHueCurveTransform }; /// Types for GradingRGBCurve. @@ -592,11 +592,13 @@ enum HueCurveType enum BSplineCurveType { - B_SPLINE = 0, //!< Monotonic quadratic B-spline based function. - DIAGONAL_B_SPLINE, //!< Monotonic quadratic B-spline based function (newer algorithm). - HUE_HUE_B_SPLINE, //!< Special B-spline used for the hue vs. hue curve (monotonic and periodic). - PERIODIC_B_SPLINE, //!< Periodic non-monotonic B-spline used for some of the hue curves. - HORIZONTAL_B_SPLINE //!< Non-monotonic B-spline used for some of the lum vs. sat-gain curve. + B_SPLINE = 0, //!< Monotonic quadratic B-spline based function. + DIAGONAL_B_SPLINE, //!< Monotonic quadratic B-spline based function (newer algorithm). + HUE_HUE_B_SPLINE, //!< Special B-spline used for the hue vs. hue curve (monotonic and periodic). + PERIODIC_HORIZONTAL1_B_SPLINE, //!< Periodic non-monotonic B-spline centered at 1 used for some of the hue curves. + PERIODIC_HORIZONTAL0_B_SPLINE, //!< Periodic non-monotonic B-spline centered at 0 used for some of the hue curves. + HORIZONTAL1_B_SPLINE, //!< Non-monotonic B-spline, centered at 1, used for HueSat, HueLum, and SatLum curves. + HORIZONTAL0_B_SPLINE //!< Non-monotonic B-spline, centered at 0, used for HueFX curve. }; /// Types for uniform data. diff --git a/src/OpenColorIO/DynamicProperty.h b/src/OpenColorIO/DynamicProperty.h index 0480cc8a1e..3a03e6a0a6 100644 --- a/src/OpenColorIO/DynamicProperty.h +++ b/src/OpenColorIO/DynamicProperty.h @@ -189,7 +189,7 @@ class DynamicPropertyGradingHueCurveImpl : public DynamicPropertyImpl, bool getLocalBypass() const; int getNumKnots() const; int getNumCoefs() const; - static int GetNumOffsetValues() { return 8; } + static int GetNumOffsetValues() { return 16; } const int * getKnotsOffsetsArray() const; const int * getCoefsOffsetsArray() const; const float * getKnotsArray() const; diff --git a/src/OpenColorIO/Op.h b/src/OpenColorIO/Op.h index aac79a1239..895c426d80 100644 --- a/src/OpenColorIO/Op.h +++ b/src/OpenColorIO/Op.h @@ -271,7 +271,7 @@ class Op virtual void replaceDynamicProperty(DynamicPropertyType /* type */, DynamicPropertyGradingHueCurveImplRcPtr & /* prop */) { - throw Exception("Op does not implement grading rgb curve dynamic property."); + throw Exception("Op does not implement grading hue curve dynamic property."); } virtual void replaceDynamicProperty(DynamicPropertyType /* type */, DynamicPropertyGradingToneImplRcPtr & /* prop */) diff --git a/src/OpenColorIO/OpBuilders.h b/src/OpenColorIO/OpBuilders.h index 4639be212b..369c2d001b 100644 --- a/src/OpenColorIO/OpBuilders.h +++ b/src/OpenColorIO/OpBuilders.h @@ -100,7 +100,7 @@ void BuildGradingPrimaryOp(OpRcPtrVec & ops, const GradingPrimaryTransform & transform, TransformDirection dir); -void BuildHueCurveOp(OpRcPtrVec & ops, +void BuildGradingHueCurveOp(OpRcPtrVec & ops, const Config & config, const ConstContextRcPtr & context, const GradingHueCurveTransform & transform, diff --git a/src/OpenColorIO/Transform.cpp b/src/OpenColorIO/Transform.cpp index 7b05bd392d..74223096fa 100755 --- a/src/OpenColorIO/Transform.cpp +++ b/src/OpenColorIO/Transform.cpp @@ -112,7 +112,7 @@ void BuildOps(OpRcPtrVec & ops, else if (ConstGradingHueCurveTransformRcPtr hueCurveTransform = \ DynamicPtrCast(transform)) { - BuildHueCurveOp(ops, config, context, *hueCurveTransform, dir); + BuildGradingHueCurveOp(ops, config, context, *hueCurveTransform, dir); } else if (ConstGradingToneTransformRcPtr gradingToneTransform = \ DynamicPtrCast(transform)) diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp index cdf8e9081c..fae0331516 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp @@ -45,12 +45,12 @@ static const std::vector DefaultLumLumLinCtrl{ { -7.0f, -7. } const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueHue(DefaultHueHueCtrl, BSplineCurveType::HUE_HUE_B_SPLINE ); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueSat(DefaultHueSatCtrl, BSplineCurveType::PERIODIC_B_SPLINE ); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueFx(DefaultHueFxCtrl, BSplineCurveType::PERIODIC_B_SPLINE ); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSat(DefaultLumSatCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSatLin(DefaultLumSatLinCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueSat(DefaultHueSatCtrl, BSplineCurveType::PERIODIC_HORIZONTAL1_B_SPLINE ); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueFx(DefaultHueFxCtrl, BSplineCurveType::PERIODIC_HORIZONTAL0_B_SPLINE ); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSat(DefaultLumSatCtrl, BSplineCurveType::HORIZONTAL1_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSatLin(DefaultLumSatLinCtrl, BSplineCurveType::HORIZONTAL1_B_SPLINE); const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultSatSat(DefaultSatSatCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultSatLum(DefaultSatLumCtrl, BSplineCurveType::HORIZONTAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultSatLum(DefaultSatLumCtrl, BSplineCurveType::HORIZONTAL1_B_SPLINE); const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumLum(DefaultLumLumCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumLumLin(DefaultLumLumLinCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); @@ -153,7 +153,7 @@ const char * CurveType(int c) default: break; } - return "invalid"; + return "illegal"; } } @@ -197,7 +197,7 @@ ConstGradingBSplineCurveRcPtr GradingHueCurveImpl::getCurve(HueCurveType c) cons { if(!isHueCurveTypeValid(c)) { - throw Exception("The HueCurveType provided is invalid"); + throw Exception("The HueCurveType provided is illegal"); } return m_curves[c]; @@ -207,7 +207,7 @@ GradingBSplineCurveRcPtr GradingHueCurveImpl::getCurve(HueCurveType c) { if(!isHueCurveTypeValid(c)) { - throw Exception("The HueCurveType provided is invalid"); + throw Exception("The HueCurveType provided is illegal"); } return m_curves[c]; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp index c34ee5fc33..0538f18972 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp @@ -74,7 +74,7 @@ GradingHueCurveOp::GradingHueCurveOp(GradingHueCurveOpDataRcPtr & hueCurveData) OpRcPtr GradingHueCurveOp::clone() const { GradingHueCurveOpDataRcPtr p = hueCurveData()->clone(); - return std::make_shared(p); + return std::make_shared(p); } GradingHueCurveOp::~GradingHueCurveOp() @@ -238,7 +238,7 @@ void CreateGradingHueCurveTransform(GroupTransformRcPtr & group, ConstOpRcPtr & group->appendTransform(gcTransform); } -void BuildHueCurveOp(OpRcPtrVec & ops, +void BuildGradingHueCurveOp(OpRcPtrVec & ops, const Config & /*config*/, const ConstContextRcPtr & /*context*/, const GradingHueCurveTransform & transform, diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp index 7fea51c815..6a752aef31 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp @@ -241,8 +241,8 @@ void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator st.indent(); const bool isInv = gcData->getDirection() == TRANSFORM_DIR_INVERSE; - GradingBSplineCurveImpl::AddShaderEvalHueCurve(st, props.m_knotsOffsets, props.m_coefsOffsets, - props.m_knots, props.m_coefs, isInv); + GradingBSplineCurveImpl::AddShaderEval(st, props.m_knotsOffsets, props.m_coefsOffsets, + props.m_knots, props.m_coefs, isInv); st.dedent(); st.newLine() << "}"; diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index 5ba6fff574..bd55a77b10 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -11,197 +11,344 @@ #include "GpuShaderUtils.h" #include "ops/gradingrgbcurve/GradingBSplineCurve.h" -namespace OCIO_NAMESPACE +namespace { -GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size) -{ - auto newSpline = std::make_shared(size); - GradingBSplineCurveRcPtr res = newSpline; - return res; -} +} // anonymous namespace -GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size, BSplineCurveType curveType) +namespace OCIO_NAMESPACE { - auto newSpline = std::make_shared(size, curveType); - GradingBSplineCurveRcPtr res = newSpline; - return res; -} - -GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values) +namespace { - auto newSpline = std::make_shared(values.size()); - size_t i = 0; - for (const auto & c : values) - { - newSpline->getControlPoint(i++) = c; - } - GradingBSplineCurveRcPtr res; - res = newSpline; - return res; -} -GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values, BSplineCurveType curveType) +bool IsDiagonalControlPoint( const GradingControlPoint& cp ) { - auto newSpline = std::make_shared(values.size(), curveType); - size_t i = 0; - for (const auto & c : values) - { - newSpline->getControlPoint(i++) = c; - } - GradingBSplineCurveRcPtr res; - res = newSpline; - return res; + return cp.m_x == cp.m_y; } -GradingBSplineCurveImpl::GradingBSplineCurveImpl(size_t size) - : m_controlPoints(size), m_slopesArray(size, 0.f), m_curveType(B_SPLINE) +bool IsHorizontal0ControlPoint( const GradingControlPoint& cp ) { + return cp.m_y == 0.f; } -GradingBSplineCurveImpl::GradingBSplineCurveImpl(size_t size, BSplineCurveType curveType) - : m_controlPoints(size), m_slopesArray(size, 0.f), m_curveType(curveType) +bool IsHorizontal1ControlPoint( const GradingControlPoint& cp ) { + return cp.m_y == 1.f; } -GradingBSplineCurveImpl::GradingBSplineCurveImpl(const std::vector & controlPoints) - : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_curveType(B_SPLINE) -{ -} +//------------------------------------------------------------------------------------------------ +// +void PrepHueCurveData(const std::vector& ctrlPnts, + std::vector& outCtrlPnts, + bool isPeriodic, + bool isHorizontal) + { + size_t numCtrlPnts = ctrlPnts.size(); + for (unsigned i = 0; i < numCtrlPnts; ++i) + { + const float xval = ctrlPnts[ i ].m_x; + const float yval = ctrlPnts[ i ].m_y; + // Wrap periodic x values into [0,1). + if (isPeriodic && (xval < 0.f)) + { + outCtrlPnts.push_back(GradingControlPoint(xval + 1.f, isHorizontal ? yval : yval + 1.f)); + } + else if (isPeriodic && (xval >= 1.f)) + { + outCtrlPnts.push_back(GradingControlPoint(xval - 1.f, isHorizontal ? yval : yval - 1.f)); + } + else + { + outCtrlPnts.push_back(GradingControlPoint(xval, yval)); + } + } -GradingBSplineCurveImpl::GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineCurveType curveType) - : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_curveType(curveType) -{ -} + // Sort x and y based on x order. + for (unsigned i = 0; i < numCtrlPnts; ++i) + { + unsigned min_index = i; + float min_val = outCtrlPnts[i].m_x; + for (unsigned j = i + 1; j < numCtrlPnts; ++j) + { + if (outCtrlPnts[j].m_x < min_val) + { + min_val = outCtrlPnts[j].m_x; + min_index = j; + } + } -GradingBSplineCurveRcPtr GradingBSplineCurveImpl::createEditableCopy() const -{ - auto copy = std::make_shared(0); - copy->m_controlPoints = m_controlPoints; - copy->m_slopesArray = m_slopesArray; - copy->m_curveType = m_curveType; - GradingBSplineCurveRcPtr res; - res = copy; - return res; -} + std::swap( outCtrlPnts[i], outCtrlPnts[min_index] ); + } -size_t GradingBSplineCurveImpl::getNumControlPoints() const noexcept -{ - return m_controlPoints.size(); -} + // Ensure that there is a minimum space between the x values. + const float tol = 2e-3f; + const float x_span = outCtrlPnts[numCtrlPnts - 1].m_x - outCtrlPnts[0].m_x; + for (unsigned i = 1; i < outCtrlPnts.size(); ++i) + { + if ( (outCtrlPnts[i].m_x - outCtrlPnts[i - 1].m_x) < x_span * tol ) + { + outCtrlPnts[i].m_x = outCtrlPnts[i - 1].m_x + x_span * tol; + } + } + if (!isHorizontal) + { + const float y_span = outCtrlPnts[numCtrlPnts - 1].m_y - outCtrlPnts[0].m_y; + for (unsigned i = 1; i < outCtrlPnts.size(); ++i) + { + if ( (outCtrlPnts[i].m_y - outCtrlPnts[i - 1].m_y) < y_span * tol ) + { + outCtrlPnts[i].m_y = outCtrlPnts[i - 1].m_y + y_span * tol; + } + } + } -void GradingBSplineCurveImpl::setNumControlPoints(size_t size) -{ - m_controlPoints.resize(size); - m_slopesArray.resize(size, 0.f); -} + if (isPeriodic) + { + // Copy a value from each side and wrap it around to the other side. + GradingControlPoint firstCtrlPnt = outCtrlPnts[numCtrlPnts - 1]; + firstCtrlPnt.m_x -= 1.f; + firstCtrlPnt.m_y = isHorizontal ? firstCtrlPnt.m_y : firstCtrlPnt.m_y - 1.f; + outCtrlPnts.insert(outCtrlPnts.begin(), firstCtrlPnt); + + GradingControlPoint lastCtrlPnt = outCtrlPnts[1]; + lastCtrlPnt.m_x += 1.f; + lastCtrlPnt.m_y = isHorizontal ? lastCtrlPnt.m_y : lastCtrlPnt.m_y + 1.f; + outCtrlPnts.push_back(lastCtrlPnt); + } + } -void GradingBSplineCurveImpl::validateIndex(size_t index) const +//------------------------------------------------------------------------------------------------ +// +float CalcKsi(unsigned i, + const std::vector& outCtrlPnts, + const std::vector& slopes) { - const size_t numPoints = m_controlPoints.size(); - if (index >= numPoints) - { - std::ostringstream oss; - oss << "There are '"<< numPoints << "' control points. '" << index << "' is invalid."; - throw Exception(oss.str().c_str()); - } -} + const GradingControlPoint& p0 = outCtrlPnts[i]; + const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + + const float k = 0.2f; + const float dx = p1.m_x - p0.m_x; + const float secantSlope = (p1.m_y - p0.m_y) / dx; + float secant = secantSlope; + float m0 = slopes[i]; + float m1 = slopes[i + 1]; + if (secant < 0.f) + { + m0 = -slopes[i]; m1 = -slopes[i + 1]; + secant = -secant; + } -const GradingControlPoint & GradingBSplineCurveImpl::getControlPoint(size_t index) const -{ - validateIndex(index); - return m_controlPoints[index]; -} + const float x_mid = p0.m_x + 0.5f * dx; + const float left_bnd = p0.m_x + dx * k; + const float right_bnd = p1.m_x - dx * k; + float top_bnd = left_bnd; + float bottom_bnd = right_bnd; + float m_min = m0; + float m_max = m1; + if (m0 > m1) + { + m_max = m0; m_min = m1; + top_bnd = right_bnd; bottom_bnd = left_bnd; + } -GradingControlPoint & GradingBSplineCurveImpl::getControlPoint(size_t index) -{ - validateIndex(index); - return m_controlPoints[index]; + const float dm = m_max - m_min; + const float b = 1.f - 0.5f * k; + const float b_high = m_min + b * dm; + const float b_low = m_min + (1.f - b) * dm; + const float bbb = m_max * 4.f; + const float bb = m_max * 1.1f; + const float m_rel_diff = dm / std::max(0.01f, m_max); + const float alpha = std::max( 0.f, std::min( (m_rel_diff - 0.05f) / (0.75f - 0.05f), 1.f ) ); + top_bnd = x_mid + alpha * (top_bnd - x_mid); + bottom_bnd = x_mid + alpha * (bottom_bnd - x_mid); + + // Calculate the middle knot. + float ksi = 0.f; + if (secant >= bbb) + { + ksi = x_mid; + } + else if (secant > bb) + { + const float blend = (secant - bb) / (bbb - bb); + ksi = top_bnd + blend * (x_mid - top_bnd); + } + else if (secant >= b_high) + { + ksi = top_bnd; + } + else if ((secant > b_low) && (b_high != b_low)) + { + const float blend = (secant - b_low) / (b_high - b_low); + ksi = bottom_bnd + blend * (top_bnd - bottom_bnd); + } + else + { + ksi = bottom_bnd; + } + return ksi; } -float GradingBSplineCurveImpl::getSlope(size_t index) const +//------------------------------------------------------------------------------------------------ +// +void FitHueSpline(const std::vector& outCtrlPnts, + const std::vector& slopes, + std::vector& knots, + std::vector& coefsA, + std::vector& coefsB, + std::vector& coefsC) { - validateIndex(index); - return m_slopesArray[index]; -} + knots.push_back( outCtrlPnts[0].m_x ); + unsigned numCtrlPnts = outCtrlPnts.size(); + for (unsigned i = 0; i < numCtrlPnts - 1; ++i) + { + const GradingControlPoint& p0 = outCtrlPnts[i]; + const GradingControlPoint& p1 = outCtrlPnts[i + 1]; -void GradingBSplineCurveImpl::setSlope(size_t index, float slope) -{ - validateIndex(index); - m_slopesArray[index] = slope; -} + const float dx = p1.m_x - p0.m_x; + const float secantSlope = (p1.m_y - p0.m_y) / dx; -bool GradingBSplineCurveImpl::slopesAreDefault() const -{ - for (size_t i = 0; i < m_slopesArray.size(); ++i) + if ( fabsf( (slopes[i] + slopes[i + 1]) - 2.f * secantSlope ) <= 1e-5f ) { - if (m_slopesArray[i] != 0.f) - { - return false; - } + coefsC.push_back( p0.m_y ); + coefsB.push_back( slopes[i] ); + coefsA.push_back( 0.5f * (slopes[i + 1] - slopes[i]) / dx ); } - return true; -} + else + { + // Calculate the middle knot. + const float ksi = CalcKsi(i, outCtrlPnts, slopes); -void GradingBSplineCurveImpl::validate() const + // Calculate the coefficients. + const float m_bar = (2.f * secantSlope - slopes[i + 1]) + + (slopes[i + 1] - slopes[i]) * (ksi - p0.m_x) / (p1.m_x - p0.m_x); + const float eta = (m_bar - slopes[i]) / (ksi - p0.m_x); + coefsC.push_back( p0.m_y ); + coefsB.push_back( slopes[i] ); + coefsA.push_back( 0.5f * eta ); + coefsC.push_back( p0.m_y + slopes[i] * (ksi - p0.m_x) + 0.5f * eta * (ksi - p0.m_x) * (ksi - p0.m_x) ); + coefsB.push_back( m_bar ); + coefsA.push_back( 0.5f * (slopes[i + 1] - m_bar) / (p1.m_x - ksi) ); + knots.push_back( ksi ); + } + + knots.push_back( p1.m_x ); + } +} + +//------------------------------------------------------------------------------------------------ +// +void EstimateHueSlopes(std::vector& outCtrlPnts, + std::vector& slopes, + bool isPeriodic, + bool isHorizontal) { - const size_t numPoints = m_controlPoints.size(); - if (numPoints < 2) + slopes.clear(); + unsigned numCtrlPnts = outCtrlPnts.size(); + std::vector secantSlope; + std::vector secantLen; + for (unsigned i = 0; i < numCtrlPnts - 1; ++i) + { + const GradingControlPoint& p0 = outCtrlPnts[i]; + const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + + const float del_x = p1.m_x - p0.m_x; // PrepHueCurveData ensures this is > 0 + const float del_y = p1.m_y - p0.m_y; + secantSlope.push_back( del_y / del_x ); + secantLen.push_back( sqrt( del_x * del_x + del_y * del_y ) ); + } + + if (numCtrlPnts == 2) + { + slopes.push_back( secantSlope[0] ); + slopes.push_back( secantSlope[0] ); + return; + } + + slopes.push_back(0.f); + + if (isHorizontal) // All horizontal curves and diagonal hue-hue. + { + for (unsigned i = 1; i < numCtrlPnts - 1; ++i) { - throw Exception("There must be at least 2 control points."); + float s = 0.f; + float denom = secantSlope[i] + secantSlope[i - 1]; + if (fabsf(denom) < 1e-3f) + { + const float minval = denom < 0.f ? -1e-3f : 1e-3f; + s = 2.f * secantSlope[i] * secantSlope[i - 1] / minval; + } + else + { + s = 2.f * secantSlope[i] * secantSlope[i - 1] / denom; + } + // Set slope to zero at flat areas or extrema. + if ( secantSlope[i] * secantSlope[i - 1] <= 0.f ) + { + s = 0.f; + } + slopes.push_back( s ); } - if (numPoints != m_slopesArray.size()) + slopes.push_back( 0.5f * ( 3.f * secantSlope[numCtrlPnts - 2] - slopes[numCtrlPnts - 2] ) ); + slopes[0] = 0.5f * ( 3.f * secantSlope[0] - slopes[1] ); + } + else // Diagonal curves except hue-hue (LvL and SvS). + { + unsigned i = 0; + while (true) { - throw Exception("The slopes array must be the same length as the control points."); + unsigned j = i; + float DL = secantLen[i]; + while ( ( j < numCtrlPnts - 2 ) && ( fabsf( secantSlope[j + 1] - secantSlope[j] ) < 1e-6f ) ) + { + DL += secantLen[ j + 1 ]; + j++; + } + for (unsigned k = i; k <= j; ++k) + secantLen[k] = DL; + if (j >= numCtrlPnts - 3) + break; + i = j + 1; } - // Make sure the points are non-decreasing. - float lastX = -std::numeric_limits::max(); - for (size_t i = 0; i < numPoints; ++i) + for (unsigned k = 1; k < numCtrlPnts - 1; ++k) { - // Test x values only. - const float x = m_controlPoints[i].m_x; - if (x < lastX) - { - std::ostringstream oss; - oss << "Control point at index " << i << " has a x coordinate '" << x << "' that is "; - oss << "less from previous control point x cooordinate '" << lastX << "'."; - throw Exception(oss.str().c_str()); - } - lastX = x; + const float s = ( secantLen[k] * secantSlope[k] + secantLen[k - 1] * secantSlope[k - 1] ) / + ( secantLen[k] + secantLen[k - 1] ); + slopes.push_back( s ); } -} -bool GradingBSplineCurveImpl::isIdentity() const -{ - for (const auto & cp : m_controlPoints) - { - if (cp.m_x != cp.m_y) - { - return false; - } - } - if (!slopesAreDefault()) - { - return false; - } - return true; -} + const float minSlope = 0.01f; + slopes.push_back( std::max(minSlope, 0.5f * ( 3.f * secantSlope[numCtrlPnts - 2] - slopes[numCtrlPnts - 2] )) ); + slopes[0] = std::max(minSlope, 0.5f * ( 3.f * secantSlope[0] - slopes[1] )); + } -bool IsGradingCurveIdentity(const ConstGradingBSplineCurveRcPtr & curve) -{ - auto curveImpl = dynamic_cast(curve.get()); - if (curveImpl) + // Adjust slopes that are not shape-preserving. + for (unsigned i = 0; i < numCtrlPnts - 1; ++i) + { + float k = 0.2f; + if (fabsf(slopes[i]) > fabsf(slopes[i+1])) + k = 1.f - k; + const float m_near_min = slopes[i] + k * (slopes[i + 1] - slopes[i]); + float scale = 1.f; + if (m_near_min != 0.f) + scale = 0.75f * 2.f * secantSlope[i] / m_near_min; + if (scale < 1.f) { - return curveImpl->isIdentity(); + slopes[i] = scale * slopes[i]; + slopes[i + 1] = scale * slopes[i + 1]; } - return false; + } + + // Copy end slopes from the opposite side. + if (isPeriodic) + { + slopes[0] = slopes[numCtrlPnts - 2]; + slopes[numCtrlPnts - 1] = slopes[1]; + } } -namespace -{ - -void EstimateSlopesBSpline(const std::vector & ctrlPnts, std::vector & slopes) +void EstimateRGBSlopes(const std::vector & ctrlPnts, std::vector & slopes) { std::vector secantSlope; std::vector secantLen; @@ -353,420 +500,299 @@ bool AdjustSlopes(const std::vector & ctrlPnts, } // namespace - -//------------------------------------------------------------------------------------------------ -// -BSplineCurveType GradingBSplineCurveImpl::getCurveType() const +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size) { - return m_curveType; + auto newSpline = std::make_shared(size); + GradingBSplineCurveRcPtr res = newSpline; + return res; } -//------------------------------------------------------------------------------------------------ -// -void GradingBSplineCurveImpl::setCurveType(BSplineCurveType curveType) +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size, BSplineCurveType curveType) { - m_curveType = curveType; + auto newSpline = std::make_shared(size, curveType); + GradingBSplineCurveRcPtr res = newSpline; + return res; } - -//------------------------------------------------------------------------------------------------ -// -void GradingBSplineCurveImpl::computeKnotsAndCoefsBSpline(KnotsCoefs & knotsCoefs, int curveIdx) const +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values) { - // Skip invalid data and identity. - if (m_controlPoints.size() < 2 || isIdentity()) + auto newSpline = std::make_shared(values.size()); + size_t i = 0; + for (const auto & c : values) { - // Identity curve: offset is -1 and count is 0. - knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = -1; - knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = 0; - knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = -1; - knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = 0; + newSpline->getControlPoint(i++) = c; } - else + GradingBSplineCurveRcPtr res; + res = newSpline; + return res; +} + +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values, BSplineCurveType curveType) +{ + auto newSpline = std::make_shared(values.size(), curveType); + size_t i = 0; + for (const auto & c : values) { - std::vector knots; - std::vector coefsA; - std::vector coefsB; - std::vector coefsC; - std::vector slopes; + newSpline->getControlPoint(i++) = c; + } + GradingBSplineCurveRcPtr res; + res = newSpline; + return res; +} - if ( !slopesAreDefault() && (m_slopesArray.size() == m_controlPoints.size()) ) - { - // If the user-supplied slopes are non-zero, use those. - slopes = m_slopesArray; - } - else - { - // Otherwise, estimate slopes based on the control points. - EstimateSlopesBSpline(m_controlPoints, slopes); - } +GradingBSplineCurveImpl::GradingBSplineCurveImpl(size_t size) + : m_controlPoints(size), m_slopesArray(size, 0.f), m_curveType(B_SPLINE) +{ +} - FitSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); +GradingBSplineCurveImpl::GradingBSplineCurveImpl(size_t size, BSplineCurveType curveType) + : m_controlPoints(size), m_slopesArray(size, 0.f), m_curveType(curveType) +{ +} - bool adjustment_done = AdjustSlopes(m_controlPoints, slopes, knots); +GradingBSplineCurveImpl::GradingBSplineCurveImpl(const std::vector & controlPoints) + : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_curveType(B_SPLINE) +{ +} - if (adjustment_done) - { - knots.clear(); - coefsA.clear(); coefsB.clear(); coefsC.clear(); - FitSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); - } +GradingBSplineCurveImpl::GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineCurveType curveType) + : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_curveType(curveType) +{ +} - const int numKnots = static_cast(knotsCoefs.m_numKnots); - const int newKnots = static_cast(knots.size()); - const int numCoefs = static_cast(knotsCoefs.m_numCoefs); - const int newCoefs = static_cast(coefsA.size() * 3); +GradingBSplineCurveRcPtr GradingBSplineCurveImpl::createEditableCopy() const +{ + auto copy = std::make_shared(0); + copy->m_controlPoints = m_controlPoints; + copy->m_slopesArray = m_slopesArray; + copy->m_curveType = m_curveType; + GradingBSplineCurveRcPtr res; + res = copy; + return res; +} - if (numKnots + newKnots > KnotsCoefs::MAX_NUM_KNOTS || - numCoefs + newCoefs > KnotsCoefs::MAX_NUM_COEFS) - { - throw Exception("RGB curve: maximum number of control points reached."); - } +size_t GradingBSplineCurveImpl::getNumControlPoints() const noexcept +{ + return m_controlPoints.size(); +} - knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = numKnots; - knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = newKnots; - knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = numCoefs; - knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = newCoefs; +void GradingBSplineCurveImpl::setNumControlPoints(size_t size) +{ + m_controlPoints.resize(size); + m_slopesArray.resize(size, 0.f); +} - const unsigned coefsSize = (unsigned) coefsA.size(); - std::copy(knots.begin(), knots.end(), knotsCoefs.m_knotsArray.begin() + numKnots); - std::copy(coefsA.begin(), coefsA.end(), knotsCoefs.m_coefsArray.begin() + numCoefs); - std::copy(coefsB.begin(), coefsB.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize); - std::copy(coefsC.begin(), coefsC.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize * 2); - - knotsCoefs.m_numKnots += newKnots; - knotsCoefs.m_numCoefs += newCoefs; +void GradingBSplineCurveImpl::validateIndex(size_t index) const +{ + const size_t numPoints = m_controlPoints.size(); + if (index >= numPoints) + { + std::ostringstream oss; + oss << "There are '"<< numPoints << "' control points. '" << index << "' is invalid."; + throw Exception(oss.str().c_str()); } } -//------------------------------------------------------------------------------------------------ -// -void prepHueCurveData(const std::vector& ctrlPnts, - std::vector& outCtrlPnts, - bool isPeriodic, - bool isHorizontal) - { - size_t numCtrlPnts = ctrlPnts.size(); - for (unsigned i = 0; i < numCtrlPnts; ++i) - { - const float xval = ctrlPnts[ i ].m_x; - const float yval = ctrlPnts[ i ].m_y; - // Wrap periodic x values into [0,1). - if (isPeriodic && (xval < 0.f)) - { - outCtrlPnts.push_back(GradingControlPoint(xval + 1.f, isHorizontal ? yval : yval + 1.f)); - } - else if (isPeriodic && (xval >= 1.f)) - { - outCtrlPnts.push_back(GradingControlPoint(xval - 1.f, isHorizontal ? yval : yval - 1.f)); - } - else - { - outCtrlPnts.push_back(GradingControlPoint(xval, yval)); - } - } - - // Sort x and y based on x order. - for (unsigned i = 0; i < numCtrlPnts; ++i) - { - unsigned min_index = i; - float min_val = outCtrlPnts[i].m_x; - for (unsigned j = i + 1; j < numCtrlPnts; ++j) - { - if (outCtrlPnts[j].m_x < min_val) - { - min_val = outCtrlPnts[j].m_x; - min_index = j; - } - } - - std::swap( outCtrlPnts[i], outCtrlPnts[min_index] ); - } - - // Ensure that there is a minimum space between the x values. - const float tol = 2e-3f; - const float x_span = outCtrlPnts[numCtrlPnts - 1].m_x - outCtrlPnts[0].m_x; - for (unsigned i = 1; i < outCtrlPnts.size(); ++i) - { - if ( (outCtrlPnts[i].m_x - outCtrlPnts[i - 1].m_x) < x_span * tol ) - { - outCtrlPnts[i].m_x = outCtrlPnts[i - 1].m_x + x_span * tol; - } - } - if (!isHorizontal) - { - const float y_span = outCtrlPnts[numCtrlPnts - 1].m_y - outCtrlPnts[0].m_y; - for (unsigned i = 1; i < outCtrlPnts.size(); ++i) - { - if ( (outCtrlPnts[i].m_y - outCtrlPnts[i - 1].m_y) < y_span * tol ) - { - outCtrlPnts[i].m_y = outCtrlPnts[i - 1].m_y + y_span * tol; - } - } - } +const GradingControlPoint & GradingBSplineCurveImpl::getControlPoint(size_t index) const +{ + validateIndex(index); + return m_controlPoints[index]; +} - if (isPeriodic) - { - // Copy a value from each side and wrap it around to the other side. - GradingControlPoint firstCtrlPnt = outCtrlPnts[numCtrlPnts - 1]; - firstCtrlPnt.m_x -= 1.f; - firstCtrlPnt.m_y = isHorizontal ? firstCtrlPnt.m_y : firstCtrlPnt.m_y - 1.f; - outCtrlPnts.insert(outCtrlPnts.begin(), firstCtrlPnt); - - GradingControlPoint lastCtrlPnt = outCtrlPnts[1]; - lastCtrlPnt.m_x += 1.f; - lastCtrlPnt.m_y = isHorizontal ? lastCtrlPnt.m_y : lastCtrlPnt.m_y + 1.f; - outCtrlPnts.push_back(lastCtrlPnt); - } - } +GradingControlPoint & GradingBSplineCurveImpl::getControlPoint(size_t index) +{ + validateIndex(index); + return m_controlPoints[index]; +} - //------------------------------------------------------------------------------------------------ - // - float calcKsi(unsigned i, - const std::vector& outCtrlPnts, - const std::vector& slopes) - { - const GradingControlPoint& p0 = outCtrlPnts[i]; - const GradingControlPoint& p1 = outCtrlPnts[i + 1]; +float GradingBSplineCurveImpl::getSlope(size_t index) const +{ + validateIndex(index); + return m_slopesArray[index]; +} - const float k = 0.2f; +void GradingBSplineCurveImpl::setSlope(size_t index, float slope) +{ + validateIndex(index); + m_slopesArray[index] = slope; +} - const float dx = p1.m_x - p0.m_x; - const float secantSlope = (p1.m_y - p0.m_y) / dx; +bool GradingBSplineCurveImpl::slopesAreDefault() const +{ + for (size_t i = 0; i < m_slopesArray.size(); ++i) + { + if (m_slopesArray[i] != 0.f) + { + return false; + } + } + return true; +} - float secant = secantSlope; - float m0 = slopes[i]; - float m1 = slopes[i + 1]; - if (secant < 0.f) +void GradingBSplineCurveImpl::validate() const +{ + const size_t numPoints = m_controlPoints.size(); + if (numPoints < 2) { - m0 = -slopes[i]; m1 = -slopes[i + 1]; - secant = -secant; + throw Exception("There must be at least 2 control points."); } - const float x_mid = p0.m_x + 0.5f * dx; - - const float left_bnd = p0.m_x + dx * k; - const float right_bnd = p1.m_x - dx * k; - float top_bnd = left_bnd; - float bottom_bnd = right_bnd; - float m_min = m0; - float m_max = m1; - if (m0 > m1) + if (numPoints != m_slopesArray.size()) { - m_max = m0; m_min = m1; - top_bnd = right_bnd; bottom_bnd = left_bnd; + throw Exception("The slopes array must be the same length as the control points."); } - const float dm = m_max - m_min; - const float b = 1.f - 0.5f * k; - const float b_high = m_min + b * dm; - const float b_low = m_min + (1.f - b) * dm; - const float bbb = m_max * 4.f; - const float bb = m_max * 1.1f; - - const float m_rel_diff = dm / std::max(0.01f, m_max); - const float alpha = std::max( 0.f, std::min( (m_rel_diff - 0.05f) / (0.75f - 0.05f), 1.f ) ); - top_bnd = x_mid + alpha * (top_bnd - x_mid); - bottom_bnd = x_mid + alpha * (bottom_bnd - x_mid); - - // Calculate the middle knot. - float ksi = 0.f; - - if (secant >= bbb) + + // Make sure the points are non-decreasing. + float lastX = -std::numeric_limits::max(); + for (size_t i = 0; i < numPoints; ++i) { - ksi = x_mid; + // Test x values only. + const float x = m_controlPoints[i].m_x; + if (x < lastX) + { + std::ostringstream oss; + oss << "Control point at index " << i << " has a x coordinate '" << x << "' that is "; + oss << "less from previous control point x cooordinate '" << lastX << "'."; + throw Exception(oss.str().c_str()); + } + lastX = x; } - else if (secant > bb) +} + +bool GradingBSplineCurveImpl::isIdentity() const +{ + bool isIdentity = true; + if(m_curveType == DIAGONAL_B_SPLINE || m_curveType == B_SPLINE || m_curveType == HUE_HUE_B_SPLINE) + { + isIdentity = std::all_of(m_controlPoints.begin(), m_controlPoints.end(), IsDiagonalControlPoint); + } + else if( m_curveType == HORIZONTAL0_B_SPLINE || m_curveType == PERIODIC_HORIZONTAL0_B_SPLINE) { - const float blend = (secant - bb) / (bbb - bb); - ksi = top_bnd + blend * (x_mid - top_bnd); - } - else if (secant >= b_high) + isIdentity = std::all_of(m_controlPoints.begin(), m_controlPoints.end(), IsHorizontal0ControlPoint); + } + else if( m_curveType == HORIZONTAL1_B_SPLINE || m_curveType == PERIODIC_HORIZONTAL1_B_SPLINE) { - ksi = top_bnd; + isIdentity = std::all_of(m_controlPoints.begin(), m_controlPoints.end(), IsHorizontal1ControlPoint); + } else + { + throw Exception("Unknown curve type: Could not determine if curve is identity."); } - else if ((secant > b_low) && (b_high != b_low)) + + if (!isIdentity || !slopesAreDefault()) { - const float blend = (secant - b_low) / (b_high - b_low); - ksi = bottom_bnd + blend * (top_bnd - bottom_bnd); + return false; } - else + return true; +} + +bool IsGradingCurveIdentity(const ConstGradingBSplineCurveRcPtr & curve) +{ + auto curveImpl = dynamic_cast(curve.get()); + if (curveImpl) { - ksi = bottom_bnd; + return curveImpl->isIdentity(); } + return false; +} - return ksi; - } +//------------------------------------------------------------------------------------------------ +// +BSplineCurveType GradingBSplineCurveImpl::getCurveType() const +{ + return m_curveType; +} - //------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ // -void fitHueSpline(const std::vector& outCtrlPnts, - const std::vector& slopes, - std::vector& knots, - std::vector& coefsA, - std::vector& coefsB, - std::vector& coefsC) +void GradingBSplineCurveImpl::setCurveType(BSplineCurveType curveType) { - knots.push_back( outCtrlPnts[0].m_x ); - unsigned numCtrlPnts = outCtrlPnts.size(); - for (unsigned i = 0; i < numCtrlPnts - 1; ++i) - { - const GradingControlPoint& p0 = outCtrlPnts[i]; - const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + m_curveType = curveType; +} - const float dx = p1.m_x - p0.m_x; - const float secantSlope = (p1.m_y - p0.m_y) / dx; - if ( fabsf( (slopes[i] + slopes[i + 1]) - 2.f * secantSlope ) <= 1e-5f ) +//------------------------------------------------------------------------------------------------ +// +void GradingBSplineCurveImpl::computeKnotsAndCoefsForRGBCurve(KnotsCoefs & knotsCoefs, int curveIdx) const +{ + // Skip invalid data and identity. + if (m_controlPoints.size() < 2 || isIdentity()) { - coefsC.push_back( p0.m_y ); - coefsB.push_back( slopes[i] ); - coefsA.push_back( 0.5f * (slopes[i + 1] - slopes[i]) / dx ); } else { - // Calculate the middle knot. - const float ksi = calcKsi(i, outCtrlPnts, slopes); - - // Calculate the coefficients. - const float m_bar = (2.f * secantSlope - slopes[i + 1]) + - (slopes[i + 1] - slopes[i]) * (ksi - p0.m_x) / (p1.m_x - p0.m_x); - const float eta = (m_bar - slopes[i]) / (ksi - p0.m_x); - coefsC.push_back( p0.m_y ); - coefsB.push_back( slopes[i] ); - coefsA.push_back( 0.5f * eta ); - coefsC.push_back( p0.m_y + slopes[i] * (ksi - p0.m_x) + 0.5f * eta * (ksi - p0.m_x) * (ksi - p0.m_x) ); - coefsB.push_back( m_bar ); - coefsA.push_back( 0.5f * (slopes[i + 1] - m_bar) / (p1.m_x - ksi) ); - knots.push_back( ksi ); - } + std::vector knots; + std::vector coefsA; + std::vector coefsB; + std::vector coefsC; + std::vector slopes; - knots.push_back( p1.m_x ); - } -} - -//------------------------------------------------------------------------------------------------ -// -void estimateHueSlopes(std::vector& outCtrlPnts, - std::vector& slopes, - bool isPeriodic, - bool isHorizontal) -{ - slopes.clear(); - unsigned numCtrlPnts = outCtrlPnts.size(); - std::vector secantSlope; - std::vector secantLen; - for (unsigned i = 0; i < numCtrlPnts - 1; ++i) - { - const GradingControlPoint& p0 = outCtrlPnts[i]; - const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + if ( !slopesAreDefault() && (m_slopesArray.size() == m_controlPoints.size()) ) + { + // If the user-supplied slopes are non-zero, use those. + slopes = m_slopesArray; + } + else + { + // Otherwise, estimate slopes based on the control points. + EstimateRGBSlopes(m_controlPoints, slopes); + } - const float del_x = p1.m_x - p0.m_x; // prepHueCurveData ensures this is > 0 - const float del_y = p1.m_y - p0.m_y; - secantSlope.push_back( del_y / del_x ); - secantLen.push_back( sqrt( del_x * del_x + del_y * del_y ) ); - } + FitSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); - if (numCtrlPnts == 2) - { - slopes.push_back( secantSlope[0] ); - slopes.push_back( secantSlope[0] ); - return; - } + bool adjustment_done = AdjustSlopes(m_controlPoints, slopes, knots); - slopes.push_back(0.f); + if (adjustment_done) + { + knots.clear(); + coefsA.clear(); coefsB.clear(); coefsC.clear(); + FitSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); + } - if (isHorizontal) // All horizontal curves and diagonal hue-hue. - { - for (unsigned i = 1; i < numCtrlPnts - 1; ++i) - { - float s = 0.f; - float denom = secantSlope[i] + secantSlope[i - 1]; - if (fabsf(denom) < 1e-3f) - { - const float minval = denom < 0.f ? -1e-3f : 1e-3f; - s = 2.f * secantSlope[i] * secantSlope[i - 1] / minval; - } - else - { - s = 2.f * secantSlope[i] * secantSlope[i - 1] / denom; - } - // Set slope to zero at flat areas or extrema. - if ( secantSlope[i] * secantSlope[i - 1] <= 0.f ) - { - s = 0.f; - } - slopes.push_back( s ); - } - slopes.push_back( 0.5f * ( 3.f * secantSlope[numCtrlPnts - 2] - slopes[numCtrlPnts - 2] ) ); - slopes[0] = 0.5f * ( 3.f * secantSlope[0] - slopes[1] ); - } - else // Diagonal curves except hue-hue (LvL and SvS). - { - unsigned i = 0; - while (true) - { - unsigned j = i; - float DL = secantLen[i]; - while ( ( j < numCtrlPnts - 2 ) && ( fabsf( secantSlope[j + 1] - secantSlope[j] ) < 1e-6f ) ) - { - DL += secantLen[ j + 1 ]; - j++; - } - for (unsigned k = i; k <= j; ++k) - secantLen[k] = DL; - if (j >= numCtrlPnts - 3) - break; - i = j + 1; - } + const int numKnots = static_cast(knotsCoefs.m_numKnots); + const int newKnots = static_cast(knots.size()); + const int numCoefs = static_cast(knotsCoefs.m_numCoefs); + const int newCoefs = static_cast(coefsA.size() * 3); - for (unsigned k = 1; k < numCtrlPnts - 1; ++k) - { - const float s = ( secantLen[k] * secantSlope[k] + secantLen[k - 1] * secantSlope[k - 1] ) / - ( secantLen[k] + secantLen[k - 1] ); - slopes.push_back( s ); - } + if (numKnots + newKnots > KnotsCoefs::MAX_NUM_KNOTS || + numCoefs + newCoefs > KnotsCoefs::MAX_NUM_COEFS) + { + throw Exception("RGB curve: maximum number of control points reached."); + } - const float minSlope = 0.01f; - slopes.push_back( std::max(minSlope, 0.5f * ( 3.f * secantSlope[numCtrlPnts - 2] - slopes[numCtrlPnts - 2] )) ); - slopes[0] = std::max(minSlope, 0.5f * ( 3.f * secantSlope[0] - slopes[1] )); - } + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = numKnots; + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = newKnots; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = numCoefs; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = newCoefs; - // Adjust slopes that are not shape-preserving. - for (unsigned i = 0; i < numCtrlPnts - 1; ++i) - { - float k = 0.2f; - if (fabsf(slopes[i]) > fabsf(slopes[i+1])) - k = 1.f - k; - const float m_near_min = slopes[i] + k * (slopes[i + 1] - slopes[i]); - float scale = 1.f; - if (m_near_min != 0.f) - scale = 0.75f * 2.f * secantSlope[i] / m_near_min; - if (scale < 1.f) - { - slopes[i] = scale * slopes[i]; - slopes[i + 1] = scale * slopes[i + 1]; + const unsigned coefsSize = (unsigned) coefsA.size(); + std::copy(knots.begin(), knots.end(), knotsCoefs.m_knotsArray.begin() + numKnots); + std::copy(coefsA.begin(), coefsA.end(), knotsCoefs.m_coefsArray.begin() + numCoefs); + std::copy(coefsB.begin(), coefsB.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize); + std::copy(coefsC.begin(), coefsC.end(), knotsCoefs.m_coefsArray.begin() + numCoefs + coefsSize * 2); + + knotsCoefs.m_numKnots += newKnots; + knotsCoefs.m_numCoefs += newCoefs; } - } - - // Copy end slopes from the opposite side. - if (isPeriodic) - { - slopes[0] = slopes[numCtrlPnts - 2]; - slopes[numCtrlPnts - 1] = slopes[1]; - } } //------------------------------------------------------------------------------------------------ // -void GradingBSplineCurveImpl::computeKnotsAndCoefsHueCurves(KnotsCoefs & knotsCoefs, int curveIdx) const +void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knotsCoefs, int curveIdx) const { // Return 0 knots and coefficients when the curve is identity. - // TODO: isIdentity() is need to be reworked for the differents b spline types. - // Should impact only performance? - //if (isIdentity()) return; + if (m_controlPoints.size() < 2 || isIdentity()) + { + // Identity curve: offset is -1 and count is 0. + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = -1; + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = 0; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = -1; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = 0; + return; + } bool isPeriodic = false; bool isHorizontal = true; - if (m_curveType == BSplineCurveType::PERIODIC_B_SPLINE || + if (m_curveType == BSplineCurveType::PERIODIC_HORIZONTAL1_B_SPLINE || + m_curveType == BSplineCurveType::PERIODIC_HORIZONTAL0_B_SPLINE || m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE) { isPeriodic = true; @@ -778,22 +804,30 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsHueCurves(KnotsCoefs & knotsCo } std::vector resultCtrlPnts; - prepHueCurveData(m_controlPoints, resultCtrlPnts, isPeriodic, isHorizontal); - - std::vector slopes; + PrepHueCurveData(m_controlPoints, resultCtrlPnts, isPeriodic, isHorizontal); // For the purposes of slope estimation, consider the hue-hue spline to be horizontal. if (m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE) { isHorizontal = true; } - estimateHueSlopes(resultCtrlPnts, slopes, isPeriodic, isHorizontal); + + std::vector slopes; + if ( !slopesAreDefault() && (m_slopesArray.size() == m_controlPoints.size()) ) + { + // If the user-supplied slopes are non-zero, use those. + slopes = m_slopesArray; + } + else + { + EstimateHueSlopes(resultCtrlPnts, slopes, isPeriodic, isHorizontal); + } std::vector knots; std::vector coefsA; std::vector coefsB; std::vector coefsC; - fitHueSpline(resultCtrlPnts, slopes, knots, coefsA, coefsB, coefsC); + FitHueSpline(resultCtrlPnts, slopes, knots, coefsA, coefsB, coefsC); const int numKnots = static_cast(knotsCoefs.m_numKnots); const int newKnots = static_cast(knots.size()); @@ -826,11 +860,11 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int { if(m_curveType == BSplineCurveType::B_SPLINE) { - computeKnotsAndCoefsBSpline(knotsCoefs, curveIdx); + computeKnotsAndCoefsForRGBCurve(knotsCoefs, curveIdx); } else { - computeKnotsAndCoefsHueCurves(knotsCoefs, curveIdx); + computeKnotsAndCoefsForHueCurve(knotsCoefs, curveIdx); } } @@ -850,7 +884,7 @@ void GradingBSplineCurveImpl::AddShaderEval(GpuShaderText & st, // If the curve has the default/identity values the coef data is empty, so return the input. st.newLine() << "if (coefsSets == 0)"; st.newLine() << "{"; - st.newLine() << " return x;"; + st.newLine() << " return identity_x;"; st.newLine() << "}"; st.newLine() << "float knStart = " << knots << "[knotsOffs];"; @@ -902,7 +936,7 @@ void GradingBSplineCurveImpl::AddShaderEval(GpuShaderText & st, // If the curve has the default/identity values the coef data is empty, so return the input. st.newLine() << "if (coefsSets == 0)"; st.newLine() << "{"; - st.newLine() << " return x;"; + st.newLine() << " return identity_x;"; st.newLine() << "}"; st.newLine() << "float knStart = " << knots << "[knotsOffs];"; @@ -957,68 +991,6 @@ void GradingBSplineCurveImpl::AddShaderEval(GpuShaderText & st, } } -void GradingBSplineCurveImpl::AddShaderEvalHueCurve(GpuShaderText & st, - const std::string & knotsOffsets, - const std::string & coefsOffsets, - const std::string & knots, - const std::string & coefs, bool isInv) -{ - st.indent(); - st.newLine() << "int knotsOffs = " << knotsOffsets << "[curveIdx * 2];"; - st.newLine() << "int knotsCnt = " << knotsOffsets << "[curveIdx * 2 + 1];"; - st.newLine() << "int coefsOffs = " << coefsOffsets << "[curveIdx * 2];"; - st.newLine() << "int coefsCnt = " << coefsOffsets << "[curveIdx * 2 + 1];"; - st.newLine() << "int coefsSets = coefsCnt / 3;"; - - // If the curve has the default/identity values the coef data is empty, so return the input. - st.newLine() << "if (coefsSets == 0)"; - st.newLine() << "{"; - st.newLine() << " return identity_x;"; - st.newLine() << "}"; - - st.newLine() << "float knStart = " << knots << "[knotsOffs];"; - st.newLine() << "float knEnd = " << knots << "[knotsOffs + knotsCnt - 1];"; - - st.newLine() << "float y;"; - - st.newLine() << "if (x <= knStart)"; - st.newLine() << "{"; - st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets];"; - st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 2];"; - st.newLine() << " y = (x - knStart) * B + C;"; - st.newLine() << "}"; - - st.newLine() << "else if (x >= knEnd)"; - st.newLine() << "{"; - st.newLine() << " float A = " << coefs << "[coefsOffs + coefsSets - 1];"; - st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets * 2 - 1];"; - st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 3 - 1];"; - st.newLine() << " float kn = " << knots << "[knotsOffs + knotsCnt - 2];"; - st.newLine() << " float t = knEnd - kn;"; - st.newLine() << " float slope = 2. * A * t + B;"; - st.newLine() << " float offs = ( A * t + B ) * t + C;"; - st.newLine() << " y = (x - knEnd) * slope + offs;"; - st.newLine() << "}"; - - st.newLine() << "else"; - st.newLine() << "{"; - st.newLine() << " int i = 0;"; - st.newLine() << " while ( x < " << knots << "[knotsOffs + i] || x > " << knots << "[knotsOffs + i + 1] )"; - st.newLine() << " {"; - st.newLine() << " i++;"; - st.newLine() << " }"; - st.newLine() << " float A = " << coefs << "[coefsOffs + i];"; - st.newLine() << " float B = " << coefs << "[coefsOffs + i + coefsSets];"; - st.newLine() << " float C = " << coefs << "[coefsOffs + i + coefsSets * 2];"; - st.newLine() << " float kn = " << knots << "[knotsOffs + i];"; - st.newLine() << " float t = x - kn;"; - st.newLine() << " y = ( A * t + B ) * t + C;"; - st.newLine() << "}"; - - st.newLine() << "return y;"; - st.dedent(); -} - GradingBSplineCurveImpl::KnotsCoefs::KnotsCoefs(size_t numCurves) { m_knotsOffsetsArray.resize(2 * numCurves); diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h index 48940f971b..acd2acb2bc 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h @@ -24,6 +24,8 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve ~GradingBSplineCurveImpl() = default; GradingBSplineCurveRcPtr createEditableCopy() const override; + BSplineCurveType getCurveType() const override; + void setCurveType(BSplineCurveType curveType) override; size_t getNumControlPoints() const noexcept override; void setNumControlPoints(size_t size) override; const GradingControlPoint & getControlPoint(size_t index) const override; @@ -33,9 +35,6 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve bool slopesAreDefault() const override; void validate() const override; - BSplineCurveType getCurveType() const override; - void setCurveType(BSplineCurveType curveType) override; - bool isIdentity() const; // The KnotsCoefs struct is used when evaluating the curves. Unlike the GradingBSplineCurve @@ -99,9 +98,9 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve // add knots for curves that are simply identity. // // Maximum size of the knots array (for ALL curves). - static constexpr int MAX_NUM_KNOTS = 60; + static constexpr int MAX_NUM_KNOTS = 120; // Maximum size of the coefs array (for ALL curves). - static constexpr int MAX_NUM_COEFS = 180; + static constexpr int MAX_NUM_COEFS = 360; // Pre-processing arrays of length MAX_NUM_KNOTS and MAX_NUM_COEFS. std::vector m_coefsArray; // Contains packed coefs of ALL curves. @@ -121,13 +120,9 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve static void AddShaderEval(GpuShaderText & st, const std::string & knotsOffsets, const std::string & coefsOffsets, const std::string & knots, const std::string & coefs, bool isInv); - - static void AddShaderEvalHueCurve(GpuShaderText & st, - const std::string & knotsOffsets, const std::string & coefsOffsets, - const std::string & knots, const std::string & coefs, bool isInv); private: - void computeKnotsAndCoefsBSpline(KnotsCoefs & knotsCoefs, int curveIdx) const; - void computeKnotsAndCoefsHueCurves(KnotsCoefs & knotsCoefs, int curveIdx) const; + void computeKnotsAndCoefsForRGBCurve(KnotsCoefs & knotsCoefs, int curveIdx) const; + void computeKnotsAndCoefsForHueCurve(KnotsCoefs & knotsCoefs, int curveIdx) const; void validateIndex(size_t index) const; void prepData(const std::vector& inCtrlPnts, diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp index 3ce01247cf..6703092523 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp @@ -229,11 +229,11 @@ void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator st.newLine() << ""; if (shaderCreator->getLanguage() == LANGUAGE_OSL_1 || shaderCreator->getLanguage() == GPU_LANGUAGE_MSL_2_0) { - st.newLine() << st.floatKeyword() << " " << props.m_eval << "(int curveIdx, float x)"; + st.newLine() << st.floatKeyword() << " " << props.m_eval << "(int curveIdx, float x, float identity_x)"; } else { - st.newLine() << st.floatKeyword() << " " << props.m_eval << "(in int curveIdx, in float x)"; + st.newLine() << st.floatKeyword() << " " << props.m_eval << "(in int curveIdx, in float x, in float identity_x)"; } st.newLine() << "{"; st.indent(); @@ -275,13 +275,13 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, const std::string pix(shaderCreator->getPixelName()); // Call the curve evaluation method for each curve. - st.newLine() << pix << ".rgb.r = " << props.m_eval << "(0, " << pix << ".rgb.r);"; // RED - st.newLine() << pix << ".rgb.g = " << props.m_eval << "(1, " << pix << ".rgb.g);"; // GREEN - st.newLine() << pix << ".rgb.b = " << props.m_eval << "(2, " << pix << ".rgb.b);"; // BLUE + st.newLine() << pix << ".rgb.r = " << props.m_eval << "(0, " << pix << ".rgb.r, " << pix << ".rgb.r);"; // RED + st.newLine() << pix << ".rgb.g = " << props.m_eval << "(1, " << pix << ".rgb.g, " << pix << ".rgb.g);"; // GREEN + st.newLine() << pix << ".rgb.b = " << props.m_eval << "(2, " << pix << ".rgb.b, " << pix << ".rgb.b);"; // BLUE // TODO: vectorize master. - st.newLine() << pix << ".rgb.r = " << props.m_eval << "(3, " << pix << ".rgb.r);"; // MASTER - st.newLine() << pix << ".rgb.g = " << props.m_eval << "(3, " << pix << ".rgb.g);"; // MASTER - st.newLine() << pix << ".rgb.b = " << props.m_eval << "(3, " << pix << ".rgb.b);"; // MASTER + st.newLine() << pix << ".rgb.r = " << props.m_eval << "(3, " << pix << ".rgb.r, " << pix << ".rgb.r);"; // MASTER + st.newLine() << pix << ".rgb.g = " << props.m_eval << "(3, " << pix << ".rgb.g, " << pix << ".rgb.g);"; // MASTER + st.newLine() << pix << ".rgb.b = " << props.m_eval << "(3, " << pix << ".rgb.b, " << pix << ".rgb.b);"; // MASTER if (doLinToLog) { @@ -323,12 +323,12 @@ void AddGCInverseShader(GpuShaderCreatorRcPtr & shaderCreator, const std::string pix(shaderCreator->getPixelName()); // Call the curve evaluation method for each curve. - st.newLine() << pix << ".rgb.r = " << props.m_eval << "(3, " << pix << ".rgb.r);"; // MASTER - st.newLine() << pix << ".rgb.g = " << props.m_eval << "(3, " << pix << ".rgb.g);"; // MASTER - st.newLine() << pix << ".rgb.b = " << props.m_eval << "(3, " << pix << ".rgb.b);"; // MASTER - st.newLine() << pix << ".rgb.r = " << props.m_eval << "(0, " << pix << ".rgb.r);"; // RED - st.newLine() << pix << ".rgb.g = " << props.m_eval << "(1, " << pix << ".rgb.g);"; // GREEN - st.newLine() << pix << ".rgb.b = " << props.m_eval << "(2, " << pix << ".rgb.b);"; // BLUE + st.newLine() << pix << ".rgb.r = " << props.m_eval << "(3, " << pix << ".rgb.r, " << pix << ".rgb.r);"; // MASTER + st.newLine() << pix << ".rgb.g = " << props.m_eval << "(3, " << pix << ".rgb.g, " << pix << ".rgb.g);"; // MASTER + st.newLine() << pix << ".rgb.b = " << props.m_eval << "(3, " << pix << ".rgb.b, " << pix << ".rgb.b);"; // MASTER + st.newLine() << pix << ".rgb.r = " << props.m_eval << "(0, " << pix << ".rgb.r, " << pix << ".rgb.r);"; // RED + st.newLine() << pix << ".rgb.g = " << props.m_eval << "(1, " << pix << ".rgb.g, " << pix << ".rgb.g);"; // GREEN + st.newLine() << pix << ".rgb.b = " << props.m_eval << "(2, " << pix << ".rgb.b, " << pix << ".rgb.b);"; // BLUE if (doLinToLog) { diff --git a/tests/cpu/CMakeLists.txt b/tests/cpu/CMakeLists.txt index baf7245636..863f034aeb 100755 --- a/tests/cpu/CMakeLists.txt +++ b/tests/cpu/CMakeLists.txt @@ -305,9 +305,9 @@ set(TESTS transforms/ExposureContrastTransform_tests.cpp transforms/FileTransform_tests.cpp transforms/FixedFunctionTransform_tests.cpp + transforms/GradingHueCurveTransform_tests.cpp transforms/GradingPrimaryTransform_tests.cpp transforms/GradingRGBCurveTransform_tests.cpp - transforms/HueCurveTransform_tests.cpp transforms/GradingToneTransform_tests.cpp transforms/GroupTransform_tests.cpp transforms/LogAffineTransform_tests.cpp diff --git a/tests/cpu/transforms/HueCurveTransform_tests.cpp b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp similarity index 100% rename from tests/cpu/transforms/HueCurveTransform_tests.cpp rename to tests/cpu/transforms/GradingHueCurveTransform_tests.cpp From 3801f475b2a7ef2309f31c014b26611c12b2b349 Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Mon, 17 Jun 2024 10:58:52 -0400 Subject: [PATCH 05/30] Review Fixes P4 (cherry picked from commit b243d1d3e37db1a3bbfcad4271142fa973687d89) Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTypes.h | 13 +- .../fileformats/ctf/CTFTransform.cpp | 6 +- .../ops/fixedfunction/FixedFunctionOpGPU.cpp | 52 +- .../ops/gradinghuecurve/GradingHueCurve.cpp | 32 +- .../gradinghuecurve/GradingHueCurveOpGPU.cpp | 1 - .../gradingrgbcurve/GradingBSplineCurve.cpp | 613 +++++++++--------- .../ops/gradingrgbcurve/GradingBSplineCurve.h | 4 - .../transforms/GradingHueCurveTransform.cpp | 2 +- 8 files changed, 353 insertions(+), 370 deletions(-) diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index cfd0672125..b3a6961c21 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -592,13 +592,12 @@ enum HueCurveType enum BSplineCurveType { - B_SPLINE = 0, //!< Monotonic quadratic B-spline based function. - DIAGONAL_B_SPLINE, //!< Monotonic quadratic B-spline based function (newer algorithm). - HUE_HUE_B_SPLINE, //!< Special B-spline used for the hue vs. hue curve (monotonic and periodic). - PERIODIC_HORIZONTAL1_B_SPLINE, //!< Periodic non-monotonic B-spline centered at 1 used for some of the hue curves. - PERIODIC_HORIZONTAL0_B_SPLINE, //!< Periodic non-monotonic B-spline centered at 0 used for some of the hue curves. - HORIZONTAL1_B_SPLINE, //!< Non-monotonic B-spline, centered at 1, used for HueSat, HueLum, and SatLum curves. - HORIZONTAL0_B_SPLINE //!< Non-monotonic B-spline, centered at 0, used for HueFX curve. + B_SPLINE = 0, //!< Monotonic quadratic B-spline used for the RGBM curves. + DIAGONAL_B_SPLINE, //!< Monotonic quadratic B-spline for the sat-sat and lum-lum curves. + HUE_HUE_B_SPLINE, //!< Monotonic and periodic B-spline used for the hue-hue curve. + PERIODIC_1_B_SPLINE, //!< Periodic, horizontal (at 1) B-spline for hue-sat and hue-lum curves. + PERIODIC_0_B_SPLINE, //!< Periodic, horizontal (at 0) B-spline used for the hue-fx curve. + HORIZONTAL1_B_SPLINE, //!< Horizontal (at 1) B-spline used for the lum-sat and sat-lum curves. }; /// Types for uniform data. diff --git a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp index a5a63f7836..927f2c2c21 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp @@ -1639,7 +1639,7 @@ class GradingHueCurveWriter : public OpWriter }; GradingHueCurveWriter::GradingHueCurveWriter(XmlFormatter & formatter, - ConstGradingHueCurveOpDataRcPtr curves) + ConstGradingHueCurveOpDataRcPtr curves) : OpWriter(formatter) , m_curves(curves) { @@ -1676,7 +1676,7 @@ void GradingHueCurveWriter::getAttributes(XmlFormatter::Attributes& attributes) } void GradingHueCurveWriter::writeCurve(const char * tag, - const ConstGradingBSplineCurveRcPtr & curve) const + const ConstGradingBSplineCurveRcPtr & curve) const { m_formatter.writeStartTag(tag, XmlFormatter::Attributes()); { @@ -1726,7 +1726,7 @@ void GradingHueCurveWriter::writeContent() const const auto & vals = m_curves->getValue(); auto & defCurve = (m_curves->getStyle() == GRADING_LIN) ? GradingHueCurveImpl::DefaultCurvesLin: - GradingHueCurveImpl::DefaultCurves; + GradingHueCurveImpl::DefaultCurves; static const std::vector curveTags = { TAG_HUE_CURVE_HUE_HUE, TAG_HUE_CURVE_HUE_SAT, diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp index 7bbd742d70..5b1aba8b78 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp @@ -1705,21 +1705,21 @@ void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "float distRGB = dot( abs(RGBm), ones );"; if (min0 > 0.f) { - ss.newLine() << "float sumRGB = dot( " << pxl << ".rgb, ones );"; - ss.newLine() << "float sat_hi = distRGB / (0.15 + sumRGB);"; - ss.newLine() << "float sat_lo = distRGB * 5.;"; - ss.newLine() << "float alpha = clamp( (luma - 0.001) / (0.01 - 0.001), 0., 1.);"; + ss.newLine() << "float sumRGB = dot( " << pxl << ".rgb, ones );"; + ss.newLine() << "float sat_hi = distRGB / (0.15 + sumRGB);"; + ss.newLine() << "float sat_lo = distRGB * 5.;"; + ss.newLine() << "float alpha = clamp( (luma - 0.001) / (0.01 - 0.001), 0., 1.);"; - ss.newLine() << "float sat = sat_lo + alpha * (sat_hi - sat_lo);"; - ss.newLine() << "sat *= 1.4;"; + ss.newLine() << "float sat = sat_lo + alpha * (sat_hi - sat_lo);"; + ss.newLine() << "sat *= 1.4;"; } else if (min0 < 0.f) { - ss.newLine() << "float sat = distRGB * 4.;"; + ss.newLine() << "float sat = distRGB * 4.;"; } else { - ss.newLine() << "float sat = distRGB * 1.25;"; + ss.newLine() << "float sat = distRGB * 1.25;"; } ss.newLine() << "float hue = 0.0;"; ss.newLine() << "if (minRGB != maxRGB) {"; @@ -1754,30 +1754,30 @@ void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "float distRGB = dot( abs(RGB0 - luma), ones );"; if (min0 > 0.f) { - ss.newLine() << "float sumRGB = dot( RGB0, ones );"; - ss.newLine() << "float k = 0.15;"; - ss.newLine() << "float lo_gain = 5.;"; - ss.newLine() << "sat /= 1.4;"; - ss.newLine() << "float tmp = -sat * sumRGB + sat * 3. * luma + distRGB;"; - ss.newLine() << "float s1 = (tmp == 0.) ? 0. : sat * (k + 3. * luma) / tmp;"; - ss.newLine() << "float s0 = sat / max(1e-10, distRGB * lo_gain);"; - ss.newLine() << "float alpha = clamp( (luma - 0.001) / (0.01 - 0.001), 0., 1.);"; - ss.newLine() << "float a = distRGB * lo_gain * (1. - alpha) * (sumRGB - 3. * luma);"; - ss.newLine() << "float b = distRGB * lo_gain * (1. - alpha) * (k + 3. * luma) + distRGB * alpha - sat * (sumRGB - 3. * luma);"; - ss.newLine() << "float c = -sat * (k + 3. * luma);"; - ss.newLine() << "float discrim = sqrt( b * b - 4. * a * c );"; - ss.newLine() << "float denom = -discrim - b;"; - ss.newLine() << "float sm = (2. * c) / denom;"; - ss.newLine() << "sm = (sm >= 0.) ? sm : (2. * c) / (denom + discrim * 2.);"; - ss.newLine() << "float gainS = (alpha == 1.) ? s1 : (alpha == 0.) ? s0 : sm;"; + ss.newLine() << "float sumRGB = dot( RGB0, ones );"; + ss.newLine() << "float k = 0.15;"; + ss.newLine() << "float lo_gain = 5.;"; + ss.newLine() << "sat /= 1.4;"; + ss.newLine() << "float tmp = -sat * sumRGB + sat * 3. * luma + distRGB;"; + ss.newLine() << "float s1 = (tmp == 0.) ? 0. : sat * (k + 3. * luma) / tmp;"; + ss.newLine() << "float s0 = sat / max(1e-10, distRGB * lo_gain);"; + ss.newLine() << "float alpha = clamp( (luma - 0.001) / (0.01 - 0.001), 0., 1.);"; + ss.newLine() << "float a = distRGB * lo_gain * (1. - alpha) * (sumRGB - 3. * luma);"; + ss.newLine() << "float b = distRGB * lo_gain * (1. - alpha) * (k + 3. * luma) + distRGB * alpha - sat * (sumRGB - 3. * luma);"; + ss.newLine() << "float c = -sat * (k + 3. * luma);"; + ss.newLine() << "float discrim = sqrt( b * b - 4. * a * c );"; + ss.newLine() << "float denom = -discrim - b;"; + ss.newLine() << "float sm = (2. * c) / denom;"; + ss.newLine() << "sm = (sm >= 0.) ? sm : (2. * c) / (denom + discrim * 2.);"; + ss.newLine() << "float gainS = (alpha == 1.) ? s1 : (alpha == 0.) ? s0 : sm;"; } else if (min0 < 0.f) { - ss.newLine() << "float gainS = sat / max(1e-10, distRGB * 4.);"; + ss.newLine() << "float gainS = sat / max(1e-10, distRGB * 4.);"; } else { - ss.newLine() << "float gainS = sat / max(1e-10, distRGB * 1.25);"; + ss.newLine() << "float gainS = sat / max(1e-10, distRGB * 1.25);"; } ss.newLine() << "" << pxl << ".rgb = luma + gainS * (RGB0 - luma);"; } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp index fae0331516..f2352c6880 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp @@ -45,8 +45,8 @@ static const std::vector DefaultLumLumLinCtrl{ { -7.0f, -7. } const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueHue(DefaultHueHueCtrl, BSplineCurveType::HUE_HUE_B_SPLINE ); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueSat(DefaultHueSatCtrl, BSplineCurveType::PERIODIC_HORIZONTAL1_B_SPLINE ); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueFx(DefaultHueFxCtrl, BSplineCurveType::PERIODIC_HORIZONTAL0_B_SPLINE ); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueSat(DefaultHueSatCtrl, BSplineCurveType::PERIODIC_1_B_SPLINE ); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueFx(DefaultHueFxCtrl, BSplineCurveType::PERIODIC_0_B_SPLINE ); const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSat(DefaultLumSatCtrl, BSplineCurveType::HORIZONTAL1_B_SPLINE); const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSatLin(DefaultLumSatLinCtrl, BSplineCurveType::HORIZONTAL1_B_SPLINE); const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultSatSat(DefaultSatSatCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); @@ -56,24 +56,24 @@ const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumLumLin(DefaultLumLu const std::array, static_cast(HUE_NUM_CURVES)> GradingHueCurveImpl::DefaultCurvesLin( { std::ref(DefaultHueHue), - std::ref(DefaultHueSat), - std::ref(DefaultHueSat), - std::ref(DefaultLumSatLin), - std::ref(DefaultSatSat), - std::ref(DefaultLumLumLin), - std::ref(DefaultSatLum), - std::ref(DefaultHueFx) }); + std::ref(DefaultHueSat), + std::ref(DefaultHueSat), // HUE_LUM use the same as HUE_SAT + std::ref(DefaultLumSatLin), + std::ref(DefaultSatSat), + std::ref(DefaultLumLumLin), + std::ref(DefaultSatLum), + std::ref(DefaultHueFx) }); const std::array, static_cast(HUE_NUM_CURVES)> GradingHueCurveImpl::DefaultCurves( { std::ref(DefaultHueHue), - std::ref(DefaultHueSat), - std::ref(DefaultHueSat), - std::ref(DefaultLumSat), - std::ref(DefaultSatSat), - std::ref(DefaultLumLum), - std::ref(DefaultSatLum), - std::ref(DefaultHueFx) }); + std::ref(DefaultHueSat), + std::ref(DefaultHueSat), // HUE_LUM use the same as HUE_SAT + std::ref(DefaultLumSat), + std::ref(DefaultSatSat), + std::ref(DefaultLumLum), + std::ref(DefaultSatLum), + std::ref(DefaultHueFx) }); GradingHueCurveImpl::GradingHueCurveImpl() : GradingHueCurveImpl(GRADING_LOG) diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp index 6a752aef31..363bf44485 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp @@ -265,7 +265,6 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, st.indent(); } - // Add the conversion from RGB to HSY. { FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index bd55a77b10..d388562be3 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -11,344 +11,324 @@ #include "GpuShaderUtils.h" #include "ops/gradingrgbcurve/GradingBSplineCurve.h" -namespace -{ - -} // anonymous namespace - namespace OCIO_NAMESPACE { namespace { - -bool IsDiagonalControlPoint( const GradingControlPoint& cp ) -{ - return cp.m_x == cp.m_y; -} - -bool IsHorizontal0ControlPoint( const GradingControlPoint& cp ) -{ - return cp.m_y == 0.f; -} - -bool IsHorizontal1ControlPoint( const GradingControlPoint& cp ) -{ - return cp.m_y == 1.f; -} - //------------------------------------------------------------------------------------------------ // void PrepHueCurveData(const std::vector& ctrlPnts, - std::vector& outCtrlPnts, - bool isPeriodic, - bool isHorizontal) + std::vector& outCtrlPnts, + bool isPeriodic, + bool isHorizontal) { - size_t numCtrlPnts = ctrlPnts.size(); - for (unsigned i = 0; i < numCtrlPnts; ++i) - { - const float xval = ctrlPnts[ i ].m_x; - const float yval = ctrlPnts[ i ].m_y; - // Wrap periodic x values into [0,1). - if (isPeriodic && (xval < 0.f)) - { - outCtrlPnts.push_back(GradingControlPoint(xval + 1.f, isHorizontal ? yval : yval + 1.f)); - } - else if (isPeriodic && (xval >= 1.f)) + size_t numCtrlPnts = ctrlPnts.size(); + for (unsigned i = 0; i < numCtrlPnts; ++i) { - outCtrlPnts.push_back(GradingControlPoint(xval - 1.f, isHorizontal ? yval : yval - 1.f)); + const float xval = ctrlPnts[ i ].m_x; + const float yval = ctrlPnts[ i ].m_y; + // Wrap periodic x values into [0,1). + if (isPeriodic && (xval < 0.f)) + { + outCtrlPnts.push_back(GradingControlPoint(xval + 1.f, isHorizontal ? yval : yval + 1.f)); + } + else if (isPeriodic && (xval >= 1.f)) + { + outCtrlPnts.push_back(GradingControlPoint(xval - 1.f, isHorizontal ? yval : yval - 1.f)); + } + else + { + outCtrlPnts.push_back(GradingControlPoint(xval, yval)); + } } - else + + // Sort x and y based on x order. + for (unsigned i = 0; i < numCtrlPnts; ++i) { - outCtrlPnts.push_back(GradingControlPoint(xval, yval)); + unsigned min_index = i; + float min_val = outCtrlPnts[i].m_x; + for (unsigned j = i + 1; j < numCtrlPnts; ++j) + { + if (outCtrlPnts[j].m_x < min_val) + { + min_val = outCtrlPnts[j].m_x; + min_index = j; + } + } + + std::swap( outCtrlPnts[i], outCtrlPnts[min_index] ); } - } - - // Sort x and y based on x order. - for (unsigned i = 0; i < numCtrlPnts; ++i) - { - unsigned min_index = i; - float min_val = outCtrlPnts[i].m_x; - for (unsigned j = i + 1; j < numCtrlPnts; ++j) + + // Ensure that there is a minimum space between the x values. + const float tol = 2e-3f; + const float x_span = outCtrlPnts[numCtrlPnts - 1].m_x - outCtrlPnts[0].m_x; + for (unsigned i = 1; i < outCtrlPnts.size(); ++i) { - if (outCtrlPnts[j].m_x < min_val) + if ( (outCtrlPnts[i].m_x - outCtrlPnts[i - 1].m_x) < x_span * tol ) { - min_val = outCtrlPnts[j].m_x; - min_index = j; + outCtrlPnts[i].m_x = outCtrlPnts[i - 1].m_x + x_span * tol; } } - - std::swap( outCtrlPnts[i], outCtrlPnts[min_index] ); - } - - // Ensure that there is a minimum space between the x values. - const float tol = 2e-3f; - const float x_span = outCtrlPnts[numCtrlPnts - 1].m_x - outCtrlPnts[0].m_x; - for (unsigned i = 1; i < outCtrlPnts.size(); ++i) - { - if ( (outCtrlPnts[i].m_x - outCtrlPnts[i - 1].m_x) < x_span * tol ) + if (!isHorizontal) { - outCtrlPnts[i].m_x = outCtrlPnts[i - 1].m_x + x_span * tol; + const float y_span = outCtrlPnts[numCtrlPnts - 1].m_y - outCtrlPnts[0].m_y; + for (unsigned i = 1; i < outCtrlPnts.size(); ++i) + { + if ( (outCtrlPnts[i].m_y - outCtrlPnts[i - 1].m_y) < y_span * tol ) + { + outCtrlPnts[i].m_y = outCtrlPnts[i - 1].m_y + y_span * tol; + } + } } - } - if (!isHorizontal) - { - const float y_span = outCtrlPnts[numCtrlPnts - 1].m_y - outCtrlPnts[0].m_y; - for (unsigned i = 1; i < outCtrlPnts.size(); ++i) + + if (isPeriodic) { - if ( (outCtrlPnts[i].m_y - outCtrlPnts[i - 1].m_y) < y_span * tol ) - { - outCtrlPnts[i].m_y = outCtrlPnts[i - 1].m_y + y_span * tol; - } + // Copy a value from each side and wrap it around to the other side. + GradingControlPoint firstCtrlPnt = outCtrlPnts[numCtrlPnts - 1]; + firstCtrlPnt.m_x -= 1.f; + firstCtrlPnt.m_y = isHorizontal ? firstCtrlPnt.m_y : firstCtrlPnt.m_y - 1.f; + outCtrlPnts.insert(outCtrlPnts.begin(), firstCtrlPnt); + + GradingControlPoint lastCtrlPnt = outCtrlPnts[1]; + lastCtrlPnt.m_x += 1.f; + lastCtrlPnt.m_y = isHorizontal ? lastCtrlPnt.m_y : lastCtrlPnt.m_y + 1.f; + outCtrlPnts.push_back(lastCtrlPnt); } - } - - if (isPeriodic) - { - // Copy a value from each side and wrap it around to the other side. - GradingControlPoint firstCtrlPnt = outCtrlPnts[numCtrlPnts - 1]; - firstCtrlPnt.m_x -= 1.f; - firstCtrlPnt.m_y = isHorizontal ? firstCtrlPnt.m_y : firstCtrlPnt.m_y - 1.f; - outCtrlPnts.insert(outCtrlPnts.begin(), firstCtrlPnt); - - GradingControlPoint lastCtrlPnt = outCtrlPnts[1]; - lastCtrlPnt.m_x += 1.f; - lastCtrlPnt.m_y = isHorizontal ? lastCtrlPnt.m_y : lastCtrlPnt.m_y + 1.f; - outCtrlPnts.push_back(lastCtrlPnt); - } } //------------------------------------------------------------------------------------------------ // float CalcKsi(unsigned i, - const std::vector& outCtrlPnts, - const std::vector& slopes) -{ - const GradingControlPoint& p0 = outCtrlPnts[i]; - const GradingControlPoint& p1 = outCtrlPnts[i + 1]; - - const float k = 0.2f; - const float dx = p1.m_x - p0.m_x; - const float secantSlope = (p1.m_y - p0.m_y) / dx; - float secant = secantSlope; - float m0 = slopes[i]; - float m1 = slopes[i + 1]; - if (secant < 0.f) - { - m0 = -slopes[i]; m1 = -slopes[i + 1]; - secant = -secant; - } - - const float x_mid = p0.m_x + 0.5f * dx; - const float left_bnd = p0.m_x + dx * k; - const float right_bnd = p1.m_x - dx * k; - float top_bnd = left_bnd; - float bottom_bnd = right_bnd; - float m_min = m0; - float m_max = m1; - if (m0 > m1) - { - m_max = m0; m_min = m1; - top_bnd = right_bnd; bottom_bnd = left_bnd; - } - - const float dm = m_max - m_min; - const float b = 1.f - 0.5f * k; - const float b_high = m_min + b * dm; - const float b_low = m_min + (1.f - b) * dm; - const float bbb = m_max * 4.f; - const float bb = m_max * 1.1f; - const float m_rel_diff = dm / std::max(0.01f, m_max); - const float alpha = std::max( 0.f, std::min( (m_rel_diff - 0.05f) / (0.75f - 0.05f), 1.f ) ); - top_bnd = x_mid + alpha * (top_bnd - x_mid); - bottom_bnd = x_mid + alpha * (bottom_bnd - x_mid); - - // Calculate the middle knot. - float ksi = 0.f; - if (secant >= bbb) - { - ksi = x_mid; - } - else if (secant > bb) - { - const float blend = (secant - bb) / (bbb - bb); - ksi = top_bnd + blend * (x_mid - top_bnd); - } - else if (secant >= b_high) - { - ksi = top_bnd; - } - else if ((secant > b_low) && (b_high != b_low)) - { - const float blend = (secant - b_low) / (b_high - b_low); - ksi = bottom_bnd + blend * (top_bnd - bottom_bnd); - } - else - { - ksi = bottom_bnd; - } - return ksi; -} - -//------------------------------------------------------------------------------------------------ -// -void FitHueSpline(const std::vector& outCtrlPnts, - const std::vector& slopes, - std::vector& knots, - std::vector& coefsA, - std::vector& coefsB, - std::vector& coefsC) + const std::vector& outCtrlPnts, + const std::vector& slopes) { - knots.push_back( outCtrlPnts[0].m_x ); - unsigned numCtrlPnts = outCtrlPnts.size(); - for (unsigned i = 0; i < numCtrlPnts - 1; ++i) - { const GradingControlPoint& p0 = outCtrlPnts[i]; const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + const float k = 0.2f; const float dx = p1.m_x - p0.m_x; const float secantSlope = (p1.m_y - p0.m_y) / dx; + float secant = secantSlope; + float m0 = slopes[i]; + float m1 = slopes[i + 1]; + if (secant < 0.f) + { + m0 = -slopes[i]; m1 = -slopes[i + 1]; + secant = -secant; + } - if ( fabsf( (slopes[i] + slopes[i + 1]) - 2.f * secantSlope ) <= 1e-5f ) + const float x_mid = p0.m_x + 0.5f * dx; + const float left_bnd = p0.m_x + dx * k; + const float right_bnd = p1.m_x - dx * k; + float top_bnd = left_bnd; + float bottom_bnd = right_bnd; + float m_min = m0; + float m_max = m1; + if (m0 > m1) + { + m_max = m0; m_min = m1; + top_bnd = right_bnd; bottom_bnd = left_bnd; + } + + const float dm = m_max - m_min; + const float b = 1.f - 0.5f * k; + const float b_high = m_min + b * dm; + const float b_low = m_min + (1.f - b) * dm; + const float bbb = m_max * 4.f; + const float bb = m_max * 1.1f; + const float m_rel_diff = dm / std::max(0.01f, m_max); + const float alpha = std::max( 0.f, std::min( (m_rel_diff - 0.05f) / (0.75f - 0.05f), 1.f ) ); + top_bnd = x_mid + alpha * (top_bnd - x_mid); + bottom_bnd = x_mid + alpha * (bottom_bnd - x_mid); + + // Calculate the middle knot. + float ksi = 0.f; + if (secant >= bbb) + { + ksi = x_mid; + } + else if (secant > bb) + { + const float blend = (secant - bb) / (bbb - bb); + ksi = top_bnd + blend * (x_mid - top_bnd); + } + else if (secant >= b_high) { - coefsC.push_back( p0.m_y ); - coefsB.push_back( slopes[i] ); - coefsA.push_back( 0.5f * (slopes[i + 1] - slopes[i]) / dx ); + ksi = top_bnd; + } + else if ((secant > b_low) && (b_high != b_low)) + { + const float blend = (secant - b_low) / (b_high - b_low); + ksi = bottom_bnd + blend * (top_bnd - bottom_bnd); } else { - // Calculate the middle knot. - const float ksi = CalcKsi(i, outCtrlPnts, slopes); - - // Calculate the coefficients. - const float m_bar = (2.f * secantSlope - slopes[i + 1]) + - (slopes[i + 1] - slopes[i]) * (ksi - p0.m_x) / (p1.m_x - p0.m_x); - const float eta = (m_bar - slopes[i]) / (ksi - p0.m_x); - coefsC.push_back( p0.m_y ); - coefsB.push_back( slopes[i] ); - coefsA.push_back( 0.5f * eta ); - coefsC.push_back( p0.m_y + slopes[i] * (ksi - p0.m_x) + 0.5f * eta * (ksi - p0.m_x) * (ksi - p0.m_x) ); - coefsB.push_back( m_bar ); - coefsA.push_back( 0.5f * (slopes[i + 1] - m_bar) / (p1.m_x - ksi) ); - knots.push_back( ksi ); + ksi = bottom_bnd; } + return ksi; +} - knots.push_back( p1.m_x ); - } +//------------------------------------------------------------------------------------------------ +// +void FitHueSpline(const std::vector& outCtrlPnts, + const std::vector& slopes, + std::vector& knots, + std::vector& coefsA, + std::vector& coefsB, + std::vector& coefsC) +{ + knots.push_back( outCtrlPnts[0].m_x ); + unsigned numCtrlPnts = outCtrlPnts.size(); + for (unsigned i = 0; i < numCtrlPnts - 1; ++i) + { + const GradingControlPoint& p0 = outCtrlPnts[i]; + const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + + const float dx = p1.m_x - p0.m_x; + const float secantSlope = (p1.m_y - p0.m_y) / dx; + + if ( fabsf( (slopes[i] + slopes[i + 1]) - 2.f * secantSlope ) <= 1e-5f ) + { + coefsC.push_back( p0.m_y ); + coefsB.push_back( slopes[i] ); + coefsA.push_back( 0.5f * (slopes[i + 1] - slopes[i]) / dx ); + } + else + { + // Calculate the middle knot. + const float ksi = CalcKsi(i, outCtrlPnts, slopes); + + // Calculate the coefficients. + const float m_bar = (2.f * secantSlope - slopes[i + 1]) + + (slopes[i + 1] - slopes[i]) * (ksi - p0.m_x) / (p1.m_x - p0.m_x); + const float eta = (m_bar - slopes[i]) / (ksi - p0.m_x); + coefsC.push_back( p0.m_y ); + coefsB.push_back( slopes[i] ); + coefsA.push_back( 0.5f * eta ); + coefsC.push_back( p0.m_y + slopes[i] * (ksi - p0.m_x) + 0.5f * eta * (ksi - p0.m_x) * (ksi - p0.m_x) ); + coefsB.push_back( m_bar ); + coefsA.push_back( 0.5f * (slopes[i + 1] - m_bar) / (p1.m_x - ksi) ); + knots.push_back( ksi ); + } + + knots.push_back( p1.m_x ); + } } //------------------------------------------------------------------------------------------------ // void EstimateHueSlopes(std::vector& outCtrlPnts, - std::vector& slopes, - bool isPeriodic, - bool isHorizontal) + std::vector& slopes, + bool isPeriodic, + bool isHorizontal) { - slopes.clear(); - unsigned numCtrlPnts = outCtrlPnts.size(); - std::vector secantSlope; - std::vector secantLen; - for (unsigned i = 0; i < numCtrlPnts - 1; ++i) - { - const GradingControlPoint& p0 = outCtrlPnts[i]; - const GradingControlPoint& p1 = outCtrlPnts[i + 1]; - - const float del_x = p1.m_x - p0.m_x; // PrepHueCurveData ensures this is > 0 - const float del_y = p1.m_y - p0.m_y; - secantSlope.push_back( del_y / del_x ); - secantLen.push_back( sqrt( del_x * del_x + del_y * del_y ) ); - } - - if (numCtrlPnts == 2) - { - slopes.push_back( secantSlope[0] ); - slopes.push_back( secantSlope[0] ); - return; - } - - slopes.push_back(0.f); - - if (isHorizontal) // All horizontal curves and diagonal hue-hue. - { - for (unsigned i = 1; i < numCtrlPnts - 1; ++i) + slopes.clear(); + unsigned numCtrlPnts = outCtrlPnts.size(); + std::vector secantSlope; + std::vector secantLen; + for (unsigned i = 0; i < numCtrlPnts - 1; ++i) { - float s = 0.f; - float denom = secantSlope[i] + secantSlope[i - 1]; - if (fabsf(denom) < 1e-3f) - { - const float minval = denom < 0.f ? -1e-3f : 1e-3f; - s = 2.f * secantSlope[i] * secantSlope[i - 1] / minval; - } - else - { - s = 2.f * secantSlope[i] * secantSlope[i - 1] / denom; - } - // Set slope to zero at flat areas or extrema. - if ( secantSlope[i] * secantSlope[i - 1] <= 0.f ) - { - s = 0.f; - } - slopes.push_back( s ); + const GradingControlPoint& p0 = outCtrlPnts[i]; + const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + + const float del_x = p1.m_x - p0.m_x; // PrepHueCurveData ensures this is > 0 + const float del_y = p1.m_y - p0.m_y; + secantSlope.push_back( del_y / del_x ); + secantLen.push_back( sqrt( del_x * del_x + del_y * del_y ) ); } - slopes.push_back( 0.5f * ( 3.f * secantSlope[numCtrlPnts - 2] - slopes[numCtrlPnts - 2] ) ); - slopes[0] = 0.5f * ( 3.f * secantSlope[0] - slopes[1] ); - } - else // Diagonal curves except hue-hue (LvL and SvS). - { - unsigned i = 0; - while (true) + + if (numCtrlPnts == 2) { - unsigned j = i; - float DL = secantLen[i]; - while ( ( j < numCtrlPnts - 2 ) && ( fabsf( secantSlope[j + 1] - secantSlope[j] ) < 1e-6f ) ) - { - DL += secantLen[ j + 1 ]; - j++; - } - for (unsigned k = i; k <= j; ++k) - secantLen[k] = DL; - if (j >= numCtrlPnts - 3) - break; - i = j + 1; + slopes.push_back( secantSlope[0] ); + slopes.push_back( secantSlope[0] ); + return; } - for (unsigned k = 1; k < numCtrlPnts - 1; ++k) + slopes.push_back(0.f); + + if (isHorizontal) // All horizontal curves and diagonal hue-hue. { - const float s = ( secantLen[k] * secantSlope[k] + secantLen[k - 1] * secantSlope[k - 1] ) / - ( secantLen[k] + secantLen[k - 1] ); - slopes.push_back( s ); + for (unsigned i = 1; i < numCtrlPnts - 1; ++i) + { + float s = 0.f; + float denom = secantSlope[i] + secantSlope[i - 1]; + if (fabsf(denom) < 1e-3f) + { + const float minval = denom < 0.f ? -1e-3f : 1e-3f; + s = 2.f * secantSlope[i] * secantSlope[i - 1] / minval; + } + else + { + s = 2.f * secantSlope[i] * secantSlope[i - 1] / denom; + } + // Set slope to zero at flat areas or extrema. + if ( secantSlope[i] * secantSlope[i - 1] <= 0.f ) + { + s = 0.f; + } + slopes.push_back( s ); + } + slopes.push_back( 0.5f * ( 3.f * secantSlope[numCtrlPnts - 2] - slopes[numCtrlPnts - 2] ) ); + slopes[0] = 0.5f * ( 3.f * secantSlope[0] - slopes[1] ); + } + else // Diagonal curves except hue-hue (LvL and SvS). + { + unsigned i = 0; + while (true) + { + unsigned j = i; + float DL = secantLen[i]; + while ( ( j < numCtrlPnts - 2 ) && ( fabsf( secantSlope[j + 1] - secantSlope[j] ) < 1e-6f ) ) + { + DL += secantLen[ j + 1 ]; + j++; + } + for (unsigned k = i; k <= j; ++k) + secantLen[k] = DL; + if (j >= numCtrlPnts - 3) + break; + i = j + 1; + } + + for (unsigned k = 1; k < numCtrlPnts - 1; ++k) + { + const float s = ( secantLen[k] * secantSlope[k] + secantLen[k - 1] * secantSlope[k - 1] ) / + ( secantLen[k] + secantLen[k - 1] ); + slopes.push_back( s ); + } + + const float minSlope = 0.01f; + slopes.push_back( std::max(minSlope, 0.5f * ( 3.f * secantSlope[numCtrlPnts - 2] - slopes[numCtrlPnts - 2] )) ); + slopes[0] = std::max(minSlope, 0.5f * ( 3.f * secantSlope[0] - slopes[1] )); } - - const float minSlope = 0.01f; - slopes.push_back( std::max(minSlope, 0.5f * ( 3.f * secantSlope[numCtrlPnts - 2] - slopes[numCtrlPnts - 2] )) ); - slopes[0] = std::max(minSlope, 0.5f * ( 3.f * secantSlope[0] - slopes[1] )); - } // Adjust slopes that are not shape-preserving. for (unsigned i = 0; i < numCtrlPnts - 1; ++i) { - float k = 0.2f; - if (fabsf(slopes[i]) > fabsf(slopes[i+1])) - k = 1.f - k; - const float m_near_min = slopes[i] + k * (slopes[i + 1] - slopes[i]); - float scale = 1.f; - if (m_near_min != 0.f) - scale = 0.75f * 2.f * secantSlope[i] / m_near_min; - if (scale < 1.f) - { - slopes[i] = scale * slopes[i]; - slopes[i + 1] = scale * slopes[i + 1]; - } + float k = 0.2f; + if (fabsf(slopes[i]) > fabsf(slopes[i+1])) + k = 1.f - k; + const float m_near_min = slopes[i] + k * (slopes[i + 1] - slopes[i]); + float scale = 1.f; + if (m_near_min != 0.f) + scale = 0.75f * 2.f * secantSlope[i] / m_near_min; + if (scale < 1.f) + { + slopes[i] = scale * slopes[i]; + slopes[i + 1] = scale * slopes[i + 1]; + } } // Copy end slopes from the opposite side. if (isPeriodic) { - slopes[0] = slopes[numCtrlPnts - 2]; - slopes[numCtrlPnts - 1] = slopes[1]; + slopes[0] = slopes[numCtrlPnts - 2]; + slopes[numCtrlPnts - 1] = slopes[1]; } } -void EstimateRGBSlopes(const std::vector & ctrlPnts, std::vector & slopes) +void EstimateRGBSlopes(const std::vector & ctrlPnts, + std::vector & slopes) { std::vector secantSlope; std::vector secantLen; @@ -398,12 +378,12 @@ void EstimateRGBSlopes(const std::vector & ctrlPnts, std::v slopes[0] = std::max(0.01f, 0.5f * (3.f * secantSlope[0] - slopes[1])); } -void FitSpline(const std::vector & ctrlPnts, - const std::vector & slopes, - std::vector & knots, - std::vector & coefsA, - std::vector & coefsB, - std::vector & coefsC) +void FitRGBSpline(const std::vector & ctrlPnts, + const std::vector & slopes, + std::vector & knots, + std::vector & coefsA, + std::vector & coefsB, + std::vector & coefsC) { const size_t numCtrlPnts = ctrlPnts.size(); @@ -458,9 +438,9 @@ void FitSpline(const std::vector & ctrlPnts, } } -bool AdjustSlopes(const std::vector & ctrlPnts, - std::vector & slopes, - std::vector & knots) +bool AdjustRGBSlopes(const std::vector & ctrlPnts, + std::vector & slopes, + std::vector & knots) { bool adjustment_done = false; size_t i = 0, j = 0; @@ -571,6 +551,16 @@ GradingBSplineCurveRcPtr GradingBSplineCurveImpl::createEditableCopy() const return res; } +BSplineCurveType GradingBSplineCurveImpl::getCurveType() const +{ + return m_curveType; +} + +void GradingBSplineCurveImpl::setCurveType(BSplineCurveType curveType) +{ + m_curveType = curveType; +} + size_t GradingBSplineCurveImpl::getNumControlPoints() const noexcept { return m_controlPoints.size(); @@ -661,17 +651,23 @@ void GradingBSplineCurveImpl::validate() const bool GradingBSplineCurveImpl::isIdentity() const { bool isIdentity = true; - if(m_curveType == DIAGONAL_B_SPLINE || m_curveType == B_SPLINE || m_curveType == HUE_HUE_B_SPLINE) + if( m_curveType == DIAGONAL_B_SPLINE || m_curveType == B_SPLINE || m_curveType == HUE_HUE_B_SPLINE ) { - isIdentity = std::all_of(m_controlPoints.begin(), m_controlPoints.end(), IsDiagonalControlPoint); + isIdentity = std::all_of(m_controlPoints.begin(), + m_controlPoints.end(), + [](const GradingControlPoint& cp) { return cp.m_x == cp.m_y; }); } - else if( m_curveType == HORIZONTAL0_B_SPLINE || m_curveType == PERIODIC_HORIZONTAL0_B_SPLINE) + else if( m_curveType == PERIODIC_0_B_SPLINE ) { - isIdentity = std::all_of(m_controlPoints.begin(), m_controlPoints.end(), IsHorizontal0ControlPoint); + isIdentity = std::all_of(m_controlPoints.begin(), + m_controlPoints.end(), + [](const GradingControlPoint& cp) { return cp.m_y == 0.f; }); } - else if( m_curveType == HORIZONTAL1_B_SPLINE || m_curveType == PERIODIC_HORIZONTAL1_B_SPLINE) + else if( m_curveType == HORIZONTAL1_B_SPLINE || m_curveType == PERIODIC_1_B_SPLINE ) { - isIdentity = std::all_of(m_controlPoints.begin(), m_controlPoints.end(), IsHorizontal1ControlPoint); + isIdentity = std::all_of(m_controlPoints.begin(), + m_controlPoints.end(), + [](const GradingControlPoint& cp) { return cp.m_y == 1.f; }); } else { throw Exception("Unknown curve type: Could not determine if curve is identity."); @@ -694,21 +690,6 @@ bool IsGradingCurveIdentity(const ConstGradingBSplineCurveRcPtr & curve) return false; } -//------------------------------------------------------------------------------------------------ -// -BSplineCurveType GradingBSplineCurveImpl::getCurveType() const -{ - return m_curveType; -} - -//------------------------------------------------------------------------------------------------ -// -void GradingBSplineCurveImpl::setCurveType(BSplineCurveType curveType) -{ - m_curveType = curveType; -} - - //------------------------------------------------------------------------------------------------ // void GradingBSplineCurveImpl::computeKnotsAndCoefsForRGBCurve(KnotsCoefs & knotsCoefs, int curveIdx) const @@ -716,6 +697,12 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForRGBCurve(KnotsCoefs & knots // Skip invalid data and identity. if (m_controlPoints.size() < 2 || isIdentity()) { + // Identity curve: offset is -1 and count is 0. + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = -1; + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = 0; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = -1; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = 0; + return; } else { @@ -736,15 +723,15 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForRGBCurve(KnotsCoefs & knots EstimateRGBSlopes(m_controlPoints, slopes); } - FitSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); + FitRGBSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); - bool adjustment_done = AdjustSlopes(m_controlPoints, slopes, knots); + bool adjustment_done = AdjustRGBSlopes(m_controlPoints, slopes, knots); if (adjustment_done) { knots.clear(); coefsA.clear(); coefsB.clear(); coefsC.clear(); - FitSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); + FitRGBSpline(m_controlPoints, slopes, knots, coefsA, coefsB, coefsC); } const int numKnots = static_cast(knotsCoefs.m_numKnots); @@ -791,16 +778,16 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots bool isPeriodic = false; bool isHorizontal = true; - if (m_curveType == BSplineCurveType::PERIODIC_HORIZONTAL1_B_SPLINE || - m_curveType == BSplineCurveType::PERIODIC_HORIZONTAL0_B_SPLINE || + if (m_curveType == BSplineCurveType::PERIODIC_1_B_SPLINE || + m_curveType == BSplineCurveType::PERIODIC_0_B_SPLINE || m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE) { - isPeriodic = true; + isPeriodic = true; } if (m_curveType == BSplineCurveType::DIAGONAL_B_SPLINE || m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE) { - isHorizontal = false; + isHorizontal = false; } std::vector resultCtrlPnts; @@ -1000,6 +987,7 @@ GradingBSplineCurveImpl::KnotsCoefs::KnotsCoefs(size_t numCurves) m_knotsArray.resize(DynamicPropertyGradingRGBCurveImpl::GetMaxKnots()); } +// TODO: Update to return hue curve identity value. float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x) const { const int coefsSets = m_coefsOffsetsArray[2 * c + 1] / 3; @@ -1048,6 +1036,7 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x) const } } +// TODO: Update to return hue curve identity value. float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const { const int coefsSets = m_coefsOffsetsArray[2 * c + 1] / 3; diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h index acd2acb2bc..4b0e36927f 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h @@ -125,10 +125,6 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve void computeKnotsAndCoefsForHueCurve(KnotsCoefs & knotsCoefs, int curveIdx) const; void validateIndex(size_t index) const; - void prepData(const std::vector& inCtrlPnts, - std::vector& outCtrlPnt, - bool isPeriodic, - bool isHorizontal) const; std::vector m_controlPoints; std::vector m_slopesArray; // Optional slope values for the control points. diff --git a/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp b/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp index cf0d79c9ed..57ebefa207 100644 --- a/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp +++ b/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp @@ -13,7 +13,7 @@ namespace OCIO_NAMESPACE GradingHueCurveTransformRcPtr GradingHueCurveTransform::Create(GradingStyle style) { return GradingHueCurveTransformRcPtr(new GradingHueCurveTransformImpl(style), - &GradingHueCurveTransformImpl::deleter); + &GradingHueCurveTransformImpl::deleter); } GradingHueCurveTransformImpl::GradingHueCurveTransformImpl(GradingStyle style) : From d5d971326dcc639e333c5d244c3e079e770924c3 Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Mon, 17 Jun 2024 18:36:49 -0400 Subject: [PATCH 06/30] GradingHueCurveOpData_tests (cherry picked from commit 2a96167e458260eb1d68d2fa95203d5b066838cd) Signed-off-by: Doug Walker --- .../GradingHueCurveOpData_tests.cpp | 232 +++++++++--------- .../gradingrgbcurve/GradingRGBCurve_tests.cpp | 1 + 2 files changed, 121 insertions(+), 112 deletions(-) diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp index e04298f6d1..c8133994cf 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp @@ -10,129 +10,137 @@ namespace OCIO = OCIO_NAMESPACE; OCIO_ADD_TEST(GradingHueCurveOpData, accessors) { - // Create GradingRGBCurveOpData and check values. Changes them and check. - //OCIO::GradingHueCurveOpData gc; - - //static constexpr char expected[]{ "log forward " - // "]>, " - // "green=]>, " - // "blue=]>, " - // "master=]>>" }; - //OCIO_CHECK_EQUAL(gc.getCacheID(), expected); - - //OCIO_CHECK_EQUAL(gc.getStyle(), OCIO::GRADING_LOG); - //auto curves = gc.getValue(); - //OCIO_REQUIRE_ASSERT(curves); - //OCIO_CHECK_ASSERT(curves->isIdentity()); - //OCIO_CHECK_ASSERT(gc.isIdentity()); - //OCIO_CHECK_ASSERT(gc.isNoOp()); - //OCIO_CHECK_ASSERT(!gc.hasChannelCrosstalk()); - //OCIO_CHECK_ASSERT(!gc.getBypassLinToLog()); + // Create GradingHueCurveOpData and check values. Changes them and check. + OCIO::GradingHueCurveOpData gc( OCIO::GRADING_LOG ); + + static constexpr char expected[]{ "log forward " + "]>, " + "hue_sat=]>, " + "hue_lum=]>, " + "lum_sat=]>, " + "sat_sat=]>, " + "lum_lum=]>, " + "sat_lum=]>, " + "hue_fx=]>>" }; + OCIO_CHECK_EQUAL(gc.getCacheID(), expected); + + OCIO_CHECK_EQUAL(gc.getStyle(), OCIO::GRADING_LOG); + auto curves = gc.getValue(); + OCIO_REQUIRE_ASSERT(curves); + OCIO_CHECK_ASSERT(curves->isIdentity()); + OCIO_CHECK_ASSERT(gc.isIdentity()); + OCIO_CHECK_ASSERT(gc.isNoOp()); + OCIO_CHECK_ASSERT(!gc.hasChannelCrosstalk()); + OCIO_CHECK_ASSERT(!gc.getBypassLinToLog()); - //gc.setStyle(OCIO::GRADING_LIN); - //OCIO_CHECK_EQUAL(gc.getStyle(), OCIO::GRADING_LIN); - //gc.setBypassLinToLog(true); - //OCIO_CHECK_ASSERT(gc.getBypassLinToLog()); + gc.setStyle(OCIO::GRADING_LIN); + OCIO_CHECK_EQUAL(gc.getStyle(), OCIO::GRADING_LIN); + gc.setBypassLinToLog(true); + OCIO_CHECK_ASSERT(gc.getBypassLinToLog()); // Get dynamic property as a generic dynamic property and as a type one and verify they are // the same and can be made dynamic. - //OCIO_CHECK_ASSERT(!gc.isDynamic()); - //auto dp = gc.getDynamicProperty(); - //OCIO_REQUIRE_ASSERT(dp); - //OCIO_CHECK_EQUAL(dp->getType(), OCIO::DYNAMIC_PROPERTY_GRADING_RGBCURVE); - //auto dpImpl = gc.getDynamicPropertyInternal(); - //OCIO_REQUIRE_ASSERT(dpImpl); - //OCIO_CHECK_ASSERT(dp == dpImpl); - //OCIO_CHECK_ASSERT(!dpImpl->isDynamic()); - //dpImpl->makeDynamic(); - //OCIO_CHECK_ASSERT(gc.isDynamic()); -// - //OCIO_CHECK_EQUAL(gc.getDirection(), OCIO::TRANSFORM_DIR_FORWARD); - //gc.setDirection(OCIO::TRANSFORM_DIR_INVERSE); - //OCIO_CHECK_EQUAL(gc.getDirection(), OCIO::TRANSFORM_DIR_INVERSE); + OCIO_CHECK_ASSERT(!gc.isDynamic()); + auto dp = gc.getDynamicProperty(); + OCIO_REQUIRE_ASSERT(dp); + OCIO_CHECK_EQUAL(dp->getType(), OCIO::DYNAMIC_PROPERTY_GRADING_HUECURVE); + auto dpImpl = gc.getDynamicPropertyInternal(); + OCIO_REQUIRE_ASSERT(dpImpl); + OCIO_CHECK_ASSERT(dp == dpImpl); + OCIO_CHECK_ASSERT(!dpImpl->isDynamic()); + dpImpl->makeDynamic(); + OCIO_CHECK_ASSERT(gc.isDynamic()); + + OCIO_CHECK_EQUAL(gc.getDirection(), OCIO::TRANSFORM_DIR_FORWARD); + gc.setDirection(OCIO::TRANSFORM_DIR_INVERSE); + OCIO_CHECK_EQUAL(gc.getDirection(), OCIO::TRANSFORM_DIR_INVERSE); // Test operator==. - //OCIO::GradingHueCurveOpData gc1{}; - //OCIO::GradingHueCurveOpData gc2{}; -// - //OCIO_CHECK_ASSERT(gc1 == gc2); - //gc1.setDirection(OCIO::TRANSFORM_DIR_INVERSE); - //OCIO_CHECK_ASSERT(!(gc1 == gc2)); - //gc2.setDirection(OCIO::TRANSFORM_DIR_INVERSE); - //OCIO_CHECK_ASSERT(gc1 == gc2); - - //gc1.setStyle(OCIO::GRADING_LOG); - //OCIO_CHECK_ASSERT(!(gc1 == gc2)); - //gc2.setStyle(OCIO::GRADING_LOG); - //OCIO_CHECK_ASSERT(gc1 == gc2); -// - //auto v1 = gc1.getValue()->createEditableCopy(); - //auto red = v1->getCurve(OCIO::RGB_RED); - //red->setNumControlPoints(4); - //red->getControlPoint(3).m_x = red->getControlPoint(2).m_x + 1.f; - //red->getControlPoint(3).m_y = red->getControlPoint(2).m_y + 0.5f; - //gc1.setValue(v1); - //OCIO_CHECK_ASSERT(!(gc1 == gc2)); - //auto v2 = gc2.getValue()->createEditableCopy(); - //auto red2 = v2->getCurve(OCIO::RGB_RED); - //red2->setNumControlPoints(4); - //red2->getControlPoint(3).m_x = red2->getControlPoint(2).m_x + 1.f; - //red2->getControlPoint(3).m_y = red2->getControlPoint(2).m_y + 0.5f; - //gc2.setValue(v2); - //OCIO_CHECK_ASSERT(gc1 == gc2); -// - //gc1.setSlope(OCIO::RGB_BLUE, 2, 0.9f); - //OCIO_CHECK_EQUAL(gc1.getSlope(OCIO::RGB_BLUE, 2), 0.9f); - //OCIO_CHECK_ASSERT(gc1.slopesAreDefault(OCIO::RGB_GREEN)); - //OCIO_CHECK_ASSERT(!gc1.slopesAreDefault(OCIO::RGB_BLUE)); -// - //OCIO_CHECK_EQUAL(gc1.isIdentity(), false); - //OCIO_CHECK_ASSERT(!gc1.hasChannelCrosstalk()); -// - //// Check isInverse. -// - //// We have equal ops, inverse one. - //gc1.setDirection(OCIO::TRANSFORM_DIR_FORWARD); - //// Need a shared pointer for the parameter. - //OCIO::ConstGradingRGBCurveOpDataRcPtr gcptr2 = gc1.clone(); - //gc1.setDirection(OCIO::TRANSFORM_DIR_INVERSE); - //OCIO_CHECK_ASSERT(gc1.isInverse(gcptr2)); - //// Change value of one: no longer inverse. - //red->getControlPoint(3).m_y += 0.1f; - //gc1.setValue(v1); - //OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); - //// Restore value. - //red->getControlPoint(3).m_y -= 0.1f; - //gc1.setValue(v1); - //OCIO_CHECK_ASSERT(gc1.isInverse(gcptr2)); - //// Change direction: no longer inverse. - //gc1.setDirection(OCIO::TRANSFORM_DIR_FORWARD); - //OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); + OCIO::GradingHueCurveOpData gc1{ OCIO::GRADING_LIN }; + OCIO::GradingHueCurveOpData gc2{ OCIO::GRADING_LIN }; + + OCIO_CHECK_ASSERT(gc1 == gc2); + gc1.setDirection(OCIO::TRANSFORM_DIR_INVERSE); + OCIO_CHECK_ASSERT(!(gc1 == gc2)); + gc2.setDirection(OCIO::TRANSFORM_DIR_INVERSE); + OCIO_CHECK_ASSERT(gc1 == gc2); + + gc1.setStyle(OCIO::GRADING_LOG); + OCIO_CHECK_ASSERT(!(gc1 == gc2)); + gc2.setStyle(OCIO::GRADING_LOG); + OCIO_CHECK_ASSERT(gc1 == gc2); + + auto v1 = gc1.getValue()->createEditableCopy(); + auto hueHue = v1->getCurve(OCIO::HUE_HUE); + hueHue->setNumControlPoints(4); + hueHue->getControlPoint(3).m_x = hueHue->getControlPoint(2).m_x + 1.f; + hueHue->getControlPoint(3).m_y = hueHue->getControlPoint(2).m_y + 0.5f; + gc1.setValue(v1); + OCIO_CHECK_ASSERT(!(gc1 == gc2)); + auto v2 = gc2.getValue()->createEditableCopy(); + auto hueHue2 = v2->getCurve(OCIO::HUE_HUE); + hueHue2->setNumControlPoints(4); + hueHue2->getControlPoint(3).m_x = hueHue2->getControlPoint(2).m_x + 1.f; + hueHue2->getControlPoint(3).m_y = hueHue2->getControlPoint(2).m_y + 0.5f; + gc2.setValue(v2); + OCIO_CHECK_ASSERT(gc1 == gc2); + + gc1.setSlope(OCIO::HUE_SAT, 2, 0.9f); + OCIO_CHECK_EQUAL(gc1.getSlope(OCIO::HUE_SAT, 2), 0.9f); + OCIO_CHECK_ASSERT(gc1.slopesAreDefault(OCIO::HUE_LUM)); + OCIO_CHECK_ASSERT(!gc1.slopesAreDefault(OCIO::HUE_SAT)); + + OCIO_CHECK_EQUAL(gc1.isIdentity(), false); + OCIO_CHECK_ASSERT(!gc1.hasChannelCrosstalk()); + + // Check isInverse. + + // We have equal ops, inverse one. + gc1.setDirection(OCIO::TRANSFORM_DIR_FORWARD); + // Need a shared pointer for the parameter. + OCIO::ConstGradingHueCurveOpDataRcPtr gcptr2 = gc1.clone(); + gc1.setDirection(OCIO::TRANSFORM_DIR_INVERSE); + OCIO_CHECK_ASSERT(gc1.isInverse(gcptr2)); + // Change value of one: no longer inverse. + hueHue->getControlPoint(3).m_y += 0.1f; + gc1.setValue(v1); + OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); + // Restore value. + hueHue->getControlPoint(3).m_y -= 0.1f; + gc1.setValue(v1); + OCIO_CHECK_ASSERT(gc1.isInverse(gcptr2)); + // Change direction: no longer inverse. + gc1.setDirection(OCIO::TRANSFORM_DIR_FORWARD); + OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); } OCIO_ADD_TEST(GradingHueCurveOpData, validate) { // Default is valid. - //OCIO::GradingHueCurveOpData gc; - //OCIO_CHECK_NO_THROW(gc.validate()); + OCIO::GradingHueCurveOpData gc{ OCIO::GRADING_LOG }; + OCIO_CHECK_NO_THROW(gc.validate()); // Curves with a single control point are not valid. - //auto curve = OCIO::GradingBSplineCurve::Create(1); - //auto curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); - //OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, - // "There must be at least 2 control points."); -// - //// Curve x coordinates have to increase. - //curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.7f,0.3f }, - // { 0.5f,0.7f },{ 1.f,1.f } }); - //curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); - //OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, - // "has a x coordinate '0.5' that is less from previous control " - // "point x cooordinate '0.7'."); -// - //// Fix the curve x coordinate. - //curve->getControlPoint(1).m_x = 0.3f; - //curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); - //OCIO_CHECK_NO_THROW(gc.setValue(curves)); - //OCIO_CHECK_NO_THROW(gc.validate()); + auto curve = OCIO::GradingBSplineCurve::Create(1); + OCIO::GradingHueCurves curvesStruct{curve, curve, curve, curve, curve, curve, curve, curve}; + auto curves = OCIO::GradingHueCurve::Create(curvesStruct); + OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, + "There must be at least 2 control points."); + + // Curve x coordinates have to increase. + curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.7f,0.3f }, + { 0.5f,0.7f },{ 1.f,1.f } }); + + curvesStruct = OCIO::GradingHueCurves{curve, curve, curve, curve, curve, curve, curve, curve}; + curves = OCIO::GradingHueCurve::Create(curvesStruct); + OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, + "has a x coordinate '0.5' that is less from previous control " + "point x cooordinate '0.7'."); + + // Fix the curve x coordinate. + curve->getControlPoint(1).m_x = 0.3f; + curvesStruct = OCIO::GradingHueCurves{curve, curve, curve, curve, curve, curve, curve, curve}; + curves = OCIO::GradingHueCurve::Create(curvesStruct); + OCIO_CHECK_NO_THROW(gc.setValue(curves)); + OCIO_CHECK_NO_THROW(gc.validate()); } diff --git a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp index a4e37c8751..3755e4a6db 100644 --- a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp +++ b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp @@ -107,6 +107,7 @@ OCIO_ADD_TEST(GradingRGBCurve, curves) OCIO_CHECK_EQUAL(curves->getCurve(OCIO::RGB_GREEN)->getNumControlPoints(), 4); } +// TODO:: Move this test in the HueCurve version since it is easier to reach the max control points. OCIO_ADD_TEST(GradingRGBCurve, max_ctrl_pnts) { auto curveR = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, From d50f76f3f99f39a36bdd8994a4bf369dff42f1c2 Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Tue, 18 Jun 2024 08:50:32 -0400 Subject: [PATCH 07/30] Review Fix, ChannelCrosstalk (cherry picked from commit 0bac40198610e8dca2f0207e633eb0252578684b) Signed-off-by: Doug Walker --- src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h | 2 +- .../cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h index d8df8f65a7..396a37b07f 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h @@ -39,7 +39,7 @@ class GradingHueCurveOpData : public OpData bool isNoOp() const override; bool isIdentity() const override; - bool hasChannelCrosstalk() const override { return false; } + bool hasChannelCrosstalk() const override { return true; } bool isInverse(ConstGradingHueCurveOpDataRcPtr & r) const; GradingHueCurveOpDataRcPtr inverse() const; diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp index c8133994cf..810f05b716 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp @@ -8,6 +8,7 @@ namespace OCIO = OCIO_NAMESPACE; +// Test coming from GradingRGBCurveOpData_tests.cpp OCIO_ADD_TEST(GradingHueCurveOpData, accessors) { // Create GradingHueCurveOpData and check values. Changes them and check. @@ -30,7 +31,7 @@ OCIO_ADD_TEST(GradingHueCurveOpData, accessors) OCIO_CHECK_ASSERT(curves->isIdentity()); OCIO_CHECK_ASSERT(gc.isIdentity()); OCIO_CHECK_ASSERT(gc.isNoOp()); - OCIO_CHECK_ASSERT(!gc.hasChannelCrosstalk()); + OCIO_CHECK_ASSERT(gc.hasChannelCrosstalk()); OCIO_CHECK_ASSERT(!gc.getBypassLinToLog()); gc.setStyle(OCIO::GRADING_LIN); @@ -91,7 +92,7 @@ OCIO_ADD_TEST(GradingHueCurveOpData, accessors) OCIO_CHECK_ASSERT(!gc1.slopesAreDefault(OCIO::HUE_SAT)); OCIO_CHECK_EQUAL(gc1.isIdentity(), false); - OCIO_CHECK_ASSERT(!gc1.hasChannelCrosstalk()); + OCIO_CHECK_ASSERT(gc1.hasChannelCrosstalk()); // Check isInverse. From 873982e2db19f30a5c03f2d86510473f444556f4 Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Tue, 18 Jun 2024 10:03:23 -0400 Subject: [PATCH 08/30] Review Fix, missing fixed function enums in switch cases (cherry picked from commit 051bd7d574cb1f0d88d2a79a543dde8d6dffc535) Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTypes.h | 5 +- src/OpenColorIO/ParseUtils.cpp | 3 + .../ops/fixedfunction/FixedFunctionOpCPU.cpp | 39 +++++++++++ .../ops/fixedfunction/FixedFunctionOpData.cpp | 69 +++++++++++++++++-- 4 files changed, 111 insertions(+), 5 deletions(-) diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index b3a6961c21..832a5acafd 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -509,7 +509,10 @@ enum FixedFunctionStyle FIXED_FUNCTION_ACES_OUTPUT_TRANSFORM_20, ///< ACES 2.0 Display Rendering -- EXPERIMENTAL FIXED_FUNCTION_ACES_RGB_TO_JMH_20, ///< ACES 2.0 RGB to JMh -- EXPERIMENTAL FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20, ///< ACES 2.0 Tonescale and chroma compression -- EXPERIMENTAL - FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20 ///< ACES 2.0 Gamut compression -- EXPERIMENTAL + FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20, ///< ACES 2.0 Gamut compression -- EXPERIMENTAL + FIXED_FUNCTION_RGB_TO_HSY_LIN, ///< RGB to HSY (Hue, Saturation, Lightness) using linear + FIXED_FUNCTION_RGB_TO_HSY_LOG, ///< RGB to HSY (Hue, Saturation, Lightness) using log + FIXED_FUNCTION_RGB_TO_HSY_VID, ///< RGB to HSY (Hue, Saturation, Lightness) using video }; /// Enumeration of the :cpp:class:`ExposureContrastTransform` transform algorithms. diff --git a/src/OpenColorIO/ParseUtils.cpp b/src/OpenColorIO/ParseUtils.cpp index c8b77c5f3a..755644c328 100644 --- a/src/OpenColorIO/ParseUtils.cpp +++ b/src/OpenColorIO/ParseUtils.cpp @@ -371,6 +371,9 @@ const char * FixedFunctionStyleToString(FixedFunctionStyle style) case FIXED_FUNCTION_LIN_TO_PQ: return "Lin_TO_PQ"; case FIXED_FUNCTION_LIN_TO_GAMMA_LOG: return "Lin_TO_GammaLog"; case FIXED_FUNCTION_LIN_TO_DOUBLE_LOG: return "Lin_TO_DoubleLog"; + case FIXED_FUNCTION_RGB_TO_HSY_LIN: return "RGB_TO_HSY_LIN"; + case FIXED_FUNCTION_RGB_TO_HSY_LOG: return "RGB_TO_HSY_LOG"; + case FIXED_FUNCTION_RGB_TO_HSY_VID: return "RGB_TO_HSY_VID"; case FIXED_FUNCTION_ACES_GAMUTMAP_02: case FIXED_FUNCTION_ACES_GAMUTMAP_07: throw Exception("Unimplemented fixed function types: " diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp index feb387db5f..b6aab3160d 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp @@ -2322,6 +2322,45 @@ ConstOpCPURcPtr GetFixedFunctionCPURenderer(ConstFixedFunctionOpDataRcPtr & func /// TODO: SIMD implementation return std::make_shared(func); } + + case FixedFunctionOpData::RGB_TO_HSY_LOG: + { + // TODO: Handle CPU rendering for this FixedFunction + return nullptr; + //return std::make_shared(func); + } + case FixedFunctionOpData::HSY_LOG_TO_RGB: + { + // TODO: Handle CPU rendering for this FixedFunction + return nullptr; + //return std::make_shared(func); + } + + case FixedFunctionOpData::RGB_TO_HSY_LIN: + { + // TODO: Handle CPU rendering for this FixedFunction + return nullptr; + //return std::make_shared(func); + } + case FixedFunctionOpData::HSY_LIN_TO_RGB: + { + // TODO: Handle CPU rendering for this FixedFunction + return nullptr; + //return std::make_shared(func); + } + + case FixedFunctionOpData::RGB_TO_HSY_VID: + { + // TODO: Handle CPU rendering for this FixedFunction + return nullptr; + //return std::make_shared(func); + } + case FixedFunctionOpData::HSY_VID_TO_RGB: + { + // TODO: Handle CPU rendering for this FixedFunction + return nullptr; + //return std::make_shared(func); + } } throw Exception("Unsupported FixedFunction style"); diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp index bd20345095..8e5d5833d0 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp @@ -434,6 +434,21 @@ FixedFunctionOpData::Style FixedFunctionOpData::ConvertStyle(FixedFunctionStyle { return FixedFunctionOpData::RGB_TO_HSV; } + case FIXED_FUNCTION_RGB_TO_HSY_LIN: + { + return isForward ? FixedFunctionOpData::RGB_TO_HSY_LIN : + FixedFunctionOpData::HSY_LIN_TO_RGB; + } + case FIXED_FUNCTION_RGB_TO_HSY_LOG: + { + return isForward ? FixedFunctionOpData::RGB_TO_HSY_LOG : + FixedFunctionOpData::HSY_LOG_TO_RGB; + } + case FIXED_FUNCTION_RGB_TO_HSY_VID: + { + return isForward ? FixedFunctionOpData::RGB_TO_HSY_VID : + FixedFunctionOpData::HSY_VID_TO_RGB; + } case FIXED_FUNCTION_XYZ_TO_xyY: { return FixedFunctionOpData::XYZ_TO_xyY; @@ -553,10 +568,17 @@ FixedFunctionStyle FixedFunctionOpData::ConvertStyle(FixedFunctionOpData::Style case FixedFunctionOpData::DOUBLE_LOG_TO_LIN: return FIXED_FUNCTION_LIN_TO_DOUBLE_LOG; - // TODO: Implement the following conversions. - //case FixedFunctionOpData::RGB_TO_HSY_LIN: - //case FixedFunctionOpData::HSY_LIN_TO_RGB: - // return FIXED_FUNCTION_RGB_TO_HSY_LIN; + case FixedFunctionOpData::RGB_TO_HSY_LIN: + case FixedFunctionOpData::HSY_LIN_TO_RGB: + return FIXED_FUNCTION_RGB_TO_HSY_LIN; + + case FixedFunctionOpData::RGB_TO_HSY_LOG: + case FixedFunctionOpData::HSY_LOG_TO_RGB: + return FIXED_FUNCTION_RGB_TO_HSY_LOG; + + case FixedFunctionOpData::RGB_TO_HSY_VID: + case FixedFunctionOpData::HSY_VID_TO_RGB: + return FIXED_FUNCTION_RGB_TO_HSY_VID; } @@ -960,6 +982,39 @@ void FixedFunctionOpData::invert() noexcept break; } + case RGB_TO_HSY_LOG: + { + setStyle(HSY_LOG_TO_RGB); + break; + } + case HSY_LOG_TO_RGB: + { + setStyle(RGB_TO_HSY_LOG); + break; + } + + case RGB_TO_HSY_LIN: + { + setStyle(HSY_LIN_TO_RGB); + break; + } + case HSY_LIN_TO_RGB: + { + setStyle(RGB_TO_HSY_LIN); + break; + } + + case RGB_TO_HSY_VID: + { + setStyle(HSY_VID_TO_RGB); + break; + } + case HSY_VID_TO_RGB: + { + setStyle(RGB_TO_HSY_VID); + break; + } + case XYZ_TO_xyY: { setStyle(xyY_TO_XYZ); @@ -1056,6 +1111,9 @@ TransformDirection FixedFunctionOpData::getDirection() const noexcept case FixedFunctionOpData::ACES_GAMUT_COMPRESS_20_FWD: case FixedFunctionOpData::REC2100_SURROUND_FWD: case FixedFunctionOpData::RGB_TO_HSV: + case FixedFunctionOpData::RGB_TO_HSY_LOG: + case FixedFunctionOpData::RGB_TO_HSY_LIN: + case FixedFunctionOpData::RGB_TO_HSY_VID: case FixedFunctionOpData::XYZ_TO_xyY: case FixedFunctionOpData::XYZ_TO_uvY: case FixedFunctionOpData::XYZ_TO_LUV: @@ -1076,6 +1134,9 @@ TransformDirection FixedFunctionOpData::getDirection() const noexcept case FixedFunctionOpData::ACES_GAMUT_COMPRESS_20_INV: case FixedFunctionOpData::REC2100_SURROUND_INV: case FixedFunctionOpData::HSV_TO_RGB: + case FixedFunctionOpData::HSY_LOG_TO_RGB: + case FixedFunctionOpData::HSY_LIN_TO_RGB: + case FixedFunctionOpData::HSY_VID_TO_RGB: case FixedFunctionOpData::xyY_TO_XYZ: case FixedFunctionOpData::uvY_TO_XYZ: case FixedFunctionOpData::LUV_TO_XYZ: From 8f54436c2abdb0a059f27f8ae97465072b01a78d Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Tue, 18 Jun 2024 10:55:15 -0400 Subject: [PATCH 09/30] Review Fix, Missing comment on unused test parameters (cherry picked from commit 07aca3cf31fc0a8b36d117dd6cbe32588549f95d) Signed-off-by: Doug Walker --- tests/gpu/GradingHueCurveOp_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/gpu/GradingHueCurveOp_test.cpp b/tests/gpu/GradingHueCurveOp_test.cpp index fa8ecef16e..e4576ba26c 100644 --- a/tests/gpu/GradingHueCurveOp_test.cpp +++ b/tests/gpu/GradingHueCurveOp_test.cpp @@ -7,7 +7,7 @@ namespace OCIO = OCIO_NAMESPACE; -void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +void GradingHueCurveLog(OCIOGPUTest & /*test*/, OCIO::TransformDirection /*dir*/, bool /*dynamic*/) { // TODO: Put back the GradingHueCurve test once the CPU implementation is available.' // GPU test compares the result of the GPU processor to the CPU processor. @@ -58,7 +58,7 @@ OCIO_ADD_GPU_TEST(GradingHueCurve, style_log_rev_dynamic) GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, true); } -void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +void HueCurveLin(OCIOGPUTest & /*test*/, OCIO::TransformDirection /*dir*/, bool /*dynamic*/) { // TODO: Put back the GradingHueCurve test once the CPU implementation is available.' // GPU test compares the result of the GPU processor to the CPU processor. @@ -109,7 +109,7 @@ OCIO_ADD_GPU_TEST(GradingHueCurve, style_lin_rev_dynamic) HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, true); } -void HueSCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +void HueSCurve(OCIOGPUTest & /*test*/, OCIO::TransformDirection /*dir*/, bool /*dynamic*/) { // TODO: Implement this GradingHueCurve test once the CPU implementation is available.' // GPU test compares the result of the GPU processor to the CPU processor. From d01a753bac36e5858d23c87fe5215ea5b5198c64 Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Tue, 18 Jun 2024 14:12:08 -0400 Subject: [PATCH 10/30] CI Fix, removing multi-line comment (cherry picked from commit bf780199a1647286d56b8bfbf7933e8ff6762573) Signed-off-by: Doug Walker --- tests/cpu/transforms/GradingHueCurveTransform_tests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp index fdcf664461..e7889d1c76 100644 --- a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp +++ b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp @@ -250,10 +250,10 @@ OCIO_ADD_TEST(GradingHueCurveTransform, serialization) //curve->setValue(data); //static constexpr char CURVE_STR[] - // = "]>, "\ - // "green=]>, "\ - // "blue=]>, "\ + // = "]>, " + // "green=]>, " + // "blue=]>, " // "master=]>>>"; // //{ From 2abeb38fda04ac89f0eac97656c9826a6e305406 Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Tue, 18 Jun 2024 16:43:04 -0400 Subject: [PATCH 11/30] Fixes for Test 2: test_cpu (cherry picked from commit 4a5001ed26cd39af46ef9cdf6fe87a529db21e23) Signed-off-by: Doug Walker --- src/OpenColorIO/DynamicProperty.cpp | 4 +- tests/cpu/GpuShader_tests.cpp | 48 +++++++++---------- .../gradingrgbcurve/GradingRGBCurve_tests.cpp | 16 +++++-- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/OpenColorIO/DynamicProperty.cpp b/src/OpenColorIO/DynamicProperty.cpp index 5ea1f9021a..6f798ab42e 100644 --- a/src/OpenColorIO/DynamicProperty.cpp +++ b/src/OpenColorIO/DynamicProperty.cpp @@ -225,12 +225,12 @@ bool DynamicPropertyGradingRGBCurveImpl::getLocalBypass() const int DynamicPropertyGradingRGBCurveImpl::getNumKnots() const { - return static_cast(m_knotsCoefs.m_knotsArray.size()); + return static_cast(m_knotsCoefs.m_numKnots); } int DynamicPropertyGradingRGBCurveImpl::getNumCoefs() const { - return static_cast(m_knotsCoefs.m_coefsArray.size()); + return static_cast(m_knotsCoefs.m_numCoefs); } const int * DynamicPropertyGradingRGBCurveImpl::getKnotsOffsetsArray() const diff --git a/tests/cpu/GpuShader_tests.cpp b/tests/cpu/GpuShader_tests.cpp index 961fe269bf..6b974c5b08 100644 --- a/tests/cpu/GpuShader_tests.cpp +++ b/tests/cpu/GpuShader_tests.cpp @@ -1037,7 +1037,7 @@ constant constexpr static float ocio_grading_rgbcurve_knots_0[5] = {0., 0.333333 constant constexpr static int ocio_grading_rgbcurve_coefsOffsets_0[8] = {0, 12, -1, 0, -1, 0, -1, 0}; constant constexpr static float ocio_grading_rgbcurve_coefs_0[12] = {0.0982520878, 0.393008381, 0.347727984, 0.08693178, 0.934498608, 1., 1.13100278, 1.246912, 0., 0.322416425, 0.5, 0.698159397}; -float ocio_grading_rgbcurve_evalBSplineCurve_0(int curveIdx, float x) +float ocio_grading_rgbcurve_evalBSplineCurve_0(int curveIdx, float x, float identity_x) { int knotsOffs = ocio_grading_rgbcurve_knotsOffsets_0[curveIdx * 2]; int knotsCnt = ocio_grading_rgbcurve_knotsOffsets_0[curveIdx * 2 + 1]; @@ -1046,7 +1046,7 @@ float ocio_grading_rgbcurve_evalBSplineCurve_0(int curveIdx, float x) int coefsSets = coefsCnt / 3; if (coefsSets == 0) { - return x; + return identity_x; } float knStart = ocio_grading_rgbcurve_knots_0[knotsOffs]; float knEnd = ocio_grading_rgbcurve_knots_0[knotsOffs + knotsCnt - 1]; @@ -1092,12 +1092,12 @@ float4 OCIOMain(float4 inPixel) // Add GradingRGBCurve 'log' forward processing { - outColor.rgb.r = ocio_grading_rgbcurve_evalBSplineCurve_0(0, outColor.rgb.r); - outColor.rgb.g = ocio_grading_rgbcurve_evalBSplineCurve_0(1, outColor.rgb.g); - outColor.rgb.b = ocio_grading_rgbcurve_evalBSplineCurve_0(2, outColor.rgb.b); - outColor.rgb.r = ocio_grading_rgbcurve_evalBSplineCurve_0(3, outColor.rgb.r); - outColor.rgb.g = ocio_grading_rgbcurve_evalBSplineCurve_0(3, outColor.rgb.g); - outColor.rgb.b = ocio_grading_rgbcurve_evalBSplineCurve_0(3, outColor.rgb.b); + outColor.rgb.r = ocio_grading_rgbcurve_evalBSplineCurve_0(0, outColor.rgb.r, outColor.rgb.r); + outColor.rgb.g = ocio_grading_rgbcurve_evalBSplineCurve_0(1, outColor.rgb.g, outColor.rgb.g); + outColor.rgb.b = ocio_grading_rgbcurve_evalBSplineCurve_0(2, outColor.rgb.b, outColor.rgb.b); + outColor.rgb.r = ocio_grading_rgbcurve_evalBSplineCurve_0(3, outColor.rgb.r, outColor.rgb.r); + outColor.rgb.g = ocio_grading_rgbcurve_evalBSplineCurve_0(3, outColor.rgb.g, outColor.rgb.g); + outColor.rgb.b = ocio_grading_rgbcurve_evalBSplineCurve_0(3, outColor.rgb.b, outColor.rgb.b); } return outColor; @@ -1143,11 +1143,11 @@ struct ocioOCIOMain ocioOCIOMain( constant int ocio_grading_rgbcurve_knotsOffsets[8] , int ocio_grading_rgbcurve_knotsOffsets_count - , constant float ocio_grading_rgbcurve_knots[60] + , constant float ocio_grading_rgbcurve_knots[120] , int ocio_grading_rgbcurve_knots_count , constant int ocio_grading_rgbcurve_coefsOffsets[8] , int ocio_grading_rgbcurve_coefsOffsets_count - , constant float ocio_grading_rgbcurve_coefs[180] + , constant float ocio_grading_rgbcurve_coefs[360] , int ocio_grading_rgbcurve_coefs_count , bool ocio_grading_rgbcurve_localBypass ) @@ -1164,7 +1164,7 @@ ocioOCIOMain( { this->ocio_grading_rgbcurve_knots[i] = ocio_grading_rgbcurve_knots[i]; } - for(int i = ocio_grading_rgbcurve_knots_count; i < 60; ++i) + for(int i = ocio_grading_rgbcurve_knots_count; i < 120; ++i) { this->ocio_grading_rgbcurve_knots[i] = 0; } @@ -1180,7 +1180,7 @@ ocioOCIOMain( { this->ocio_grading_rgbcurve_coefs[i] = ocio_grading_rgbcurve_coefs[i]; } - for(int i = ocio_grading_rgbcurve_coefs_count; i < 180; ++i) + for(int i = ocio_grading_rgbcurve_coefs_count; i < 360; ++i) { this->ocio_grading_rgbcurve_coefs[i] = 0; } @@ -1191,15 +1191,15 @@ ocioOCIOMain( // Declaration of all variables int ocio_grading_rgbcurve_knotsOffsets[8]; -float ocio_grading_rgbcurve_knots[60]; +float ocio_grading_rgbcurve_knots[120]; int ocio_grading_rgbcurve_coefsOffsets[8]; -float ocio_grading_rgbcurve_coefs[180]; +float ocio_grading_rgbcurve_coefs[360]; bool ocio_grading_rgbcurve_localBypass; // Declaration of all helper methods -float ocio_grading_rgbcurve_evalBSplineCurve(int curveIdx, float x) +float ocio_grading_rgbcurve_evalBSplineCurve(int curveIdx, float x, float identity_x) { int knotsOffs = ocio_grading_rgbcurve_knotsOffsets[curveIdx * 2]; int knotsCnt = ocio_grading_rgbcurve_knotsOffsets[curveIdx * 2 + 1]; @@ -1208,7 +1208,7 @@ float ocio_grading_rgbcurve_evalBSplineCurve(int curveIdx, float x) int coefsSets = coefsCnt / 3; if (coefsSets == 0) { - return x; + return identity_x; } float knStart = ocio_grading_rgbcurve_knots[knotsOffs]; float knEnd = ocio_grading_rgbcurve_knots[knotsOffs + knotsCnt - 1]; @@ -1256,12 +1256,12 @@ float4 OCIOMain(float4 inPixel) { if (!ocio_grading_rgbcurve_localBypass) { - outColor.rgb.r = ocio_grading_rgbcurve_evalBSplineCurve(0, outColor.rgb.r); - outColor.rgb.g = ocio_grading_rgbcurve_evalBSplineCurve(1, outColor.rgb.g); - outColor.rgb.b = ocio_grading_rgbcurve_evalBSplineCurve(2, outColor.rgb.b); - outColor.rgb.r = ocio_grading_rgbcurve_evalBSplineCurve(3, outColor.rgb.r); - outColor.rgb.g = ocio_grading_rgbcurve_evalBSplineCurve(3, outColor.rgb.g); - outColor.rgb.b = ocio_grading_rgbcurve_evalBSplineCurve(3, outColor.rgb.b); + outColor.rgb.r = ocio_grading_rgbcurve_evalBSplineCurve(0, outColor.rgb.r, outColor.rgb.r); + outColor.rgb.g = ocio_grading_rgbcurve_evalBSplineCurve(1, outColor.rgb.g, outColor.rgb.g); + outColor.rgb.b = ocio_grading_rgbcurve_evalBSplineCurve(2, outColor.rgb.b, outColor.rgb.b); + outColor.rgb.r = ocio_grading_rgbcurve_evalBSplineCurve(3, outColor.rgb.r, outColor.rgb.r); + outColor.rgb.g = ocio_grading_rgbcurve_evalBSplineCurve(3, outColor.rgb.g, outColor.rgb.g); + outColor.rgb.b = ocio_grading_rgbcurve_evalBSplineCurve(3, outColor.rgb.b, outColor.rgb.b); } } @@ -1275,11 +1275,11 @@ float4 OCIOMain(float4 inPixel) float4 OCIOMain( constant int ocio_grading_rgbcurve_knotsOffsets[8] , int ocio_grading_rgbcurve_knotsOffsets_count - , constant float ocio_grading_rgbcurve_knots[60] + , constant float ocio_grading_rgbcurve_knots[120] , int ocio_grading_rgbcurve_knots_count , constant int ocio_grading_rgbcurve_coefsOffsets[8] , int ocio_grading_rgbcurve_coefsOffsets_count - , constant float ocio_grading_rgbcurve_coefs[180] + , constant float ocio_grading_rgbcurve_coefs[360] , int ocio_grading_rgbcurve_coefs_count , bool ocio_grading_rgbcurve_localBypass , float4 inPixel) diff --git a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp index 3755e4a6db..d23109b6bb 100644 --- a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp +++ b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp @@ -111,16 +111,24 @@ OCIO_ADD_TEST(GradingRGBCurve, curves) OCIO_ADD_TEST(GradingRGBCurve, max_ctrl_pnts) { auto curveR = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, - { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); + { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f }, { 16.f, 86.f }, { 17.f, 87.f }, + { 18.f, 88.f }, { 19.f, 89.f }, { 20.f, 90.f }, { 21.f, 91.f }, { 22.f, 92.f }, { 23.f, 93.f }, { 24.f, 94.f }, + { 25.f, 95.f }, { 26.f, 96.f }, { 27.f, 97.f }, { 28.f, 98.f }, { 29.f, 99.f }, { 30.f, 100.f }}); auto curveG = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, - { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); + { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f }, { 16.f, 86.f }, { 17.f, 87.f }, + { 18.f, 88.f }, { 19.f, 89.f }, { 20.f, 90.f }, { 21.f, 91.f }, { 22.f, 92.f }, { 23.f, 93.f }, { 24.f, 94.f }, + { 25.f, 95.f }, { 26.f, 96.f }, { 27.f, 97.f }, { 28.f, 98.f }, { 29.f, 99.f }, { 30.f, 100.f } }); auto curveB = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, - { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); + { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f }, { 16.f, 86.f }, { 17.f, 87.f }, + { 18.f, 88.f }, { 19.f, 89.f }, { 20.f, 90.f }, { 21.f, 91.f }, { 22.f, 92.f }, { 23.f, 93.f }, { 24.f, 94.f }, + { 25.f, 95.f }, { 26.f, 96.f }, { 27.f, 97.f }, { 28.f, 98.f }, { 29.f, 99.f }, { 30.f, 100.f } }); auto curveM = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, - { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); + { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f }, { 16.f, 86.f }, { 17.f, 87.f }, + { 18.f, 88.f }, { 19.f, 89.f }, { 20.f, 90.f }, { 21.f, 91.f }, { 22.f, 92.f }, { 23.f, 93.f }, { 24.f, 94.f }, + { 25.f, 95.f }, { 26.f, 96.f }, { 27.f, 97.f }, { 28.f, 98.f }, { 29.f, 99.f }, { 30.f, 100.f } }); auto rgbCurve = OCIO::GradingRGBCurve::Create(curveR, curveG, curveB, curveM); OCIO_REQUIRE_ASSERT(rgbCurve); From c8263fa31305e5e6ffedbde096e72d5bc4136b1d Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Tue, 25 Jun 2024 12:16:00 -0400 Subject: [PATCH 12/30] Fix Windows CI Towards clean (cherry picked from commit d298681a84b7236f4b5795a5cf06ab1fd243338b) Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTransforms.h | 22 ++++----- .../ops/gradinghuecurve/GradingHueCurve.cpp | 46 ++++++++++++++----- .../ops/gradinghuecurve/GradingHueCurve.h | 10 +++- .../gradinghuecurve/GradingHueCurveOpData.cpp | 23 ++++++++-- .../gradinghuecurve/GradingHueCurveOpData.h | 9 +++- .../gradingrgbcurve/GradingBSplineCurve.cpp | 38 ++++++++------- .../GradingHueCurveOpData_tests.cpp | 9 ++-- 7 files changed, 106 insertions(+), 51 deletions(-) diff --git a/include/OpenColorIO/OpenColorTransforms.h b/include/OpenColorIO/OpenColorTransforms.h index 75f943dce5..1f874e2173 100644 --- a/include/OpenColorIO/OpenColorTransforms.h +++ b/include/OpenColorIO/OpenColorTransforms.h @@ -575,18 +575,6 @@ extern OCIOEXPORT bool operator==(const GradingRGBCurve & lhs, const GradingRGBC extern OCIOEXPORT bool operator!=(const GradingRGBCurve & lhs, const GradingRGBCurve & rhs); extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingRGBCurve &); -struct OCIOEXPORT GradingHueCurves -{ - ConstGradingBSplineCurveRcPtr hueHue; - ConstGradingBSplineCurveRcPtr hueSat; - ConstGradingBSplineCurveRcPtr hueLum; - ConstGradingBSplineCurveRcPtr lumSat; - ConstGradingBSplineCurveRcPtr satSat; - ConstGradingBSplineCurveRcPtr lumLum; - ConstGradingBSplineCurveRcPtr satLum; - ConstGradingBSplineCurveRcPtr hueFx; -}; - /** * A set of HUE/SAT/LUM curves. It is used by GradingHueCurveTransform and can be used as * a dynamic property (see \ref DynamicPropertyGradingHueCurve). @@ -596,7 +584,15 @@ class OCIOEXPORT GradingHueCurve public: static GradingHueCurveRcPtr Create(GradingStyle style); static GradingHueCurveRcPtr Create(const ConstGradingHueCurveRcPtr & rhs); - static GradingHueCurveRcPtr Create(const GradingHueCurves & curves); + static GradingHueCurveRcPtr Create( + ConstGradingBSplineCurveRcPtr hueHue, + ConstGradingBSplineCurveRcPtr hueSat, + ConstGradingBSplineCurveRcPtr hueLum, + ConstGradingBSplineCurveRcPtr lumSat, + ConstGradingBSplineCurveRcPtr satSat, + ConstGradingBSplineCurveRcPtr lumLum, + ConstGradingBSplineCurveRcPtr satLum, + ConstGradingBSplineCurveRcPtr hueFx); virtual GradingHueCurveRcPtr createEditableCopy() const = 0; virtual void validate() const = 0; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp index f2352c6880..afa43e362f 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp @@ -90,16 +90,24 @@ GradingHueCurveImpl::GradingHueCurveImpl(GradingStyle style) } } -GradingHueCurveImpl::GradingHueCurveImpl(const GradingHueCurves & curves) +GradingHueCurveImpl::GradingHueCurveImpl( + ConstGradingBSplineCurveRcPtr hueHue, + ConstGradingBSplineCurveRcPtr hueSat, + ConstGradingBSplineCurveRcPtr hueLum, + ConstGradingBSplineCurveRcPtr lumSat, + ConstGradingBSplineCurveRcPtr satSat, + ConstGradingBSplineCurveRcPtr lumLum, + ConstGradingBSplineCurveRcPtr satLum, + ConstGradingBSplineCurveRcPtr hueFx) { - m_curves[HUE_HUE] = curves.hueHue->createEditableCopy(); - m_curves[HUE_SAT] = curves.hueSat->createEditableCopy(); - m_curves[HUE_LUM] = curves.hueLum->createEditableCopy(); - m_curves[LUM_SAT] = curves.lumSat->createEditableCopy(); - m_curves[SAT_SAT] = curves.satSat->createEditableCopy(); - m_curves[LUM_LUM] = curves.lumLum->createEditableCopy(); - m_curves[SAT_LUM] = curves.satLum->createEditableCopy(); - m_curves[HUE_FX] = curves.hueFx->createEditableCopy(); + m_curves[HUE_HUE] = hueHue->createEditableCopy(); + m_curves[HUE_SAT] = hueSat->createEditableCopy(); + m_curves[HUE_LUM] = hueLum->createEditableCopy(); + m_curves[LUM_SAT] = lumSat->createEditableCopy(); + m_curves[SAT_SAT] = satSat->createEditableCopy(); + m_curves[LUM_LUM] = lumLum->createEditableCopy(); + m_curves[SAT_LUM] = satLum->createEditableCopy(); + m_curves[HUE_FX] = hueFx->createEditableCopy(); } GradingHueCurveImpl::GradingHueCurveImpl(const ConstGradingHueCurveRcPtr & rhs) @@ -227,9 +235,25 @@ GradingHueCurveRcPtr GradingHueCurve::Create(const ConstGradingHueCurveRcPtr & r return res; } -GradingHueCurveRcPtr GradingHueCurve::Create(const GradingHueCurves & curves) +GradingHueCurveRcPtr GradingHueCurve::Create( + ConstGradingBSplineCurveRcPtr hueHue, + ConstGradingBSplineCurveRcPtr hueSat, + ConstGradingBSplineCurveRcPtr hueLum, + ConstGradingBSplineCurveRcPtr lumSat, + ConstGradingBSplineCurveRcPtr satSat, + ConstGradingBSplineCurveRcPtr lumLum, + ConstGradingBSplineCurveRcPtr satLum, + ConstGradingBSplineCurveRcPtr hueFx) { - auto newCurve = std::make_shared(curves); + auto newCurve = std::make_shared( + hueHue, + hueSat, + hueLum, + lumSat, + satSat, + lumLum, + satLum, + hueFx); GradingHueCurveRcPtr res = newCurve; return res; } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h index 1d2dd0db8f..698e9fa037 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h @@ -21,7 +21,15 @@ class GradingHueCurveImpl : public GradingHueCurve public: GradingHueCurveImpl(); GradingHueCurveImpl(GradingStyle style); - GradingHueCurveImpl(const GradingHueCurves & curves ); + GradingHueCurveImpl( + ConstGradingBSplineCurveRcPtr hueHue, + ConstGradingBSplineCurveRcPtr hueSat, + ConstGradingBSplineCurveRcPtr hueLum, + ConstGradingBSplineCurveRcPtr lumSat, + ConstGradingBSplineCurveRcPtr satSat, + ConstGradingBSplineCurveRcPtr lumLum, + ConstGradingBSplineCurveRcPtr satLum, + ConstGradingBSplineCurveRcPtr hueFx ); GradingHueCurveImpl(const ConstGradingHueCurveRcPtr & rhs); GradingHueCurveRcPtr createEditableCopy() const override; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp index 1f82b61d27..84ff7af6e1 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp @@ -5,6 +5,8 @@ #include +#include + #include "ops/gradinghuecurve/GradingHueCurve.h" #include "ops/gradinghuecurve/GradingHueCurveOpData.h" #include "Platform.h" @@ -35,12 +37,27 @@ GradingHueCurveOpData::GradingHueCurveOpData(const GradingHueCurveOpData & rhs) *this = rhs; } -GradingHueCurveOpData::GradingHueCurveOpData(GradingStyle style, - const GradingHueCurves & curves) +GradingHueCurveOpData::GradingHueCurveOpData(GradingStyle style, + ConstGradingBSplineCurveRcPtr hueHue, + ConstGradingBSplineCurveRcPtr hueSat, + ConstGradingBSplineCurveRcPtr hueLum, + ConstGradingBSplineCurveRcPtr lumSat, + ConstGradingBSplineCurveRcPtr satSat, + ConstGradingBSplineCurveRcPtr lumLum, + ConstGradingBSplineCurveRcPtr satLum, + ConstGradingBSplineCurveRcPtr hueFx) : OpData() , m_style(style) { - ConstGradingHueCurveRcPtr hueCurve = GradingHueCurve::Create(curves); + ConstGradingHueCurveRcPtr hueCurve = GradingHueCurve::Create( + hueHue, + hueSat, + hueLum, + lumSat, + satSat, + lumLum, + satLum, + hueFx); m_value = std::make_shared(hueCurve, false); } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h index 396a37b07f..fb8c9fd663 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h @@ -26,7 +26,14 @@ class GradingHueCurveOpData : public OpData GradingHueCurveOpData(GradingStyle style); GradingHueCurveOpData(const GradingHueCurveOpData & rhs); GradingHueCurveOpData(GradingStyle style, - const GradingHueCurves & curve); + ConstGradingBSplineCurveRcPtr hueHue, + ConstGradingBSplineCurveRcPtr hueSat, + ConstGradingBSplineCurveRcPtr hueLum, + ConstGradingBSplineCurveRcPtr lumSat, + ConstGradingBSplineCurveRcPtr satSat, + ConstGradingBSplineCurveRcPtr lumLum, + ConstGradingBSplineCurveRcPtr satLum, + ConstGradingBSplineCurveRcPtr hueFx); GradingHueCurveOpData & operator=(const GradingHueCurveOpData & rhs); virtual ~GradingHueCurveOpData(); diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index d388562be3..0dc2a37aa3 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -170,19 +170,19 @@ float CalcKsi(unsigned i, //------------------------------------------------------------------------------------------------ // -void FitHueSpline(const std::vector& outCtrlPnts, +void FitHueSpline(const std::vector& ctrlPnts, const std::vector& slopes, std::vector& knots, std::vector& coefsA, std::vector& coefsB, std::vector& coefsC) { - knots.push_back( outCtrlPnts[0].m_x ); - unsigned numCtrlPnts = outCtrlPnts.size(); + knots.push_back( ctrlPnts[0].m_x ); + size_t numCtrlPnts = ctrlPnts.size(); for (unsigned i = 0; i < numCtrlPnts - 1; ++i) { - const GradingControlPoint& p0 = outCtrlPnts[i]; - const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + const GradingControlPoint& p0 = ctrlPnts[i]; + const GradingControlPoint& p1 = ctrlPnts[i + 1]; const float dx = p1.m_x - p0.m_x; const float secantSlope = (p1.m_y - p0.m_y) / dx; @@ -196,7 +196,7 @@ void FitHueSpline(const std::vector& outCtrlPnts, else { // Calculate the middle knot. - const float ksi = CalcKsi(i, outCtrlPnts, slopes); + const float ksi = CalcKsi(i, ctrlPnts, slopes); // Calculate the coefficients. const float m_bar = (2.f * secantSlope - slopes[i + 1]) + @@ -217,19 +217,19 @@ void FitHueSpline(const std::vector& outCtrlPnts, //------------------------------------------------------------------------------------------------ // -void EstimateHueSlopes(std::vector& outCtrlPnts, +void EstimateHueSlopes(const std::vector& ctrlPnts, std::vector& slopes, bool isPeriodic, bool isHorizontal) { slopes.clear(); - unsigned numCtrlPnts = outCtrlPnts.size(); + size_t numCtrlPnts = ctrlPnts.size(); std::vector secantSlope; std::vector secantLen; for (unsigned i = 0; i < numCtrlPnts - 1; ++i) { - const GradingControlPoint& p0 = outCtrlPnts[i]; - const GradingControlPoint& p1 = outCtrlPnts[i + 1]; + const GradingControlPoint& p0 = ctrlPnts[i]; + const GradingControlPoint& p1 = ctrlPnts[i + 1]; const float del_x = p1.m_x - p0.m_x; // PrepHueCurveData ensures this is > 0 const float del_y = p1.m_y - p0.m_y; @@ -653,20 +653,20 @@ bool GradingBSplineCurveImpl::isIdentity() const bool isIdentity = true; if( m_curveType == DIAGONAL_B_SPLINE || m_curveType == B_SPLINE || m_curveType == HUE_HUE_B_SPLINE ) { - isIdentity = std::all_of(m_controlPoints.begin(), - m_controlPoints.end(), + isIdentity = std::all_of(m_controlPoints.cbegin(), + m_controlPoints.cend(), [](const GradingControlPoint& cp) { return cp.m_x == cp.m_y; }); } else if( m_curveType == PERIODIC_0_B_SPLINE ) { - isIdentity = std::all_of(m_controlPoints.begin(), - m_controlPoints.end(), + isIdentity = std::all_of(m_controlPoints.cbegin(), + m_controlPoints.cend(), [](const GradingControlPoint& cp) { return cp.m_y == 0.f; }); } else if( m_curveType == HORIZONTAL1_B_SPLINE || m_curveType == PERIODIC_1_B_SPLINE ) { - isIdentity = std::all_of(m_controlPoints.begin(), - m_controlPoints.end(), + isIdentity = std::all_of(m_controlPoints.cbegin(), + m_controlPoints.cend(), [](const GradingControlPoint& cp) { return cp.m_y == 1.f; }); } else { @@ -804,6 +804,12 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots { // If the user-supplied slopes are non-zero, use those. slopes = m_slopesArray; + + // We need to ensure equal number of slopes and control points for the spline fit. + while(slopes.size() < resultCtrlPnts.size()) + { + slopes.push_back(0.0f); + } } else { diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp index 810f05b716..fa2fa6589b 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp @@ -123,8 +123,7 @@ OCIO_ADD_TEST(GradingHueCurveOpData, validate) // Curves with a single control point are not valid. auto curve = OCIO::GradingBSplineCurve::Create(1); - OCIO::GradingHueCurves curvesStruct{curve, curve, curve, curve, curve, curve, curve, curve}; - auto curves = OCIO::GradingHueCurve::Create(curvesStruct); + auto curves = OCIO::GradingHueCurve::Create(curve, curve, curve, curve, curve, curve, curve, curve); OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, "There must be at least 2 control points."); @@ -132,16 +131,14 @@ OCIO_ADD_TEST(GradingHueCurveOpData, validate) curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.7f,0.3f }, { 0.5f,0.7f },{ 1.f,1.f } }); - curvesStruct = OCIO::GradingHueCurves{curve, curve, curve, curve, curve, curve, curve, curve}; - curves = OCIO::GradingHueCurve::Create(curvesStruct); + curves = OCIO::GradingHueCurve::Create(curve, curve, curve, curve, curve, curve, curve, curve); OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, "has a x coordinate '0.5' that is less from previous control " "point x cooordinate '0.7'."); // Fix the curve x coordinate. curve->getControlPoint(1).m_x = 0.3f; - curvesStruct = OCIO::GradingHueCurves{curve, curve, curve, curve, curve, curve, curve, curve}; - curves = OCIO::GradingHueCurve::Create(curvesStruct); + curves = OCIO::GradingHueCurve::Create(curve, curve, curve, curve, curve, curve, curve, curve); OCIO_CHECK_NO_THROW(gc.setValue(curves)); OCIO_CHECK_NO_THROW(gc.validate()); } From 3504fe4c783a7608e3661f9962f8967c3b477dea Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Wed, 26 Jun 2024 14:06:13 -0400 Subject: [PATCH 13/30] Fix for missing slopes on periodic curves (cherry picked from commit 3f393f8b100b508ef39ac908535f05677e77decb) Signed-off-by: Doug Walker --- .../ops/gradingrgbcurve/GradingBSplineCurve.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index 0dc2a37aa3..cc8bec5520 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -806,9 +806,13 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots slopes = m_slopesArray; // We need to ensure equal number of slopes and control points for the spline fit. - while(slopes.size() < resultCtrlPnts.size()) + if(isPeriodic) { - slopes.push_back(0.0f); + float firstSlope = slopes.back(); + slopes.insert(slopes.begin(), firstSlope); + + float lastSlope = slopes.front(); + slopes.push_back(lastSlope); } } else From ffcffe961bff0f101543e87c7c1ec02a7632505f Mon Sep 17 00:00:00 2001 From: Jonathan Laroche Date: Thu, 27 Jun 2024 07:39:41 -0400 Subject: [PATCH 14/30] Remove leftover #include from debuging (cherry picked from commit e7fd3f2c819bc3f69a14b8f801df90bf974cf06a) Signed-off-by: Doug Walker --- src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp index 84ff7af6e1..8a1ae6ab7e 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp @@ -5,8 +5,6 @@ #include -#include - #include "ops/gradinghuecurve/GradingHueCurve.h" #include "ops/gradinghuecurve/GradingHueCurveOpData.h" #include "Platform.h" From 06def9551574d84a0301267c4b04c508c913e8db Mon Sep 17 00:00:00 2001 From: larochj <143038478+larochj@users.noreply.github.com> Date: Thu, 18 Jul 2024 07:41:22 -0400 Subject: [PATCH 15/30] Enable to draw the curve eval only for a specific hue curve. (#11) (cherry picked from commit d658e707be29e08437e9fad8a1cdf01bfa909633) Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTransforms.h | 12 ++++++++++ .../gradinghuecurve/GradingHueCurveOpData.cpp | 3 +++ .../gradinghuecurve/GradingHueCurveOpData.h | 4 ++++ .../gradinghuecurve/GradingHueCurveOpGPU.cpp | 22 +++++++++++++------ .../transforms/GradingHueCurveTransform.cpp | 10 +++++++++ .../transforms/GradingHueCurveTransform.h | 3 +++ 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/include/OpenColorIO/OpenColorTransforms.h b/include/OpenColorIO/OpenColorTransforms.h index 1f874e2173..8c28e9f26e 100644 --- a/include/OpenColorIO/OpenColorTransforms.h +++ b/include/OpenColorIO/OpenColorTransforms.h @@ -1308,6 +1308,18 @@ class OCIOEXPORT GradingHueCurveTransform : public Transform virtual bool getBypassLinToLog() const = 0; virtual void setBypassLinToLog(bool bypass) = 0; + /** + * Enable drawCurveOnly mode to return the output value of a spline curve without any of the + * other associated processing of the RGB values. This is useful when the curves need to be + * graphed independently in a user interface. To use this, set the curve parameters on the + * Hue-Sat curve. The R, G, and B values will be sent through that curve but with the interpretation + * that they are the input axis to the curve (which would be hue, sat, or luma) rather than RGB. + * This mode ignores the setting of BypassLinToLog, so for scene-linear curves the luma values are + * interpreted as already being in the logarithmic (f-stop) space. + */ + virtual bool getDrawCurveOnly() const = 0; + virtual void setDrawCurveOnly( bool drawCurveOnly ) = 0; + ///** // * Parameters can be made dynamic so the values can be changed through the CPU or GPU processor, // * but if there are several GradingHueCurveTransform only one can have dynamic parameters. diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp index 8a1ae6ab7e..27c7096ad3 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp @@ -68,6 +68,7 @@ GradingHueCurveOpData & GradingHueCurveOpData::operator=(const GradingHueCurveOp m_direction = rhs.m_direction; m_style = rhs.m_style; m_bypassLinToLog = rhs.m_bypassLinToLog; + m_drawCurveOnly = rhs.m_drawCurveOnly; // Copy dynamic properties. Sharing happens when needed, with CPUOp for instance. m_value->setValue(rhs.m_value->getValue()); @@ -76,6 +77,7 @@ GradingHueCurveOpData & GradingHueCurveOpData::operator=(const GradingHueCurveOp m_value->makeDynamic(); } + return *this; } @@ -228,6 +230,7 @@ bool GradingHueCurveOpData::equals(const OpData & other) const if (m_direction != rop->m_direction || m_style != rop->m_style || m_bypassLinToLog != rop->m_bypassLinToLog || + m_drawCurveOnly != rop->m_drawCurveOnly || !m_value->equals( *(rop->m_value) )) { return false; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h index fb8c9fd663..5d249a261c 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h @@ -66,6 +66,9 @@ class GradingHueCurveOpData : public OpData bool getBypassLinToLog() const noexcept { return m_bypassLinToLog; } void setBypassLinToLog(bool bypass) noexcept { m_bypassLinToLog = bypass; } + bool getDrawCurveOnly() const noexcept { return m_drawCurveOnly; } + void setDrawCurveOnly(bool drawCurveOnly) noexcept { m_drawCurveOnly = drawCurveOnly; } + TransformDirection getDirection() const noexcept; void setDirection(TransformDirection dir) noexcept; @@ -85,6 +88,7 @@ class GradingHueCurveOpData : public OpData GradingStyle m_style; DynamicPropertyGradingHueCurveImplRcPtr m_value; bool m_bypassLinToLog{ false }; + bool m_drawCurveOnly{ false }; TransformDirection m_direction{ TRANSFORM_DIR_FORWARD }; }; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp index 363bf44485..7fb67fbe95 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp @@ -256,8 +256,18 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, const GCProperties & props, bool dyn, bool doLinToLog, + bool drawCurveOnly, GradingStyle style) { + const std::string pix(shaderCreator->getPixelName()); + if(drawCurveOnly) + { + st.newLine() << pix << ".r = " << props.m_eval << "(1, " << pix << ".r, 1.);"; // HUE-SAT + st.newLine() << pix << ".g = " << props.m_eval << "(1, " << pix << ".g, 1.);"; // HUE-SAT + st.newLine() << pix << ".b = " << props.m_eval << "(1, " << pix << ".b, 1.);"; // HUE-SAT + return; + } + if (dyn) { st.newLine() << "if (!" << props.m_localBypass << ")"; @@ -299,8 +309,6 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, st.newLine() << ""; } - const std::string pix(shaderCreator->getPixelName()); - st.newLine() << ""; st.newLine() << "float hueSatGain = max(0., " << props.m_eval << "(1, " << pix << ".r, 1.));"; // HUE-SAT st.newLine() << "float hueLumGain = max(0., " << props.m_eval << "(2, " << pix << ".r, 1.));"; // HUE-LUM @@ -463,10 +471,10 @@ void GetHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, shaderCreator->addDynamicProperty(newProp); // Add uniforms only if needed. - AddGCPropertiesUniforms(shaderCreator, shaderProp, properties); // _addAttributeTextToShaderProgram + AddGCPropertiesUniforms(shaderCreator, shaderProp, properties); // Add helper function plus global variables if they are not dynamic. - AddCurveEvalMethodTextToShaderProgram(shaderCreator, gcData, properties, dyn); // _addHelperMethodTextToShaderProgram + AddCurveEvalMethodTextToShaderProgram(shaderCreator, gcData, properties, dyn); } else { @@ -474,14 +482,14 @@ void GetHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, AddCurveEvalMethodTextToShaderProgram(shaderCreator, gcData, properties, dyn); } - const bool bypassLinToLog = (style == GRADING_LIN) && !gcData->getBypassLinToLog(); + const bool doLinToLog = (style == GRADING_LIN) && !gcData->getBypassLinToLog(); switch (dir) { case TRANSFORM_DIR_FORWARD: - AddGCForwardShader(shaderCreator, st, properties, dyn, bypassLinToLog, style); // _addProcessingTextToShaderProgram + AddGCForwardShader(shaderCreator, st, properties, dyn, doLinToLog, gcData->getDrawCurveOnly(), style); break; case TRANSFORM_DIR_INVERSE: // TODO: Implement the inverse shader. - //AddGCInverseShader(shaderCreator, st, properties, dyn, bypassLinToLog, style); // _addProcessingTextToShaderProgram + //AddGCInverseShader(shaderCreator, st, properties, dyn, bypassLinToLog, style); break; } diff --git a/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp b/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp index 57ebefa207..435b56b436 100644 --- a/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp +++ b/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp @@ -121,6 +121,16 @@ void GradingHueCurveTransformImpl::setBypassLinToLog(bool bypass) noexcept data().setBypassLinToLog(bypass); } +bool GradingHueCurveTransformImpl::getDrawCurveOnly() const noexcept +{ + return data().getDrawCurveOnly(); +} + +void GradingHueCurveTransformImpl::setDrawCurveOnly( bool drawCurveOnly ) noexcept +{ + data().setDrawCurveOnly( drawCurveOnly ); +} + bool GradingHueCurveTransformImpl::isDynamic() const noexcept { return data().isDynamic(); diff --git a/src/OpenColorIO/transforms/GradingHueCurveTransform.h b/src/OpenColorIO/transforms/GradingHueCurveTransform.h index ce9cd67117..ee3bf7c465 100644 --- a/src/OpenColorIO/transforms/GradingHueCurveTransform.h +++ b/src/OpenColorIO/transforms/GradingHueCurveTransform.h @@ -48,6 +48,9 @@ class GradingHueCurveTransformImpl : public GradingHueCurveTransform bool getBypassLinToLog() const noexcept override; void setBypassLinToLog(bool bypass) noexcept override; + + bool getDrawCurveOnly() const noexcept override; + void setDrawCurveOnly(bool drawCurveOnly) noexcept override; bool isDynamic() const noexcept override; void makeDynamic() noexcept override; From e12f2485c4b2f0d93fb5734d301a03b3214d5f39 Mon Sep 17 00:00:00 2001 From: larochj <143038478+larochj@users.noreply.github.com> Date: Fri, 26 Jul 2024 09:02:18 -0400 Subject: [PATCH 16/30] RGB to HSY fixed functions using CPU (#17) (cherry picked from commit a25c1c7fed4c0b6ea1b6c5e668acb12a45ee6f19) Signed-off-by: Doug Walker --- .../ops/fixedfunction/FixedFunctionOpCPU.cpp | 308 +++++++++++++++++- .../FixedFunctionOpCPU_tests.cpp | 73 +++++ .../fixedfunction/FixedFunctionOp_tests.cpp | 78 +++++ tests/gpu/FixedFunctionOp_test.cpp | 104 ++++++ tests/osl/FixedFunctionOp_test.cpp | 72 ++++ 5 files changed, 617 insertions(+), 18 deletions(-) diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp index b6aab3160d..d547002366 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp @@ -238,6 +238,66 @@ class Renderer_HSV_TO_RGB : public OpCPU void apply(const void * inImg, void * outImg, long numPixels) const override; }; +class Renderer_RGB_TO_HSY_LOG : public OpCPU +{ + const float MIN_0 = -0.1f; +public: + Renderer_RGB_TO_HSY_LOG() = delete; + explicit Renderer_RGB_TO_HSY_LOG(ConstFixedFunctionOpDataRcPtr & data); + + void apply(const void * inImg, void * outImg, long numPixels) const override; +}; + +class Renderer_HSY_LOG_TO_RGB : public OpCPU +{ + const float MIN_0 = -0.1f;; +public: + Renderer_HSY_LOG_TO_RGB() = delete; + explicit Renderer_HSY_LOG_TO_RGB(ConstFixedFunctionOpDataRcPtr & data); + + void apply(const void * inImg, void * outImg, long numPixels) const override; +}; + +class Renderer_RGB_TO_HSY_VID : public OpCPU +{ + const float MIN_0 = 0.0f; +public: + Renderer_RGB_TO_HSY_VID() = delete; + explicit Renderer_RGB_TO_HSY_VID(ConstFixedFunctionOpDataRcPtr & data); + + void apply(const void * inImg, void * outImg, long numPixels) const override; +}; + +class Renderer_HSY_VID_TO_RGB : public OpCPU +{ + const float MIN_0 = 0.0f; +public: + Renderer_HSY_VID_TO_RGB() = delete; + explicit Renderer_HSY_VID_TO_RGB(ConstFixedFunctionOpDataRcPtr & data); + + void apply(const void * inImg, void * outImg, long numPixels) const override; +}; + +class Renderer_RGB_TO_HSY_LIN : public OpCPU +{ + const float MIN_0 = 0.2f; +public: + Renderer_RGB_TO_HSY_LIN() = delete; + explicit Renderer_RGB_TO_HSY_LIN(ConstFixedFunctionOpDataRcPtr & data); + + void apply(const void * inImg, void * outImg, long numPixels) const override; +}; + +class Renderer_HSY_LIN_TO_RGB : public OpCPU +{ + const float MIN_0 = 0.2f; +public: + Renderer_HSY_LIN_TO_RGB() = delete; + explicit Renderer_HSY_LIN_TO_RGB(ConstFixedFunctionOpDataRcPtr & data); + + void apply(const void * inImg, void * outImg, long numPixels) const override; +}; + class Renderer_XYZ_TO_xyY : public OpCPU { public: @@ -1534,6 +1594,230 @@ void Renderer_HSV_TO_RGB::apply(const void * inImg, void * outImg, long numPixel } } +void applyHSYToRGB(const void * inImg, void * outImg, long numPixels, float min0) +{ + const float * in = (const float *)inImg; + float * out = (float *)outImg; + + for(unsigned idx=0; idx 0.f) // linear + { + const float sumRgb = red + grn + blu; + + const float k = 0.15f; + const float loGain = 5.f; + + sat /= 1.4f; + const float tmp = -sat * sumRgb + sat * 3.f * luma + distRgb; + const float s1 = (tmp == 0.f) ? 0.f : sat * (k + 3.f * luma) / tmp; + const float s0 = sat / std::max(1e-10f, distRgb * loGain); + + const float maxLum = 0.01f; + const float minLum = maxLum * 0.1f; + const float alpha = CLAMP( (luma - minLum) / (maxLum - minLum), 0.f, 1.f ); + + if (alpha == 1.f) + gainS = s1; + else if (alpha == 0.f) + gainS = s0; + else + { + const float a = distRgb * loGain * (1.f - alpha) * (sumRgb - 3.f * luma); + const float b = distRgb * loGain * (1.f - alpha) * (k + 3.f * luma) + distRgb * alpha - + sat * (sumRgb - 3.f * luma); + const float c = -sat * (k + 3.f * luma); + const float discrim = sqrt( b * b - 4.f * a * c ); + const float denom = -discrim - b; + gainS = (2.f * c) / denom; + + gainS = gainS >= 0.f ? gainS : (2.f * c) / (denom + discrim * 2.f); + } + } + else if (min0 < 0.f) // log + { + const float satGain = 4.f; + const float currSat = distRgb * satGain; + gainS = sat / std::max(1e-10f, currSat); + } + else // video + { + const float satGain = 1.25f; + const float currSat = distRgb * satGain; + gainS = sat / std::max(1e-10f, currSat); + } + + out[0] = luma + gainS * (red - luma); // red + out[1] = luma + gainS * (grn - luma); // grn + out[2] = luma + gainS * (blu - luma); // blu + out[3] = in[3]; // alpha + + in += 4; + out += 4; + } + +} + +void applyRGBToHSY(const void * inImg, void * outImg, long numPixels, float min0) +{ + const float * in = (const float *)inImg; + float * out = (float *)outImg; + + for (long idx=0; idx 0.f) // linear + { + const float sumRgb = red + grn + blu; + const float k = 0.15f; + const float satHi = distRgb / (k + sumRgb); + const float loGain = 5.f; + const float satLo = distRgb * loGain; + const float maxLum = 0.01f; + const float minLum = maxLum * 0.1; + const float alpha = CLAMP( (luma - minLum) / (maxLum - minLum), 0.f, 1.f ); + sat = satLo + alpha * (satHi - satLo); + sat *= 1.4f; + } + else if (min0 < 0.f) // log + { + const float sat_gain = 4.f; + sat = distRgb * sat_gain; + } + else // video + { + const float sat_gain = 1.25f; + sat = distRgb * sat_gain; + } + + float hue = 0.f; + if (rgbMin != rgbMax) + { + float delta = rgbMax - rgbMin; + if (red == rgbMax) + { + hue = 1.0f + (grn - blu) / delta; + } + else + { + if (grn == rgbMax) + { + hue = 3.0f + (blu - red) / delta; + } + else + { + hue = 5.0f + (red - grn) / delta; + } + } + hue *= 0.16666666666666666f; + } + + out[0] = hue; + out[1] = sat; + out[2] = luma; + out[3] = in[3]; + + in += 4; + out += 4; + } +} + +Renderer_RGB_TO_HSY_LOG::Renderer_RGB_TO_HSY_LOG(ConstFixedFunctionOpDataRcPtr & /*data*/) + : OpCPU() +{ +} + +void Renderer_RGB_TO_HSY_LOG::apply(const void * inImg, void * outImg, long numPixels) const +{ + applyRGBToHSY(inImg, outImg, numPixels, MIN_0); +} + +Renderer_HSY_LOG_TO_RGB::Renderer_HSY_LOG_TO_RGB(ConstFixedFunctionOpDataRcPtr & /*data*/) + : OpCPU() +{ +} + +void Renderer_HSY_LOG_TO_RGB::apply(const void * inImg, void * outImg, long numPixels) const +{ + applyHSYToRGB(inImg, outImg, numPixels, MIN_0); +} + +Renderer_RGB_TO_HSY_LIN::Renderer_RGB_TO_HSY_LIN(ConstFixedFunctionOpDataRcPtr & /*data*/) + : OpCPU() +{ +} + +void Renderer_RGB_TO_HSY_LIN::apply(const void * inImg, void * outImg, long numPixels) const +{ + applyRGBToHSY(inImg, outImg, numPixels, MIN_0); +} + +Renderer_HSY_LIN_TO_RGB::Renderer_HSY_LIN_TO_RGB(ConstFixedFunctionOpDataRcPtr & /*data*/) + : OpCPU() +{ +} + +void Renderer_HSY_LIN_TO_RGB::apply(const void * inImg, void * outImg, long numPixels) const +{ + applyHSYToRGB(inImg, outImg, numPixels, MIN_0); +} + +Renderer_RGB_TO_HSY_VID::Renderer_RGB_TO_HSY_VID(ConstFixedFunctionOpDataRcPtr & /*data*/) + : OpCPU() +{ +} + +void Renderer_RGB_TO_HSY_VID::apply(const void * inImg, void * outImg, long numPixels) const +{ + applyRGBToHSY(inImg, outImg, numPixels, MIN_0); +} + +Renderer_HSY_VID_TO_RGB::Renderer_HSY_VID_TO_RGB(ConstFixedFunctionOpDataRcPtr & /*data*/) + : OpCPU() +{ +} + +void Renderer_HSY_VID_TO_RGB::apply(const void * inImg, void * outImg, long numPixels) const +{ + applyHSYToRGB(inImg, outImg, numPixels, MIN_0); +} + Renderer_XYZ_TO_xyY::Renderer_XYZ_TO_xyY(ConstFixedFunctionOpDataRcPtr & /*data*/) : OpCPU() { @@ -2325,41 +2609,29 @@ ConstOpCPURcPtr GetFixedFunctionCPURenderer(ConstFixedFunctionOpDataRcPtr & func case FixedFunctionOpData::RGB_TO_HSY_LOG: { - // TODO: Handle CPU rendering for this FixedFunction - return nullptr; - //return std::make_shared(func); + return std::make_shared(func); } case FixedFunctionOpData::HSY_LOG_TO_RGB: { - // TODO: Handle CPU rendering for this FixedFunction - return nullptr; - //return std::make_shared(func); + return std::make_shared(func); } case FixedFunctionOpData::RGB_TO_HSY_LIN: { - // TODO: Handle CPU rendering for this FixedFunction - return nullptr; - //return std::make_shared(func); + return std::make_shared(func); } case FixedFunctionOpData::HSY_LIN_TO_RGB: { - // TODO: Handle CPU rendering for this FixedFunction - return nullptr; - //return std::make_shared(func); + return std::make_shared(func); } case FixedFunctionOpData::RGB_TO_HSY_VID: { - // TODO: Handle CPU rendering for this FixedFunction - return nullptr; - //return std::make_shared(func); + return std::make_shared(func); } case FixedFunctionOpData::HSY_VID_TO_RGB: { - // TODO: Handle CPU rendering for this FixedFunction - return nullptr; - //return std::make_shared(func); + return std::make_shared(func); } } diff --git a/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp b/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp index 008264582e..6a45844af0 100644 --- a/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp +++ b/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp @@ -1068,6 +1068,79 @@ OCIO_ADD_TEST(FixedFunctionOpCPU, RGB_TO_HSV) ApplyFixedFunction(&img[0], &rgbFrame[0], numHSV, dataFInv, 1e-6f, __LINE__); } +OCIO_ADD_TEST(FixedFunctionOpCPU, RGB_TO_HSY_LIN) +{ + const std::vector hsyFrame { + 4.70554752e-01f, 9.12594033f, 3.26650218e-02f, 0.f, + 0.75f, 0.22196741f, 0.38596f, 1.f, + 0.08333333f, 0.12976444f, 0.034974f, 0.f, + 0.96296296f, 9.7034f, -0.1862f, 1.f }; + + const std::vector rgbFrame { + -0.075290f, 0.078996f, -0.108397f, 0.f, + 0.3f, 0.4f, 0.5f, 1.f, + 0.05f, 0.03f, 0.04f, 0.f, + 0.3f, -0.4f, 0.5f, 1.f }; + + OCIO::ConstFixedFunctionOpDataRcPtr dataFwdLin + = std::make_shared(OCIO::FixedFunctionOpData::RGB_TO_HSY_LIN); + + std::vector img = rgbFrame; + ApplyFixedFunction(&img[0], &hsyFrame[0], 4, dataFwdLin, 1e-6f, __LINE__); + + OCIO::ConstFixedFunctionOpDataRcPtr dataFInvLin + = std::make_shared(OCIO::FixedFunctionOpData::HSY_LIN_TO_RGB); + + img = hsyFrame; + ApplyFixedFunction(&img[0], &rgbFrame[0], 4, dataFInvLin, 1e-6f, __LINE__); +} + +OCIO_ADD_TEST(FixedFunctionOpCPU, RGB_TO_HSY_LOG) +{ + const std::vector hsyFrame { + 14563.0f / 65535, 64392.0f / 65535, 20899.0f / 65535, 32007.0f / 65535, + 50061.0f / 65535, 64310.0f / 65535, 6328.0f / 65535, 65535.0f / 65535 }; + + const std::vector rgbFrame { + 1800.0f / 4095, 1200.0f / 4095, 900.0f / 4095, 2000.0f / 4095, + 40.0f / 4095, 440.0f / 4095, 1000.0f / 4095, 4095.0f / 4095 }; + + OCIO::ConstFixedFunctionOpDataRcPtr dataFwdLin + = std::make_shared(OCIO::FixedFunctionOpData::RGB_TO_HSY_LOG); + + std::vector img = rgbFrame; + ApplyFixedFunction(&img[0], &hsyFrame[0], 2, dataFwdLin, 1e-5f, __LINE__); + + OCIO::ConstFixedFunctionOpDataRcPtr dataFInvLin + = std::make_shared(OCIO::FixedFunctionOpData::HSY_LOG_TO_RGB); + + img = hsyFrame; + ApplyFixedFunction(&img[0], &rgbFrame[0], 2, dataFInvLin, 1e-5f, __LINE__); +} + +OCIO_ADD_TEST(FixedFunctionOpCPU, RGB_TO_HSY_VID) +{ + const std::vector hsyFrame { + 0.54190051555634f, 1.0851333141327f, 0.55111348628998f, 0.48840048909187f, + 0.54262113571167f, 1.4824789762497f, 1.1281162500381f, 1.f }; + + const std::vector rgbFrame { + 0.12152557820082f, 0.70731294155121f, 0.26879417896271f, 0.48840048909187f, + 0.53938156366348f, 1.3418402671814f, 0.74459171295166f, 1.f }; + + OCIO::ConstFixedFunctionOpDataRcPtr dataFwdLin + = std::make_shared(OCIO::FixedFunctionOpData::RGB_TO_HSY_VID); + + std::vector img = rgbFrame; + ApplyFixedFunction(&img[0], &hsyFrame[0], 2, dataFwdLin, 1e-6f, __LINE__); + + OCIO::ConstFixedFunctionOpDataRcPtr dataFInvLin + = std::make_shared(OCIO::FixedFunctionOpData::HSY_VID_TO_RGB); + + img = hsyFrame; + ApplyFixedFunction(&img[0], &rgbFrame[0], 2, dataFInvLin, 1e-6f, __LINE__); +} + OCIO_ADD_TEST(FixedFunctionOpCPU, XYZ_TO_xyY) { const std::vector inputFrame { diff --git a/tests/cpu/ops/fixedfunction/FixedFunctionOp_tests.cpp b/tests/cpu/ops/fixedfunction/FixedFunctionOp_tests.cpp index cdecbfda49..1f6e8cfc8b 100644 --- a/tests/cpu/ops/fixedfunction/FixedFunctionOp_tests.cpp +++ b/tests/cpu/ops/fixedfunction/FixedFunctionOp_tests.cpp @@ -302,6 +302,84 @@ OCIO_ADD_TEST(FixedFunctionOps, RGB_TO_HSV) OCIO_CHECK_NE(std::string::npos, StringUtils::Find(typeName, "Renderer_RGB_TO_HSV")); } +OCIO_ADD_TEST(FixedFunctionOps, RGB_TO_HSY_LIN) +{ + OCIO::OpRcPtrVec ops; + + OCIO_CHECK_NO_THROW(OCIO::CreateFixedFunctionOp(ops, OCIO::FixedFunctionOpData::RGB_TO_HSY_LIN, {})); + OCIO_CHECK_NO_THROW(OCIO::CreateFixedFunctionOp(ops, OCIO::FixedFunctionOpData::HSY_LIN_TO_RGB, {})); + + OCIO_CHECK_NO_THROW(ops.finalize()); + OCIO_REQUIRE_EQUAL(ops.size(), 2); + + OCIO::ConstOpRcPtr op0 = ops[0]; + OCIO::ConstOpRcPtr op1 = ops[1]; + + OCIO_CHECK_ASSERT(!op0->isIdentity()); + OCIO_CHECK_ASSERT(!op1->isIdentity()); + + OCIO_CHECK_ASSERT(op0->isSameType(op1)); + OCIO_CHECK_ASSERT(op0->isInverse(op1)); + OCIO_CHECK_ASSERT(op1->isInverse(op0)); + + OCIO::ConstOpCPURcPtr cpuOp = op0->getCPUOp(false); + const OCIO::OpCPU & c = *cpuOp; + const std::string typeName(typeid(c).name()); + OCIO_CHECK_NE(std::string::npos, StringUtils::Find(typeName, "Renderer_RGB_TO_HSY_LIN")); +} + +OCIO_ADD_TEST(FixedFunctionOps, RGB_TO_HSY_LOG) +{ + OCIO::OpRcPtrVec ops; + + OCIO_CHECK_NO_THROW(OCIO::CreateFixedFunctionOp(ops, OCIO::FixedFunctionOpData::RGB_TO_HSY_LOG, {})); + OCIO_CHECK_NO_THROW(OCIO::CreateFixedFunctionOp(ops, OCIO::FixedFunctionOpData::HSY_LOG_TO_RGB, {})); + + OCIO_CHECK_NO_THROW(ops.finalize()); + OCIO_REQUIRE_EQUAL(ops.size(), 2); + + OCIO::ConstOpRcPtr op0 = ops[0]; + OCIO::ConstOpRcPtr op1 = ops[1]; + + OCIO_CHECK_ASSERT(!op0->isIdentity()); + OCIO_CHECK_ASSERT(!op1->isIdentity()); + + OCIO_CHECK_ASSERT(op0->isSameType(op1)); + OCIO_CHECK_ASSERT(op0->isInverse(op1)); + OCIO_CHECK_ASSERT(op1->isInverse(op0)); + + OCIO::ConstOpCPURcPtr cpuOp = op0->getCPUOp(false); + const OCIO::OpCPU & c = *cpuOp; + const std::string typeName(typeid(c).name()); + OCIO_CHECK_NE(std::string::npos, StringUtils::Find(typeName, "Renderer_RGB_TO_HSY_LOG")); +} + +OCIO_ADD_TEST(FixedFunctionOps, RGB_TO_HSY_VID) +{ + OCIO::OpRcPtrVec ops; + + OCIO_CHECK_NO_THROW(OCIO::CreateFixedFunctionOp(ops, OCIO::FixedFunctionOpData::RGB_TO_HSY_VID, {})); + OCIO_CHECK_NO_THROW(OCIO::CreateFixedFunctionOp(ops, OCIO::FixedFunctionOpData::HSY_VID_TO_RGB, {})); + + OCIO_CHECK_NO_THROW(ops.finalize()); + OCIO_REQUIRE_EQUAL(ops.size(), 2); + + OCIO::ConstOpRcPtr op0 = ops[0]; + OCIO::ConstOpRcPtr op1 = ops[1]; + + OCIO_CHECK_ASSERT(!op0->isIdentity()); + OCIO_CHECK_ASSERT(!op1->isIdentity()); + + OCIO_CHECK_ASSERT(op0->isSameType(op1)); + OCIO_CHECK_ASSERT(op0->isInverse(op1)); + OCIO_CHECK_ASSERT(op1->isInverse(op0)); + + OCIO::ConstOpCPURcPtr cpuOp = op0->getCPUOp(false); + const OCIO::OpCPU & c = *cpuOp; + const std::string typeName(typeid(c).name()); + OCIO_CHECK_NE(std::string::npos, StringUtils::Find(typeName, "Renderer_RGB_TO_HSY_VID")); +} + OCIO_ADD_TEST(FixedFunctionOps, XYZ_TO_xyY) { OCIO::OpRcPtrVec ops; diff --git a/tests/gpu/FixedFunctionOp_test.cpp b/tests/gpu/FixedFunctionOp_test.cpp index 227da21235..c053b4dbaf 100644 --- a/tests/gpu/FixedFunctionOp_test.cpp +++ b/tests/gpu/FixedFunctionOp_test.cpp @@ -1242,6 +1242,110 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSV_inv_custom) test.setErrorThreshold(1e-6f); } +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_fwd) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); + func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); + + test.setProcessor(func); + + test.setErrorThreshold(1e-6f); + +#ifdef __APPLE__ + test.setTestNaN(false); + test.setTestInfinity(false); +#endif +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_inv) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); + func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + test.setProcessor(func); + + test.setErrorThreshold(1e-6f); + + OCIOGPUTest::CustomValues values; + values.m_inputValues = { + 4.70554752e-01f, 9.12594033f, 3.26650218e-02f, 0.f, + 0.75f, 0.22196741f, 0.38596f, 1.f, + 0.08333333f, 0.12976444f, 0.034974f, 0.f, + 0.96296296f, 9.7034f, -0.1862f, 1.f }; + test.setCustomValues(values); + +#ifdef __APPLE__ + test.setTestNaN(false); + test.setTestInfinity(false); +#endif +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_fwd) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); + func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); + + test.setProcessor(func); + + test.setErrorThreshold(1e-6f); + +#ifdef __APPLE__ + test.setTestNaN(false); + test.setTestInfinity(false); +#endif +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_inv) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); + func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + test.setProcessor(func); + + test.setErrorThreshold(1e-6f); + +#ifdef __APPLE__ + test.setTestNaN(false); + test.setTestInfinity(false); +#endif +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_fwd) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); + func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); + + test.setProcessor(func); + + test.setErrorThreshold(1e-6f); + +#ifdef __APPLE__ + test.setTestNaN(false); + test.setTestInfinity(false); +#endif +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_inv) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); + func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + test.setProcessor(func); + + test.setErrorThreshold(1e-6f); + +#ifdef __APPLE__ + test.setTestNaN(false); + test.setTestInfinity(false); +#endif +} + OCIO_ADD_GPU_TEST(FixedFunction, style_XYZ_TO_xyY_fwd) { OCIO::FixedFunctionTransformRcPtr func = diff --git a/tests/osl/FixedFunctionOp_test.cpp b/tests/osl/FixedFunctionOp_test.cpp index c9d19b41cf..05e77d8e7e 100644 --- a/tests/osl/FixedFunctionOp_test.cpp +++ b/tests/osl/FixedFunctionOp_test.cpp @@ -402,6 +402,78 @@ OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSV_inv_custom) m_data->m_threshold = 1e-6f; } +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_fwd) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); + func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); + + m_data->m_transform = func; + + m_data->m_threshold = 1e-6f; + m_data->m_relativeComparison = true; +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_inv) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); + func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + m_data->m_transform = func; + + m_data->m_threshold = 1e-6f; + m_data->m_relativeComparison = true; +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_fwd) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); + func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); + + m_data->m_transform = func; + + m_data->m_threshold = 1e-6f; + m_data->m_relativeComparison = true; +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_inv) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); + func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + m_data->m_transform = func; + + m_data->m_threshold = 1e-6f; + m_data->m_relativeComparison = true; +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_fwd) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); + func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); + + m_data->m_transform = func; + + m_data->m_threshold = 1e-6f; + m_data->m_relativeComparison = true; +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_inv) +{ + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); + func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + m_data->m_transform = func; + + m_data->m_threshold = 1e-6f; + m_data->m_relativeComparison = true; +} + OCIO_OSL_TEST(FixedFunction, style_XYZ_TO_xyY_fwd) { OCIO::FixedFunctionTransformRcPtr func = From 5626e75fe2b7e8b5c331519d13524437ac233ecf Mon Sep 17 00:00:00 2001 From: larochj <143038478+larochj@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:14:08 -0400 Subject: [PATCH 17/30] Fix identity hue curve draw only (#20) (cherry picked from commit 4f276e5c0f60ccb2c460df6521501f2d49d20b0d) Signed-off-by: Doug Walker --- .../gradingrgbcurve/GradingBSplineCurve.cpp | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index cc8bec5520..8a1a3af919 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -768,11 +768,41 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots // Return 0 knots and coefficients when the curve is identity. if (m_controlPoints.size() < 2 || isIdentity()) { - // Identity curve: offset is -1 and count is 0. - knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = -1; - knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = 0; - knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = -1; - knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = 0; + // Set knots and coefficients that represent identity curve. + const int numKnots = static_cast(knotsCoefs.m_numKnots); + const int numCoefs = static_cast(knotsCoefs.m_numCoefs); + + const int N_IDENTITY_KNOTS = 2; + const int N_IDENTITY_COEFS = 3; + + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = numKnots; + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = N_IDENTITY_KNOTS; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = knotsCoefs.m_numCoefs; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = N_IDENTITY_COEFS; + + knotsCoefs.m_knotsArray.begin()[numKnots] = 0.f; + knotsCoefs.m_knotsArray.begin()[numKnots + 1] = 1.f; + + // Identity curve are linear or constant, so set the quadratic coefficient to zero. + knotsCoefs.m_coefsArray.begin()[numCoefs] = 0.f; + + // Set the linear coefficient to match the slope of the identity curve. + const float linearCoef = m_curveType == BSplineCurveType::DIAGONAL_B_SPLINE || + m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE ? + 1.0f : 0.f; + + knotsCoefs.m_coefsArray.begin()[numCoefs + 1] = linearCoef; + + // Set the constant coefficient for an identity curve. + const float constantCoef = m_curveType == BSplineCurveType::PERIODIC_1_B_SPLINE || + m_curveType == BSplineCurveType::HORIZONTAL1_B_SPLINE ? + 1.0f : 0.f; + + knotsCoefs.m_coefsArray.begin()[numCoefs + 2] = constantCoef; + + + knotsCoefs.m_numKnots += N_IDENTITY_KNOTS; + knotsCoefs.m_numCoefs += N_IDENTITY_COEFS; return; } From 587d4864f051ddacd54babb695e5883ab2fa898b Mon Sep 17 00:00:00 2001 From: larochj <143038478+larochj@users.noreply.github.com> Date: Thu, 26 Sep 2024 09:17:16 -0400 Subject: [PATCH 18/30] < --- src/OpenColorIO/transforms/CDLTransform.cpp | 20 +++++++++++++++---- .../transforms/ExponentTransform.cpp | 6 +++--- .../ExponentWithLinearTransform.cpp | 10 +++++----- .../transforms/FixedFunctionTransform.cpp | 5 +++-- .../transforms/LogAffineTransform.cpp | 8 ++++---- .../transforms/LogCameraTransform.cpp | 12 +++++------ src/OpenColorIO/transforms/Lut1DTransform.cpp | 4 ++-- src/OpenColorIO/transforms/Lut3DTransform.cpp | 4 ++-- .../transforms/MatrixTransform.cpp | 10 +++++----- 9 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/OpenColorIO/transforms/CDLTransform.cpp b/src/OpenColorIO/transforms/CDLTransform.cpp index cee7ed963d..393bec4dbe 100755 --- a/src/OpenColorIO/transforms/CDLTransform.cpp +++ b/src/OpenColorIO/transforms/CDLTransform.cpp @@ -373,13 +373,25 @@ std::ostream & operator<< (std::ostream & os, const CDLTransform & t) os << ""; return os; diff --git a/src/OpenColorIO/transforms/ExponentTransform.cpp b/src/OpenColorIO/transforms/ExponentTransform.cpp index 719c9b44dc..1ceb576751 100755 --- a/src/OpenColorIO/transforms/ExponentTransform.cpp +++ b/src/OpenColorIO/transforms/ExponentTransform.cpp @@ -103,13 +103,13 @@ std::ostream & operator<< (std::ostream & os, const ExponentTransform & t) os << ""; return os; } diff --git a/src/OpenColorIO/transforms/ExponentWithLinearTransform.cpp b/src/OpenColorIO/transforms/ExponentWithLinearTransform.cpp index 53c4ca480b..5e93f0aec6 100644 --- a/src/OpenColorIO/transforms/ExponentWithLinearTransform.cpp +++ b/src/OpenColorIO/transforms/ExponentWithLinearTransform.cpp @@ -145,22 +145,22 @@ std::ostream & operator<< (std::ostream & os, const ExponentWithLinearTransform double gamma[4]; t.getGamma(gamma); - os << "gamma=" << gamma[0]; + os << "gamma=[" << gamma[0]; for (int i = 1; i < 4; ++i) { - os << " " << gamma[i]; + os << ", " << gamma[i]; } double offset[4]; t.getOffset(offset); - os << ", offset=" << offset[0]; + os << "], offset=[" << offset[0]; for (int i = 1; i < 4; ++i) { - os << " " << offset[i]; + os << ", " << offset[i]; } - os << ", style=" << NegativeStyleToString(t.getNegativeStyle()); + os << "], style=" << NegativeStyleToString(t.getNegativeStyle()); os << ">"; return os; } diff --git a/src/OpenColorIO/transforms/FixedFunctionTransform.cpp b/src/OpenColorIO/transforms/FixedFunctionTransform.cpp index 1449e09dba..07baef12a9 100644 --- a/src/OpenColorIO/transforms/FixedFunctionTransform.cpp +++ b/src/OpenColorIO/transforms/FixedFunctionTransform.cpp @@ -148,11 +148,12 @@ std::ostream & operator<< (std::ostream & os, const FixedFunctionTransform & t) FixedFunctionOpData::Params params(numParams, 0.); t.getParams(¶ms[0]); - os << ", params=" << params[0]; + os << ", params=[" << params[0]; for (size_t i = 1; i < numParams; ++i) { - os << " " << params[i]; + os << ", " << params[i]; } + os << "]"; } os << ">"; diff --git a/src/OpenColorIO/transforms/LogAffineTransform.cpp b/src/OpenColorIO/transforms/LogAffineTransform.cpp index d54de42d85..6711a75fea 100644 --- a/src/OpenColorIO/transforms/LogAffineTransform.cpp +++ b/src/OpenColorIO/transforms/LogAffineTransform.cpp @@ -126,13 +126,13 @@ std::ostream & operator<< (std::ostream & os, const LogAffineTransform & t) os << ", base=" << t.getBase(); double values[3]; t.getLogSideSlopeValue(values); - os << ", logSideSlope=" << values[0] << " " << values[1] << " " << values[2]; + os << ", logSideSlope=[" << values[0] << ", " << values[1] << ", " << values[2] << "]"; t.getLogSideOffsetValue(values); - os << ", logSideOffset=" << values[0] << " " << values[1] << " " << values[2]; + os << ", logSideOffset=[" << values[0] << ", " << values[1] << ", " << values[2] << "]"; t.getLinSideSlopeValue(values); - os << ", linSideSlope=" << values[0] << " " << values[1] << " " << values[2]; + os << ", linSideSlope=[" << values[0] << ", " << values[1] << ", " << values[2] << "]"; t.getLinSideOffsetValue(values); - os << ", linSideOffset=" << values[0] << " " << values[1] << " " << values[2]; + os << ", linSideOffset=[" << values[0] << ", " << values[1] << ", " << values[2] << "]"; os << ">"; return os; diff --git a/src/OpenColorIO/transforms/LogCameraTransform.cpp b/src/OpenColorIO/transforms/LogCameraTransform.cpp index ee7cb7276a..1f4188417a 100644 --- a/src/OpenColorIO/transforms/LogCameraTransform.cpp +++ b/src/OpenColorIO/transforms/LogCameraTransform.cpp @@ -158,18 +158,18 @@ std::ostream & operator<< (std::ostream & os, const LogCameraTransform & t) os << ", base=" << t.getBase(); double values[3]; t.getLogSideSlopeValue(values); - os << ", logSideSlope=" << values[0] << " " << values[1] << " " << values[2]; + os << ", logSideSlope=[" << values[0] << ", " << values[1] << ", " << values[2] << "]"; t.getLogSideOffsetValue(values); - os << ", logSideOffset=" << values[0] << " " << values[1] << " " << values[2]; + os << ", logSideOffset=[" << values[0] << ", " << values[1] << ", " << values[2] << "]"; t.getLinSideSlopeValue(values); - os << ", linSideSlope=" << values[0] << " " << values[1] << " " << values[2]; + os << ", linSideSlope=[" << values[0] << ", " << values[1] << ", " << values[2] << "]"; t.getLinSideOffsetValue(values); - os << ", linSideOffset=" << values[0] << " " << values[1] << " " << values[2]; + os << ", linSideOffset=[" << values[0] << ", " << values[1] << ", " << values[2] << "]"; t.getLinSideBreakValue(values); - os << ", linSideBreak=" << values[0] << " " << values[1] << " " << values[2]; + os << ", linSideBreak=[" << values[0] << ", " << values[1] << ", " << values[2] << "]"; if (t.getLinearSlopeValue(values)) { - os << ", linearSlope=" << values[0] << " " << values[1] << " " << values[2]; + os << ", linearSlope=[" << values[0] << ", " << values[1] << ", " << values[2] << "]"; } os << ">"; diff --git a/src/OpenColorIO/transforms/Lut1DTransform.cpp b/src/OpenColorIO/transforms/Lut1DTransform.cpp index 2e3f93a5fa..cc3a9f7f61 100644 --- a/src/OpenColorIO/transforms/Lut1DTransform.cpp +++ b/src/OpenColorIO/transforms/Lut1DTransform.cpp @@ -214,9 +214,9 @@ std::ostream & operator<< (std::ostream & os, const Lut1DTransform & t) bMax = std::max(bMax, b); } os << "minrgb=["; - os << rMin << " " << gMin << " " << bMin << "], "; + os << rMin << ", " << gMin << ", " << bMin << "], "; os << "maxrgb=["; - os << rMax << " " << gMax << " " << bMax << "]"; + os << rMax << ", " << gMax << ", " << bMax << "]"; } os << ">"; diff --git a/src/OpenColorIO/transforms/Lut3DTransform.cpp b/src/OpenColorIO/transforms/Lut3DTransform.cpp index 3d6630d93a..6e1a2babeb 100644 --- a/src/OpenColorIO/transforms/Lut3DTransform.cpp +++ b/src/OpenColorIO/transforms/Lut3DTransform.cpp @@ -208,9 +208,9 @@ std::ostream & operator<< (std::ostream & os, const Lut3DTransform & t) } } os << "minrgb=["; - os << rMin << " " << gMin << " " << bMin << "], "; + os << rMin << ", " << gMin << ", " << bMin << "], "; os << "maxrgb=["; - os << rMax << " " << gMax << " " << bMax << "]"; + os << rMax << ", " << gMax << ", " << bMax << "]"; } os << ">"; diff --git a/src/OpenColorIO/transforms/MatrixTransform.cpp b/src/OpenColorIO/transforms/MatrixTransform.cpp index d9991bd4e3..ff73b918a0 100755 --- a/src/OpenColorIO/transforms/MatrixTransform.cpp +++ b/src/OpenColorIO/transforms/MatrixTransform.cpp @@ -351,17 +351,17 @@ std::ostream& operator<< (std::ostream& os, const MatrixTransform& t) noexcept os << "direction=" << TransformDirectionToString(t.getDirection()); os << ", fileindepth=" << BitDepthToString(t.getFileInputBitDepth()); os << ", fileoutdepth=" << BitDepthToString(t.getFileOutputBitDepth()); - os << ", matrix=" << matrix[0]; + os << ", matrix=[" << matrix[0]; for (int i = 1; i < 16; ++i) { - os << " " << matrix[i]; + os << ", " << matrix[i]; } - os << ", offset=" << offset[0]; + os << "], offset=[" << offset[0]; for (int i = 1; i < 4; ++i) { - os << " " << offset[i]; + os << ", " << offset[i]; } - os << ">"; + os << "]>"; return os; } From 5f223f316f8e9a54ab3445fdfd2f91e96e0514b9 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Fri, 11 Oct 2024 20:33:44 -0400 Subject: [PATCH 19/30] Fix unit tests Signed-off-by: Doug Walker (cherry picked from commit a9e51c86de01315721025418808c78d9fa8265ff) Signed-off-by: Doug Walker --- src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h | 4 ++-- tests/cpu/NamedTransform_tests.cpp | 4 ++-- tests/cpu/transforms/Lut1DTransform_tests.cpp | 2 +- tests/cpu/transforms/Lut3DTransform_tests.cpp | 2 +- tests/python/LegacyViewingPipelineTest.py | 2 +- tests/python/LogCameraTransformTest.py | 8 ++++---- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h index b99150e695..1db6aa76dc 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h @@ -61,13 +61,13 @@ class FixedFunctionOpData : public OpData ACES_TONESCALE_COMPRESS_20_FWD, // ACES2 Tonescale and chroma compression ACES_TONESCALE_COMPRESS_20_INV, // ACES2 Tonescale and chroma compression (inv) ACES_GAMUT_COMPRESS_20_FWD, // ACES2 Gamut compression - ACES_GAMUT_COMPRESS_20_INV // ACES2 Gamut compression (inv) + ACES_GAMUT_COMPRESS_20_INV, // ACES2 Gamut compression (inv) RGB_TO_HSY_LOG, // RGB to HSY (Hue, Saturation, Lightness) using log RGB_TO_HSY_LIN, // RGB to HSY (Hue, Saturation, Lightness) using linear RGB_TO_HSY_VID, // RGB to HSY (Hue, Saturation, Lightness) using video HSY_LOG_TO_RGB, // HSY (Hue, Saturation, Lightness) using log to RGB HSY_LIN_TO_RGB, // HSY (Hue, Saturation, Lightness) using linear to RGB - HSY_VID_TO_RGB, // HSY (Hue, Saturation, Lightness) using video to RGB + HSY_VID_TO_RGB // HSY (Hue, Saturation, Lightness) using video to RGB }; static const char * ConvertStyleToString(Style style, bool detailed); diff --git a/tests/cpu/NamedTransform_tests.cpp b/tests/cpu/NamedTransform_tests.cpp index 2d2b5e810d..5a05cae41d 100644 --- a/tests/cpu/NamedTransform_tests.cpp +++ b/tests/cpu/NamedTransform_tests.cpp @@ -40,8 +40,8 @@ OCIO_ADD_TEST(NamedTransform, basic) OCIO_CHECK_NO_THROW(oss << *namedTransform); OCIO_CHECK_EQUAL(oss.str(), ">"); + "fileoutdepth=unknown, matrix=[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], " + "offset=[0, 0, 0, 0]>>"); // Test faulty cases. diff --git a/tests/cpu/transforms/Lut1DTransform_tests.cpp b/tests/cpu/transforms/Lut1DTransform_tests.cpp index 6c8928cd50..f31e9dc0fc 100644 --- a/tests/cpu/transforms/Lut1DTransform_tests.cpp +++ b/tests/cpu/transforms/Lut1DTransform_tests.cpp @@ -99,7 +99,7 @@ OCIO_ADD_TEST(Lut1DTransform, basic) oss << *lut; OCIO_CHECK_EQUAL(oss.str(), ""); + " length=3, minrgb=[-0.2, 0.1, -0.3], maxrgb=[1.2, 1.3, 0.8]>"); auto lut2 = OCIO_DYNAMIC_POINTER_CAST(lut->createEditableCopy()); std::ostringstream oss2; diff --git a/tests/cpu/transforms/Lut3DTransform_tests.cpp b/tests/cpu/transforms/Lut3DTransform_tests.cpp index ad2c9834ff..db3296e7d2 100644 --- a/tests/cpu/transforms/Lut3DTransform_tests.cpp +++ b/tests/cpu/transforms/Lut3DTransform_tests.cpp @@ -99,7 +99,7 @@ OCIO_ADD_TEST(Lut3DTransform, basic) std::ostringstream oss; oss << *lut; OCIO_CHECK_EQUAL(oss.str(), ""); + " interpolation=default, gridSize=3, minrgb=[-0.2, -0.1, -0.3], maxrgb=[1.2, 1.3, 1.8]>"); } OCIO_ADD_TEST(Lut3DTransform, create_with_parameters) diff --git a/tests/python/LegacyViewingPipelineTest.py b/tests/python/LegacyViewingPipelineTest.py index 8b4e3ea5e3..a4f9c025a2 100644 --- a/tests/python/LegacyViewingPipelineTest.py +++ b/tests/python/LegacyViewingPipelineTest.py @@ -197,7 +197,7 @@ def test_get_processor_errors(self): self.assertEqual(str(pipeline), ('DisplayViewTransform: , ' 'LinearCC: ')) + 'fileoutdepth=unknown, matrix=[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], offset=[0.1, 0.2, 0.3, 0]>')) def test_get_processor(self): """ diff --git a/tests/python/LogCameraTransformTest.py b/tests/python/LogCameraTransformTest.py index 5ef890d06e..21aebb0868 100644 --- a/tests/python/LogCameraTransformTest.py +++ b/tests/python/LogCameraTransformTest.py @@ -72,10 +72,10 @@ def test_constructor(self): self.assertEqual(lct.getDirection(), OCIO.TRANSFORM_DIR_INVERSE) self.assertEqual(str(lct), '') + 'base=2.5, logSideSlope=[1.1, 1.2, 1.3], ' + 'logSideOffset=[0.01, 0.02, 0.03], linSideSlope=[1.3, 1.2, 1.1], ' + 'linSideOffset=[0.02, 0.03, 0.01], linSideBreak=[0.1, 0.2, 0.3], ' + 'linearSlope=[0.9, 0.8, 0.7]>') LIN_SB = [0.15, 0.2, 0.3] BASE = 10 From ad92002c3cf006afc8240a23009db25e5c1d305f Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Fri, 25 Oct 2024 22:59:55 -0400 Subject: [PATCH 20/30] Fix hue curve unit tests Signed-off-by: Doug Walker (cherry picked from commit 6c233c8f19c12b711bbec705d7a90e3c84c2fb70) Signed-off-by: Doug Walker --- .../ops/fixedfunction/FixedFunctionOpCPU.cpp | 4 ++-- tests/osl/FixedFunctionOp_test.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp index d547002366..efa19dfa9d 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp @@ -1599,7 +1599,7 @@ void applyHSYToRGB(const void * inImg, void * outImg, long numPixels, float min0 const float * in = (const float *)inImg; float * out = (float *)outImg; - for(unsigned idx=0; idxm_threshold = 1e-6f; } -OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_fwd) +OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LIN_fwd) { OCIO::FixedFunctionTransformRcPtr func = OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); @@ -414,7 +414,7 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_fwd) m_data->m_relativeComparison = true; } -OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_inv) +OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LIN_inv) { OCIO::FixedFunctionTransformRcPtr func = OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); @@ -426,7 +426,7 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_inv) m_data->m_relativeComparison = true; } -OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_fwd) +OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LOG_fwd) { OCIO::FixedFunctionTransformRcPtr func = OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); @@ -438,7 +438,7 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_fwd) m_data->m_relativeComparison = true; } -OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_inv) +OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LOG_inv) { OCIO::FixedFunctionTransformRcPtr func = OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); @@ -450,7 +450,7 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_inv) m_data->m_relativeComparison = true; } -OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_fwd) +OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_VID_fwd) { OCIO::FixedFunctionTransformRcPtr func = OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); @@ -462,7 +462,7 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_fwd) m_data->m_relativeComparison = true; } -OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_inv) +OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_VID_inv) { OCIO::FixedFunctionTransformRcPtr func = OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); From c71da0b405bb90aa0adca1c9107fff1c01234de5 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Sat, 26 Oct 2024 17:23:35 -0400 Subject: [PATCH 21/30] Comment out OSL tests Signed-off-by: Doug Walker (cherry picked from commit cf7744e245cfcaba20fe30366819bafe889f2bec) Signed-off-by: Doug Walker --- tests/osl/FixedFunctionOp_test.cpp | 142 ++++++++++++++--------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/tests/osl/FixedFunctionOp_test.cpp b/tests/osl/FixedFunctionOp_test.cpp index d22cfd21a2..051c64b774 100644 --- a/tests/osl/FixedFunctionOp_test.cpp +++ b/tests/osl/FixedFunctionOp_test.cpp @@ -402,77 +402,77 @@ OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSV_inv_custom) m_data->m_threshold = 1e-6f; } -OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LIN_fwd) -{ - OCIO::FixedFunctionTransformRcPtr func = - OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); - func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); - - m_data->m_transform = func; - - m_data->m_threshold = 1e-6f; - m_data->m_relativeComparison = true; -} - -OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LIN_inv) -{ - OCIO::FixedFunctionTransformRcPtr func = - OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); - func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); - - m_data->m_transform = func; - - m_data->m_threshold = 1e-6f; - m_data->m_relativeComparison = true; -} - -OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LOG_fwd) -{ - OCIO::FixedFunctionTransformRcPtr func = - OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); - func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); - - m_data->m_transform = func; - - m_data->m_threshold = 1e-6f; - m_data->m_relativeComparison = true; -} - -OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LOG_inv) -{ - OCIO::FixedFunctionTransformRcPtr func = - OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); - func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); - - m_data->m_transform = func; - - m_data->m_threshold = 1e-6f; - m_data->m_relativeComparison = true; -} - -OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_VID_fwd) -{ - OCIO::FixedFunctionTransformRcPtr func = - OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); - func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); - - m_data->m_transform = func; - - m_data->m_threshold = 1e-6f; - m_data->m_relativeComparison = true; -} - -OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_VID_inv) -{ - OCIO::FixedFunctionTransformRcPtr func = - OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); - func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); - - m_data->m_transform = func; - - m_data->m_threshold = 1e-6f; - m_data->m_relativeComparison = true; -} +// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LIN_fwd) +// { +// OCIO::FixedFunctionTransformRcPtr func = +// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); +// func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); +// +// m_data->m_transform = func; +// +// m_data->m_threshold = 1e-6f; +// m_data->m_relativeComparison = true; +// } +// +// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LIN_inv) +// { +// OCIO::FixedFunctionTransformRcPtr func = +// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); +// func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); +// +// m_data->m_transform = func; +// +// m_data->m_threshold = 1e-6f; +// m_data->m_relativeComparison = true; +// } +// +// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LOG_fwd) +// { +// OCIO::FixedFunctionTransformRcPtr func = +// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); +// func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); +// +// m_data->m_transform = func; +// +// m_data->m_threshold = 1e-6f; +// m_data->m_relativeComparison = true; +// } +// +// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LOG_inv) +// { +// OCIO::FixedFunctionTransformRcPtr func = +// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); +// func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); +// +// m_data->m_transform = func; +// +// m_data->m_threshold = 1e-6f; +// m_data->m_relativeComparison = true; +// } +// +// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_VID_fwd) +// { +// OCIO::FixedFunctionTransformRcPtr func = +// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); +// func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); +// +// m_data->m_transform = func; +// +// m_data->m_threshold = 1e-6f; +// m_data->m_relativeComparison = true; +// } +// +// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_VID_inv) +// { +// OCIO::FixedFunctionTransformRcPtr func = +// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); +// func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); +// +// m_data->m_transform = func; +// +// m_data->m_threshold = 1e-6f; +// m_data->m_relativeComparison = true; +// } OCIO_OSL_TEST(FixedFunction, style_XYZ_TO_xyY_fwd) { From a915b2d74031fb57f11476929bbc1cf80de3a714 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Fri, 25 Jul 2025 20:31:53 -0400 Subject: [PATCH 22/30] Finish the implementation Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTransforms.h | 36 +- include/OpenColorIO/OpenColorTypes.h | 2 +- src/OpenColorIO/CMakeLists.txt | 1 + src/OpenColorIO/Config.cpp | 23 +- src/OpenColorIO/DynamicProperty.cpp | 12 +- src/OpenColorIO/DynamicProperty.h | 2 +- src/OpenColorIO/OCIOYaml.cpp | 159 +++++ src/OpenColorIO/Op.cpp | 2 + src/OpenColorIO/ParseUtils.cpp | 3 + src/OpenColorIO/fileformats/FileFormatCTF.cpp | 30 +- .../fileformats/ctf/CTFReaderHelper.cpp | 183 +++++- .../fileformats/ctf/CTFReaderHelper.h | 30 + .../fileformats/ctf/CTFTransform.cpp | 15 +- .../fileformats/ctf/CTFTransform.h | 5 +- .../ops/fixedfunction/FixedFunctionOpCPU.cpp | 6 +- .../ops/fixedfunction/FixedFunctionOpGPU.cpp | 2 + .../ops/gradinghuecurve/GradingHueCurve.cpp | 70 ++- .../ops/gradinghuecurve/GradingHueCurve.h | 17 +- .../ops/gradinghuecurve/GradingHueCurveOp.cpp | 9 +- .../gradinghuecurve/GradingHueCurveOpCPU.cpp | 335 +++++++++++ .../gradinghuecurve/GradingHueCurveOpCPU.h | 21 + .../gradinghuecurve/GradingHueCurveOpData.h | 2 +- .../gradinghuecurve/GradingHueCurveOpGPU.cpp | 313 +++++++--- .../gradinghuecurve/GradingHueCurveOpGPU.h | 4 +- .../gradingrgbcurve/GradingBSplineCurve.cpp | 522 +++++++++++----- .../ops/gradingrgbcurve/GradingBSplineCurve.h | 31 +- .../gradingrgbcurve/GradingRGBCurveOpGPU.cpp | 15 +- src/bindings/python/CMakeLists.txt | 1 + src/bindings/python/PyDynamicProperty.cpp | 4 + src/bindings/python/PyDynamicProperty.h | 20 + src/bindings/python/PyGradingData.cpp | 202 ++++++- src/bindings/python/PyOpenColorIO.h | 8 +- src/bindings/python/PyTransform.cpp | 1 + src/bindings/python/PyTransform.h | 1 + src/bindings/python/PyTypes.cpp | 60 ++ .../transforms/PyGradingHueCurveTransform.cpp | 119 ++++ tests/cpu/CMakeLists.txt | 1 + tests/cpu/Config_tests.cpp | 144 ++++- tests/cpu/DynamicProperty_tests.cpp | 157 ++++- tests/cpu/fileformats/FileFormatCTF_tests.cpp | 301 +++++++++- .../FixedFunctionOpCPU_tests.cpp | 25 +- .../GradingHueCurveOpCPU_tests.cpp | 318 ++++++++++ .../GradingHueCurveOpData_tests.cpp | 88 ++- .../GradingHueCurveOp_tests.cpp | 235 ++++---- .../gradinghuecurve/GradingHueCurve_tests.cpp | 258 ++++---- .../GradingBSplineCurve_tests.cpp | 33 +- .../GradingRGBCurveOpData_tests.cpp | 49 +- .../gradingrgbcurve/GradingRGBCurve_tests.cpp | 1 - .../GradingHueCurveTransform_tests.cpp | 557 +++++++++--------- .../GradingRGBCurveTransform_tests.cpp | 6 +- tests/data/files/grading_hue_curve.ctf | 47 ++ tests/gpu/FixedFunctionOp_test.cpp | 104 ++-- tests/gpu/GradingHueCurveOp_test.cpp | 229 ++++--- tests/osl/GradingHueCurveOp_test.cpp | 177 ------ tests/python/GradingDataTest.py | 113 +++- tests/python/GradingHueCurveTransformTest.py | 171 ++++++ tests/python/OpenColorIOTestSuite.py | 92 +-- tests/python/UnitTestUtils.py | 30 + 58 files changed, 4116 insertions(+), 1286 deletions(-) create mode 100644 src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp create mode 100644 src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.h create mode 100644 src/bindings/python/transforms/PyGradingHueCurveTransform.cpp create mode 100644 tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp create mode 100644 tests/data/files/grading_hue_curve.ctf delete mode 100644 tests/osl/GradingHueCurveOp_test.cpp create mode 100644 tests/python/GradingHueCurveTransformTest.py diff --git a/include/OpenColorIO/OpenColorTransforms.h b/include/OpenColorIO/OpenColorTransforms.h index 8c28e9f26e..77f6d24bd2 100644 --- a/include/OpenColorIO/OpenColorTransforms.h +++ b/include/OpenColorIO/OpenColorTransforms.h @@ -513,12 +513,15 @@ class OCIOEXPORT GradingBSplineCurve public: /// Create a BSpline curve with a specified number of control points. static GradingBSplineCurveRcPtr Create(size_t size); - static GradingBSplineCurveRcPtr Create(size_t size, BSplineCurveType curveType); + static GradingBSplineCurveRcPtr Create(size_t size, BSplineType splineType); + static GradingBSplineCurveRcPtr Create(size_t size, HueCurveType curveType); /// Create a BSpline curve with a list of control points. static GradingBSplineCurveRcPtr Create(std::initializer_list values); - static GradingBSplineCurveRcPtr Create(std::initializer_list values, BSplineCurveType curveType); + static GradingBSplineCurveRcPtr Create(std::initializer_list values, BSplineType splineType); + static GradingBSplineCurveRcPtr Create(std::initializer_list values, HueCurveType curveType); virtual GradingBSplineCurveRcPtr createEditableCopy() const = 0; + /// Get the number of ControlPoint objects (and the number of slopes). virtual size_t getNumControlPoints() const noexcept = 0; virtual void setNumControlPoints(size_t size) = 0; virtual const GradingControlPoint & getControlPoint(size_t index) const = 0; @@ -527,8 +530,8 @@ class OCIOEXPORT GradingBSplineCurve virtual void setSlope(size_t index, float slope) = 0; virtual bool slopesAreDefault() const = 0; virtual void validate() const = 0; - virtual BSplineCurveType getCurveType() const = 0; - virtual void setCurveType(BSplineCurveType curveType) = 0; + virtual BSplineType getSplineType() const = 0; + virtual void setSplineType(BSplineType splineType) = 0; GradingBSplineCurve(const GradingBSplineCurve &) = delete; GradingBSplineCurve & operator= (const GradingBSplineCurve &) = delete; @@ -551,6 +554,8 @@ extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingBSpline class OCIOEXPORT GradingRGBCurve { public: + /// Create a GradingRGBCurve. (The style argument is not part of the object, it is simply + /// used to initialize the proper default curves.) static GradingRGBCurveRcPtr Create(GradingStyle style); static GradingRGBCurveRcPtr Create(const ConstGradingRGBCurveRcPtr & rhs); static GradingRGBCurveRcPtr Create(const ConstGradingBSplineCurveRcPtr & red, @@ -582,6 +587,8 @@ extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingRGBCurv class OCIOEXPORT GradingHueCurve { public: + /// Create a GradingHueCurve. (The style argument is not part of the object, it is simply + /// used to initialize the proper default curves.) static GradingHueCurveRcPtr Create(GradingStyle style); static GradingHueCurveRcPtr Create(const ConstGradingHueCurveRcPtr & rhs); static GradingHueCurveRcPtr Create( @@ -594,6 +601,8 @@ class OCIOEXPORT GradingHueCurve ConstGradingBSplineCurveRcPtr satLum, ConstGradingBSplineCurveRcPtr hueFx); + static BSplineType GetBSplineTypeForHueCurveType(HueCurveType curveType); + virtual GradingHueCurveRcPtr createEditableCopy() const = 0; virtual void validate() const = 0; virtual bool isIdentity() const = 0; @@ -1261,7 +1270,24 @@ extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingPrimary /** * Hue color correction controls. * - * TODO: Description. + * RGB curve color correction controls. + * + + +UPDATE + + * This transform allows for modifying tone reproduction via B-spline curves. + * + * There is an R, G, and B curve along with a Master curve (that applies to R, G, and B). Each + * curve is specified via the x and y coordinates of its control points. A monotonic spline is + * fit to the control points. The x coordinates must be non-decreasing. When the grading style + * is linear, the units for the control points are photographic stops relative to 0.18. + * + * The control points are dynamic, so they may be adjusted even after the Transform is included + * in a Processor. + + + */ class OCIOEXPORT GradingHueCurveTransform : public Transform { diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 832a5acafd..046f997425 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -593,7 +593,7 @@ enum HueCurveType HUE_NUM_CURVES }; -enum BSplineCurveType +enum BSplineType { B_SPLINE = 0, //!< Monotonic quadratic B-spline used for the RGBM curves. DIAGONAL_B_SPLINE, //!< Monotonic quadratic B-spline for the sat-sat and lum-lum curves. diff --git a/src/OpenColorIO/CMakeLists.txt b/src/OpenColorIO/CMakeLists.txt index db813b5769..79f06a8ee5 100755 --- a/src/OpenColorIO/CMakeLists.txt +++ b/src/OpenColorIO/CMakeLists.txt @@ -95,6 +95,7 @@ set(SOURCES ops/gamma/GammaOpUtils.cpp ops/gamma/GammaOp.cpp ops/gradinghuecurve/GradingHueCurve.cpp + ops/gradinghuecurve/GradingHueCurveOpCPU.cpp ops/gradinghuecurve/GradingHueCurveOpData.cpp ops/gradinghuecurve/GradingHueCurveOpGPU.cpp ops/gradinghuecurve/GradingHueCurveOp.cpp diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index 727d434e9f..829d17f681 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -247,7 +247,7 @@ static constexpr unsigned LastSupportedMajorVersion = OCIO_VERSION_MAJOR; // For each major version keep the most recent minor. static const unsigned int LastSupportedMinorVersion[] = {0, // Version 1 - 4 // Version 2 + 5 // Version 2 }; } // namespace @@ -5529,6 +5529,19 @@ void Config::Impl::checkVersionConsistency(ConstTransformRcPtr & transform) cons throw Exception(ss.str().c_str()); } } + + if (m_majorVersion == 2 && m_minorVersion < 5 ) + { + if( ffstyle == FIXED_FUNCTION_RGB_TO_HSY_LIN || + ffstyle == FIXED_FUNCTION_RGB_TO_HSY_LOG || + ffstyle == FIXED_FUNCTION_RGB_TO_HSY_VID ) + { + std::ostringstream ss; + ss << "Only config version 2.5 (or higher) can have FixedFunctionTransform style '" + << FixedFunctionStyleToString(ffstyle) << "'."; + throw Exception(ss.str().c_str()); + } + } } else if (DynamicPtrCast(transform)) { @@ -5546,6 +5559,14 @@ void Config::Impl::checkVersionConsistency(ConstTransformRcPtr & transform) cons "GradingRGBCurveTransform."); } } + else if (DynamicPtrCast(transform)) + { + if (m_majorVersion == 2 && m_minorVersion < 5 ) + { + throw Exception("Only config version 2.5 (or higher) can have " + "GradingHueCurveTransform."); + } + } else if (DynamicPtrCast(transform)) { if (m_majorVersion < 2) diff --git a/src/OpenColorIO/DynamicProperty.cpp b/src/OpenColorIO/DynamicProperty.cpp index 6f798ab42e..3fe51f7562 100644 --- a/src/OpenColorIO/DynamicProperty.cpp +++ b/src/OpenColorIO/DynamicProperty.cpp @@ -293,21 +293,21 @@ DynamicPropertyGradingHueCurveImpl::DynamicPropertyGradingHueCurveImpl( const ConstGradingHueCurveRcPtr & value, bool dynamic) : DynamicPropertyImpl(DYNAMIC_PROPERTY_GRADING_HUECURVE, dynamic) { - m_hueCurve = GradingHueCurve::Create(value); + m_gradingHueCurve = GradingHueCurve::Create(value); // Convert control points from the UI into knots and coefficients for the apply. precompute(); } const ConstGradingHueCurveRcPtr & DynamicPropertyGradingHueCurveImpl::getValue() const { - return m_hueCurve; + return m_gradingHueCurve; } void DynamicPropertyGradingHueCurveImpl::setValue(const ConstGradingHueCurveRcPtr & value) { value->validate(); - m_hueCurve = value->createEditableCopy(); + m_gradingHueCurve = value->createEditableCopy(); // Convert control points from the UI into knots and coefficients for the apply. precompute(); } @@ -319,12 +319,12 @@ bool DynamicPropertyGradingHueCurveImpl::getLocalBypass() const int DynamicPropertyGradingHueCurveImpl::getNumKnots() const { - return static_cast(m_knotsCoefs.m_knotsArray.size()); + return static_cast(m_knotsCoefs.m_numKnots); } int DynamicPropertyGradingHueCurveImpl::getNumCoefs() const { - return static_cast(m_knotsCoefs.m_coefsArray.size()); + return static_cast(m_knotsCoefs.m_numCoefs); } const int * DynamicPropertyGradingHueCurveImpl::getKnotsOffsetsArray() const @@ -368,7 +368,7 @@ void DynamicPropertyGradingHueCurveImpl::precompute() // curve data. for (const auto c : { HUE_HUE, HUE_SAT, HUE_LUM, LUM_SAT, SAT_SAT, LUM_LUM, SAT_LUM, HUE_FX }) { - ConstGradingBSplineCurveRcPtr curve = m_hueCurve->getCurve(c); + ConstGradingBSplineCurveRcPtr curve = m_gradingHueCurve->getCurve(c); auto curveImpl = dynamic_cast(curve.get()); curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c)); } diff --git a/src/OpenColorIO/DynamicProperty.h b/src/OpenColorIO/DynamicProperty.h index 3a03e6a0a6..b77cc085f0 100644 --- a/src/OpenColorIO/DynamicProperty.h +++ b/src/OpenColorIO/DynamicProperty.h @@ -205,7 +205,7 @@ class DynamicPropertyGradingHueCurveImpl : public DynamicPropertyImpl, private: void precompute(); - ConstGradingHueCurveRcPtr m_hueCurve; + ConstGradingHueCurveRcPtr m_gradingHueCurve; // Holds curve data as knots and coefs. There are 8 curves. GradingBSplineCurveImpl::KnotsCoefs m_knotsCoefs{ 8 }; diff --git a/src/OpenColorIO/OCIOYaml.cpp b/src/OpenColorIO/OCIOYaml.cpp index b1cee18982..f57c36a7e3 100644 --- a/src/OpenColorIO/OCIOYaml.cpp +++ b/src/OpenColorIO/OCIOYaml.cpp @@ -16,6 +16,7 @@ #include "ops/exposurecontrast/ExposureContrastOpData.h" #include "ops/gradingprimary/GradingPrimaryOpData.h" #include "ops/gradingrgbcurve/GradingRGBCurve.h" +#include "ops/gradinghuecurve/GradingHueCurve.h" #include "ops/gradingtone/GradingToneOpData.h" #include "ops/log/LogUtils.h" #include "ParseUtils.h" @@ -2058,6 +2059,155 @@ inline void save(YAML::Emitter & out, ConstGradingRGBCurveTransformRcPtr t) out << YAML::EndMap; } +// GradingHueCurveTransform + +inline void load(const YAML::Node & node, GradingHueCurveTransformRcPtr & t) +{ + CheckDuplicates(node); + + t = GradingHueCurveTransform::Create(GRADING_LOG); + + GradingBSplineCurveRcPtr hh; + GradingBSplineCurveRcPtr hs; + GradingBSplineCurveRcPtr hl; + GradingBSplineCurveRcPtr ls; + GradingBSplineCurveRcPtr ss; + GradingBSplineCurveRcPtr ll; + GradingBSplineCurveRcPtr sl; + GradingBSplineCurveRcPtr hfx; + + for (Iterator iter = node.begin(); iter != node.end(); ++iter) + { + const std::string & key = iter->first.as(); + + if (iter->second.IsNull() || !iter->second.IsDefined()) continue; + + if (key == "style") + { + std::string style; + load(iter->second, style); + t->setStyle(GradingStyleFromString(style.c_str())); + } + else if (key == "direction") + { + TransformDirection val; + load(iter->second, val); + t->setDirection(val); + } + else if (key == "hue_hue") + { + hh = GradingBSplineCurve::Create(0, HUE_HUE); + load(iter->first, iter->second, hh); + } + else if (key == "hue_sat") + { + hs = GradingBSplineCurve::Create(0, HUE_SAT); + load(iter->first, iter->second, hs); + } + else if (key == "hue_lum") + { + hl = GradingBSplineCurve::Create(0, HUE_LUM); + load(iter->first, iter->second, hl); + } + else if (key == "lum_sat") + { + ls = GradingBSplineCurve::Create(0, LUM_SAT); + load(iter->first, iter->second, ls); + } + else if (key == "sat_sat") + { + ss = GradingBSplineCurve::Create(0, SAT_SAT); + load(iter->first, iter->second, ss); + } + else if (key == "lum_lum") + { + ll = GradingBSplineCurve::Create(0, LUM_LUM); + load(iter->first, iter->second, ll); + } + else if (key == "sat_lum") + { + sl = GradingBSplineCurve::Create(0, SAT_LUM); + load(iter->first, iter->second, sl); + } + else if (key == "hue_fx") + { + hfx = GradingBSplineCurve::Create(0, HUE_FX); + load(iter->first, iter->second, hfx); + } + else if (key == "name") + { + std::string name; + load(iter->second, name); + t->getFormatMetadata().setName(name.c_str()); + } + else + { + LogUnknownKeyWarning(node.Tag(), iter->first); + } + } + +// auto & defCurve = t->getStyle() == GRADING_LIN ? GradingHueCurveImpl::DefaultCurvesLin : +// GradingHueCurveImpl::DefaultCurves; +// + if (!hh) hh = GradingHueCurveImpl::DefaultHueHue.createEditableCopy(); + if (!hs) hs = GradingHueCurveImpl::DefaultHueSat.createEditableCopy(); + if (!hl) hl = GradingHueCurveImpl::DefaultHueLum.createEditableCopy(); + if (!ls) ls = t->getStyle() == GRADING_LIN ? + GradingHueCurveImpl::DefaultLumSatLin.createEditableCopy() : + GradingHueCurveImpl::DefaultLumSat.createEditableCopy(); + if (!ss) ss = GradingHueCurveImpl::DefaultSatSat.createEditableCopy(); + if (!ll) ll = t->getStyle() == GRADING_LIN ? + GradingHueCurveImpl::DefaultLumLumLin.createEditableCopy() : + GradingHueCurveImpl::DefaultLumLum.createEditableCopy(); + if (!sl) sl = GradingHueCurveImpl::DefaultSatLum.createEditableCopy(); + if (!hfx) hfx = GradingHueCurveImpl::DefaultHueFx.createEditableCopy(); + + auto curves = GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); + + t->setValue(curves); +} + +inline void save(YAML::Emitter & out, ConstGradingHueCurveTransformRcPtr t) +{ + const auto & vals = t->getValue(); + auto & defCurves = t->getStyle() == GRADING_LIN ? GradingHueCurveImpl::DefaultCurvesLin : + GradingHueCurveImpl::DefaultCurves; + bool useLineBreaks = false; + for (int c = 0; c < HUE_NUM_CURVES; ++c) + { + const auto & curve = vals->getCurve(static_cast(c)); + if (*curve != defCurves[c]) + { + useLineBreaks = true; + break; + } + } + + out << YAML::VerbatimTag("GradingHueCurveTransform"); + if (!useLineBreaks) out << YAML::Flow; + out << YAML::BeginMap; + + EmitTransformName(out, t->getFormatMetadata()); + + const auto style = t->getStyle(); + out << YAML::Key << "style"; + out << YAML::Value << YAML::Flow << GradingStyleToString(style); + + static const std::vector curveNames = { "hue_hue", "hue_sat", "hue_lum", + "lum_sat", "sat_sat", "lum_lum", "sat_lum", "hue_fx" }; + for (int c = 0; c < HUE_NUM_CURVES; ++c) + { + const auto & curve = vals->getCurve(static_cast(c)); + if ((*curve != defCurves[c]) || !(curve->slopesAreDefault())) + { + save(out, curveNames[c], curve); + } + } + + EmitBaseTransformKeyValues(out, t); + out << YAML::EndMap; +} + // GradingToneTransform inline void load(const YAML::Node & parent, const YAML::Node & node, GradingRGBMSW & rgbm, @@ -3031,6 +3181,12 @@ void load(const YAML::Node& node, TransformRcPtr& t) load(node, temp); t = temp; } + else if (type == "GradingHueCurveTransform") + { + GradingHueCurveTransformRcPtr temp; + load(node, temp); + t = temp; + } else if (type == "GradingToneTransform") { GradingToneTransformRcPtr temp; @@ -3135,6 +3291,9 @@ void save(YAML::Emitter& out, ConstTransformRcPtr t, unsigned int majorVersion) else if (ConstGradingRGBCurveTransformRcPtr GC_tran = \ DynamicPtrCast(t)) save(out, GC_tran); + else if (ConstGradingHueCurveTransformRcPtr GC_tran = \ + DynamicPtrCast(t)) + save(out, GC_tran); else if (ConstGradingToneTransformRcPtr GT_tran = \ DynamicPtrCast(t)) save(out, GT_tran); diff --git a/src/OpenColorIO/Op.cpp b/src/OpenColorIO/Op.cpp index b9d5ac9440..7e95baecfc 100755 --- a/src/OpenColorIO/Op.cpp +++ b/src/OpenColorIO/Op.cpp @@ -429,6 +429,7 @@ void OpRcPtrVec::validateDynamicProperties() DynamicPropertyDoubleImplRcPtr dpGamma; DynamicPropertyGradingPrimaryImplRcPtr dpGradingPrimary; DynamicPropertyGradingRGBCurveImplRcPtr dpGradingRGBCurve; + DynamicPropertyGradingHueCurveImplRcPtr dpGradingHueCurve; DynamicPropertyGradingToneImplRcPtr dpGradingTone; for (auto op : m_ops) @@ -439,6 +440,7 @@ void OpRcPtrVec::validateDynamicProperties() ValidateDynamicProperty(op, dpGamma, DYNAMIC_PROPERTY_GAMMA); ValidateDynamicProperty(op, dpGradingPrimary, DYNAMIC_PROPERTY_GRADING_PRIMARY); ValidateDynamicProperty(op, dpGradingRGBCurve, DYNAMIC_PROPERTY_GRADING_RGBCURVE); + ValidateDynamicProperty(op, dpGradingHueCurve, DYNAMIC_PROPERTY_GRADING_HUECURVE); ValidateDynamicProperty(op, dpGradingTone, DYNAMIC_PROPERTY_GRADING_TONE); } } diff --git a/src/OpenColorIO/ParseUtils.cpp b/src/OpenColorIO/ParseUtils.cpp index 755644c328..df43deccf1 100644 --- a/src/OpenColorIO/ParseUtils.cpp +++ b/src/OpenColorIO/ParseUtils.cpp @@ -408,6 +408,9 @@ FixedFunctionStyle FixedFunctionStyleFromString(const char * style) else if(str == "lin_to_pq") return FIXED_FUNCTION_LIN_TO_PQ; else if(str == "lin_to_gammalog") return FIXED_FUNCTION_LIN_TO_GAMMA_LOG; else if(str == "lin_to_doublelog") return FIXED_FUNCTION_LIN_TO_DOUBLE_LOG; + else if(str == "rgb_to_hsy_lin") return FIXED_FUNCTION_RGB_TO_HSY_LIN; + else if(str == "rgb_to_hsy_log") return FIXED_FUNCTION_RGB_TO_HSY_LOG; + else if(str == "rgb_to_hsy_vid") return FIXED_FUNCTION_RGB_TO_HSY_VID; // Default style is meaningless. std::stringstream ss; diff --git a/src/OpenColorIO/fileformats/FileFormatCTF.cpp b/src/OpenColorIO/fileformats/FileFormatCTF.cpp index c9ada57cda..b1f0393038 100644 --- a/src/OpenColorIO/fileformats/FileFormatCTF.cpp +++ b/src/OpenColorIO/fileformats/FileFormatCTF.cpp @@ -465,6 +465,17 @@ class XMLParserHelper TAG_RGB_CURVE_RED }; + static const std::vector gradingHueCurveSubElements = { + TAG_HUE_CURVE_HUE_HUE, + TAG_HUE_CURVE_HUE_SAT, + TAG_HUE_CURVE_HUE_LUM, + TAG_HUE_CURVE_LUM_SAT, + TAG_HUE_CURVE_SAT_SAT, + TAG_HUE_CURVE_LUM_LUM, + TAG_HUE_CURVE_SAT_LUM, + TAG_HUE_CURVE_HUE_FX, + }; + XMLParserHelper * pImpl = (XMLParserHelper*)userData; if (!pImpl || !name || !*name) @@ -539,7 +550,7 @@ class XMLParserHelper } // Safety check to try and ensure that all new elements will get handled here. - static_assert(CTFReaderOpElt::NoType == 17, "Need to handle new type here"); + static_assert(CTFReaderOpElt::NoType == 18, "Need to handle new type here"); // Will allow to give better error feedback to the user if the // element name is not handled. If any case recognizes the name, @@ -586,6 +597,11 @@ class XMLParserHelper { pImpl->AddOpReader(CTFReaderOpElt::GradingRGBCurveType, name); } + else if (SupportedElement(name, pElt, TAG_HUE_CURVE, + TAG_PROCESS_LIST, recognizedName)) + { + pImpl->AddOpReader(CTFReaderOpElt::GradingHueCurveType, name); + } else if (SupportedElement(name, pElt, TAG_TONE, TAG_PROCESS_LIST, recognizedName)) { pImpl->AddOpReader(CTFReaderOpElt::GradingToneType, name); @@ -897,7 +913,9 @@ class XMLParserHelper pImpl->getXmlFilename())); } else if (SupportedElement(name, pElt, gradingRGBCurveSubElements, - TAG_RGB_CURVE, recognizedName)) + TAG_RGB_CURVE, recognizedName) || + SupportedElement(name, pElt, gradingHueCurveSubElements, + TAG_HUE_CURVE, recognizedName)) { pImpl->m_elms.push_back( std::make_shared( @@ -907,7 +925,9 @@ class XMLParserHelper pImpl->getXmlFilename())); } else if (SupportedElement(name, pElt, TAG_CURVE_CTRL_PNTS, - gradingRGBCurveSubElements, recognizedName)) + gradingRGBCurveSubElements, recognizedName) || + SupportedElement(name, pElt, TAG_CURVE_CTRL_PNTS, + gradingHueCurveSubElements, recognizedName)) { pImpl->m_elms.push_back( std::make_shared( @@ -917,7 +937,9 @@ class XMLParserHelper pImpl->getXmlFilename())); } else if (SupportedElement(name, pElt, TAG_CURVE_SLOPES, - gradingRGBCurveSubElements, recognizedName)) + gradingRGBCurveSubElements, recognizedName) || + SupportedElement(name, pElt, TAG_CURVE_SLOPES, + gradingHueCurveSubElements, recognizedName)) { pImpl->m_elms.push_back( std::make_shared( diff --git a/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp b/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp index 146913f4a3..7dee4ba41c 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp @@ -1079,6 +1079,14 @@ CTFReaderOpEltRcPtr CTFReaderOpElt::GetReader(CTFReaderOpElt::Type type, } break; } + case CTFReaderOpElt::GradingHueCurveType: + { + if (!isCLF) + { + ADD_READER_FOR_VERSIONS_STARTING_AT(CTFReaderGradingHueCurveElt, 2_5); + } + break; + } case CTFReaderOpElt::GradingPrimaryType: { if (!isCLF) @@ -1636,6 +1644,19 @@ void CTFReaderDynamicParamElt::start(const char ** atts) GradingRGBCurveOpDataRcPtr pGCOp = pGC->getGradingRGBCurve(); pGCOp->getDynamicPropertyInternal()->makeDynamic(); } + else if (0 == Platform::Strcasecmp(TAG_DYN_PROP_HUECURVE, atts[i + 1])) + { + CTFReaderGradingHueCurveElt* pGC = + dynamic_cast(container.get()); + if (!pGC) + { + ThrowM(*this, "Dynamic parameter '", atts[i + 1], + "' is not supported in '", container->getName().c_str(), "'."); + } + + GradingHueCurveOpDataRcPtr pGCOp = pGC->getGradingHueCurve(); + pGCOp->getDynamicPropertyInternal()->makeDynamic(); + } else if (0 == Platform::Strcasecmp(TAG_DYN_PROP_TONE, atts[i + 1])) { CTFReaderGradingToneElt* pGT = @@ -2635,6 +2656,85 @@ const OpDataRcPtr CTFReaderGradingRGBCurveElt::getOp() const ////////////////////////////////////////////////////////// +CTFReaderGradingHueCurveElt::CTFReaderGradingHueCurveElt() + : m_gradingHueCurve(std::make_shared(GRADING_LOG)) +{ +} + +bool CTFReaderGradingHueCurveElt::isOpParameterValid(const char * att) const noexcept +{ + return CTFReaderOpElt::isOpParameterValid(att) || + 0 == Platform::Strcasecmp(ATTR_STYLE, att) || + 0 == Platform::Strcasecmp(ATTR_BYPASS_LIN_TO_LOG, att); +} + +void CTFReaderGradingHueCurveElt::start(const char ** atts) +{ + CTFReaderOpElt::start(atts); + + bool isStyleFound = false; + + unsigned i = 0; + while (atts[i]) + { + if (0 == Platform::Strcasecmp(ATTR_STYLE, atts[i])) + { + try + { + GradingStyle style; + TransformDirection dir; + ConvertStringToGradingStyleAndDir(atts[i + 1], style, dir); + m_gradingHueCurve->setStyle(style); + m_gradingHueCurve->setDirection(dir); + + // Initialize loading curve with corresponding style. + m_loadingHueCurve = GradingHueCurve::Create(style); + } + catch (Exception &) + { + ThrowM(*this, "Required attribute 'style' '", atts[i + 1], "' is invalid."); + } + isStyleFound = true; + } + else if (0 == Platform::Strcasecmp(ATTR_BYPASS_LIN_TO_LOG, atts[i])) + { + if (0 != Platform::Strcasecmp("true", atts[i + 1])) + { + std::ostringstream oss; + oss << "Unknown bypassLinToLog value: '" << atts[i + 1]; + oss << "' while parsing HueCurve."; + throwMessage(oss.str()); + } + + m_gradingHueCurve->setBypassLinToLog(true); + } + + i += 2; + } + + if (!isStyleFound) + { + ThrowM(*this, "Required attribute 'style' is missing."); + } +} + +void CTFReaderGradingHueCurveElt::end() +{ + CTFReaderOpElt::end(); + + // Set the loaded data. + m_gradingHueCurve->setValue(m_loadingHueCurve); + // Validate the end result. + m_gradingHueCurve->validate(); +} + +const OpDataRcPtr CTFReaderGradingHueCurveElt::getOp() const +{ + return m_gradingHueCurve; +} + +////////////////////////////////////////////////////////// + CTFReaderGradingCurveElt::CTFReaderGradingCurveElt(const std::string & name, ContainerEltRcPtr pParent, unsigned int xmlLineNumber, @@ -2649,6 +2749,32 @@ CTFReaderGradingCurveElt::~CTFReaderGradingCurveElt() namespace { + +bool IsRGBCurveType(const std::string & name) +{ + if (0 == Platform::Strcasecmp(TAG_RGB_CURVE_RED, name.c_str()) || + 0 == Platform::Strcasecmp(TAG_RGB_CURVE_GREEN, name.c_str()) || + 0 == Platform::Strcasecmp(TAG_RGB_CURVE_BLUE, name.c_str()) || + 0 == Platform::Strcasecmp(TAG_RGB_CURVE_MASTER, name.c_str())) + { + return true; + } + else if (0 == Platform::Strcasecmp(TAG_HUE_CURVE_HUE_HUE, name.c_str()) || + 0 == Platform::Strcasecmp(TAG_HUE_CURVE_HUE_SAT, name.c_str()) || + 0 == Platform::Strcasecmp(TAG_HUE_CURVE_HUE_LUM, name.c_str()) || + 0 == Platform::Strcasecmp(TAG_HUE_CURVE_LUM_SAT, name.c_str()) || + 0 == Platform::Strcasecmp(TAG_HUE_CURVE_SAT_SAT, name.c_str()) || + 0 == Platform::Strcasecmp(TAG_HUE_CURVE_LUM_LUM, name.c_str()) || + 0 == Platform::Strcasecmp(TAG_HUE_CURVE_SAT_LUM, name.c_str()) || + 0 == Platform::Strcasecmp(TAG_HUE_CURVE_HUE_FX, name.c_str())) + { + return false; + } + std::ostringstream err; + err << "Illegal grading curve name '" << name << "'."; + throw Exception(err.str().c_str()); +} + RGBCurveType GetRGBCurveType(const std::string & name) { if (0 == Platform::Strcasecmp(TAG_RGB_CURVE_RED, name.c_str())) @@ -2668,18 +2794,67 @@ RGBCurveType GetRGBCurveType(const std::string & name) return RGB_MASTER; } std::ostringstream err; - err << "Invalid curve name '" << name << "'."; + err << "Illegal grading curve name '" << name << "'."; throw Exception(err.str().c_str()); } + +HueCurveType GetHueCurveType(const std::string & name) +{ + if (0 == Platform::Strcasecmp(TAG_HUE_CURVE_HUE_HUE, name.c_str())) + { + return HUE_HUE; + } + else if (0 == Platform::Strcasecmp(TAG_HUE_CURVE_HUE_SAT, name.c_str())) + { + return HUE_SAT; + } + else if (0 == Platform::Strcasecmp(TAG_HUE_CURVE_HUE_LUM, name.c_str())) + { + return HUE_LUM; + } + else if (0 == Platform::Strcasecmp(TAG_HUE_CURVE_LUM_SAT, name.c_str())) + { + return LUM_SAT; + } + else if (0 == Platform::Strcasecmp(TAG_HUE_CURVE_SAT_SAT, name.c_str())) + { + return SAT_SAT; + } + else if (0 == Platform::Strcasecmp(TAG_HUE_CURVE_LUM_LUM, name.c_str())) + { + return LUM_LUM; + } + else if (0 == Platform::Strcasecmp(TAG_HUE_CURVE_SAT_LUM, name.c_str())) + { + return SAT_LUM; + } + else if (0 == Platform::Strcasecmp(TAG_HUE_CURVE_HUE_FX, name.c_str())) + { + return HUE_FX; + } + std::ostringstream err; + err << "Illegal grading curve name '" << name << "'."; + throw Exception(err.str().c_str()); } +} // anon + void CTFReaderGradingCurveElt::start(const char ** /* atts */) { try { - const RGBCurveType type = GetRGBCurveType(getName()); - auto pRGBCurveElt = dynamic_cast(getParent().get()); - m_curve = pRGBCurveElt->getLoadingRGBCurve()->getCurve(type); + if (IsRGBCurveType(getName())) + { + const RGBCurveType type = GetRGBCurveType(getName()); + auto pRGBCurveElt = dynamic_cast(getParent().get()); + m_curve = pRGBCurveElt->getLoadingRGBCurve()->getCurve(type); + } + else + { + const HueCurveType type = GetHueCurveType(getName()); + auto pHueCurveElt = dynamic_cast(getParent().get()); + m_curve = pHueCurveElt->getLoadingHueCurve()->getCurve(type); + } } catch (Exception& ce) { diff --git a/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.h b/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.h index dff332595d..e4e1bacecd 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.h +++ b/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.h @@ -15,6 +15,7 @@ #include "ops/gamma/GammaOpData.h" #include "ops/gradingprimary/GradingPrimaryOpData.h" #include "ops/gradingrgbcurve/GradingRGBCurveOpData.h" +#include "ops/gradinghuecurve/GradingHueCurveOpData.h" #include "ops/gradingtone/GradingToneOpData.h" #include "ops/log/LogOpData.h" #include "ops/log/LogUtils.h" @@ -323,6 +324,7 @@ class CTFReaderOpElt : public XmlReaderContainerElt GammaType, GradingPrimaryType, GradingRGBCurveType, + GradingHueCurveType, GradingToneType, InvLut1DType, InvLut3DType, @@ -698,6 +700,34 @@ class CTFReaderGradingRGBCurveElt : public CTFReaderOpElt GradingRGBCurveOpDataRcPtr m_gradingRGBCurve; }; +class CTFReaderGradingHueCurveElt : public CTFReaderOpElt +{ +public: + CTFReaderGradingHueCurveElt(); + ~CTFReaderGradingHueCurveElt() = default; + + void start(const char ** atts) override; + void end() override; + + const OpDataRcPtr getOp() const override; + + const GradingHueCurveOpDataRcPtr & getGradingHueCurve() const + { + return m_gradingHueCurve; + } + + // For sub-elements. + GradingHueCurveRcPtr & getLoadingHueCurve() { return m_loadingHueCurve; } + +protected: + bool isOpParameterValid(const char * att) const noexcept override; + +private: + // Editable HueCurve that will be set to the OpData when parsing of the element is done. + GradingHueCurveRcPtr m_loadingHueCurve; + GradingHueCurveOpDataRcPtr m_gradingHueCurve; +}; + class CTFReaderGradingCurveElt : public XmlReaderComplexElt { public: diff --git a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp index 927f2c2c21..df7f698ef0 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp @@ -274,17 +274,30 @@ CTFVersion GetOpMinimumVersion(const ConstOpDataRcPtr & op) { minVersion = CTF_PROCESS_LIST_VERSION_2_4; } + else if ( ff->getStyle() == FixedFunctionOpData::RGB_TO_HSY_LOG + || ff->getStyle() == FixedFunctionOpData::HSY_LOG_TO_RGB + || ff->getStyle() == FixedFunctionOpData::RGB_TO_HSY_LIN + || ff->getStyle() == FixedFunctionOpData::HSY_LIN_TO_RGB + || ff->getStyle() == FixedFunctionOpData::RGB_TO_HSY_VID + || ff->getStyle() == FixedFunctionOpData::HSY_VID_TO_RGB ) + { + minVersion = CTF_PROCESS_LIST_VERSION_2_5; + } break; } case OpData::GradingPrimaryType: case OpData::GradingRGBCurveType: - case OpData::GradingHueCurveType: case OpData::GradingToneType: case OpData::LogType: { minVersion = CTF_PROCESS_LIST_VERSION_2_0; break; } + case OpData::GradingHueCurveType: + { + minVersion = CTF_PROCESS_LIST_VERSION_2_5; + break; + } case OpData::ExponentType: { auto exp = OCIO_DYNAMIC_POINTER_CAST(op); diff --git a/src/OpenColorIO/fileformats/ctf/CTFTransform.h b/src/OpenColorIO/fileformats/ctf/CTFTransform.h index 5473f6ef36..f8fb741c26 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFTransform.h +++ b/src/OpenColorIO/fileformats/ctf/CTFTransform.h @@ -119,9 +119,12 @@ static const CTFVersion CTF_PROCESS_LIST_VERSION_2_1 = CTFVersion(2, 1); // the LIN_TO_PQ, LIN_TO_GAMMA_LOG, AND LIN_TO_DOUBLE_LOG FixedFunctionOps. static const CTFVersion CTF_PROCESS_LIST_VERSION_2_4 = CTFVersion(2, 4); +// Version 2.5 2025-08 adds the GradingHueCurve. +static const CTFVersion CTF_PROCESS_LIST_VERSION_2_5 = CTFVersion(2, 5); + // Add new version before this line // and do not forget to update the following line. -static const CTFVersion CTF_PROCESS_LIST_VERSION = CTF_PROCESS_LIST_VERSION_2_4; +static const CTFVersion CTF_PROCESS_LIST_VERSION = CTF_PROCESS_LIST_VERSION_2_5; // Version 1.0 initial Autodesk version for InfoElt. diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp index efa19dfa9d..f2610ef21a 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp @@ -1601,10 +1601,12 @@ void applyHSYToRGB(const void * inImg, void * outImg, long numPixels, float min0 for(long idx=0; idx DefaultLumLumLinCtrl{ { -7.0f, -7. } -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueHue(DefaultHueHueCtrl, BSplineCurveType::HUE_HUE_B_SPLINE ); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueSat(DefaultHueSatCtrl, BSplineCurveType::PERIODIC_1_B_SPLINE ); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueFx(DefaultHueFxCtrl, BSplineCurveType::PERIODIC_0_B_SPLINE ); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSat(DefaultLumSatCtrl, BSplineCurveType::HORIZONTAL1_B_SPLINE); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSatLin(DefaultLumSatLinCtrl, BSplineCurveType::HORIZONTAL1_B_SPLINE); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultSatSat(DefaultSatSatCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultSatLum(DefaultSatLumCtrl, BSplineCurveType::HORIZONTAL1_B_SPLINE); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumLum(DefaultLumLumCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); -const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumLumLin(DefaultLumLumLinCtrl, BSplineCurveType::DIAGONAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueHue(DefaultHueHueCtrl, BSplineType::HUE_HUE_B_SPLINE ); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueSat(DefaultHueSatCtrl, BSplineType::PERIODIC_1_B_SPLINE ); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueLum(DefaultHueSatCtrl, BSplineType::PERIODIC_1_B_SPLINE ); // HUE_LUM use the same as HUE_SAT +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueFx(DefaultHueFxCtrl, BSplineType::PERIODIC_0_B_SPLINE ); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSat(DefaultLumSatCtrl, BSplineType::HORIZONTAL1_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumSatLin(DefaultLumSatLinCtrl, BSplineType::HORIZONTAL1_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultSatSat(DefaultSatSatCtrl, BSplineType::DIAGONAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultSatLum(DefaultSatLumCtrl, BSplineType::HORIZONTAL1_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumLum(DefaultLumLumCtrl, BSplineType::DIAGONAL_B_SPLINE); +const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultLumLumLin(DefaultLumLumLinCtrl, BSplineType::DIAGONAL_B_SPLINE); const std::array, static_cast(HUE_NUM_CURVES)> GradingHueCurveImpl::DefaultCurvesLin( { std::ref(DefaultHueHue), std::ref(DefaultHueSat), - std::ref(DefaultHueSat), // HUE_LUM use the same as HUE_SAT + std::ref(DefaultHueLum), std::ref(DefaultLumSatLin), std::ref(DefaultSatSat), std::ref(DefaultLumLumLin), @@ -68,7 +69,7 @@ const std::array, static_c const std::array, static_cast(HUE_NUM_CURVES)> GradingHueCurveImpl::DefaultCurves( { std::ref(DefaultHueHue), std::ref(DefaultHueSat), - std::ref(DefaultHueSat), // HUE_LUM use the same as HUE_SAT + std::ref(DefaultHueLum), std::ref(DefaultLumSat), std::ref(DefaultSatSat), std::ref(DefaultLumLum), @@ -136,10 +137,10 @@ GradingHueCurveRcPtr GradingHueCurveImpl::createEditableCopy() const namespace { -const char * CurveType(int c) +const char * CurveTypeName(int c) { - const HueCurveType curve = static_cast(c); - switch (curve) + const HueCurveType curveType = static_cast(c); + switch (curveType) { case HUE_HUE: return "hue_hue"; @@ -176,10 +177,20 @@ void GradingHueCurveImpl::validate() const catch (Exception & e) { std::ostringstream oss; - oss << "GradingHueCurve validation failed for curve: " << CurveType(c) << "' curve " + oss << "GradingHueCurve validation failed for '" << CurveTypeName(c) << "' curve " << "with: " << e.what(); throw Exception(oss.str().c_str()); } + + const BSplineType splineType = m_curves[c]->getSplineType(); + const HueCurveType hueType = static_cast(c); + if (splineType != GetBSplineTypeForHueCurveType(hueType)) + { + std::ostringstream oss; + oss << "GradingHueCurve validation failed: '" << CurveTypeName(c) << "' curve " + << "is of the wrong BSplineType."; + throw Exception(oss.str().c_str()); + } } } @@ -221,6 +232,32 @@ GradingBSplineCurveRcPtr GradingHueCurveImpl::getCurve(HueCurveType c) return m_curves[c]; } +BSplineType GradingHueCurve::GetBSplineTypeForHueCurveType(HueCurveType curveType) +{ + switch (curveType) + { + case HUE_HUE: + return BSplineType::HUE_HUE_B_SPLINE; + case HUE_SAT: + return BSplineType::PERIODIC_1_B_SPLINE; + case HUE_LUM: + return BSplineType::PERIODIC_1_B_SPLINE; + case LUM_SAT: + return BSplineType::HORIZONTAL1_B_SPLINE; + case SAT_SAT: + return BSplineType::DIAGONAL_B_SPLINE; + case LUM_LUM: + return BSplineType::DIAGONAL_B_SPLINE; + case SAT_LUM: + return BSplineType::HORIZONTAL1_B_SPLINE; + case HUE_FX: + return BSplineType::PERIODIC_0_B_SPLINE; + case HUE_NUM_CURVES: + default: + return BSplineType::B_SPLINE; + } +} + GradingHueCurveRcPtr GradingHueCurve::Create(GradingStyle style) { auto newCurve = std::make_shared(style); @@ -254,6 +291,9 @@ GradingHueCurveRcPtr GradingHueCurve::Create( lumLum, satLum, hueFx); + + newCurve->validate(); + GradingHueCurveRcPtr res = newCurve; return res; } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h index 698e9fa037..2c2f456236 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h @@ -22,14 +22,14 @@ class GradingHueCurveImpl : public GradingHueCurve GradingHueCurveImpl(); GradingHueCurveImpl(GradingStyle style); GradingHueCurveImpl( - ConstGradingBSplineCurveRcPtr hueHue, - ConstGradingBSplineCurveRcPtr hueSat, - ConstGradingBSplineCurveRcPtr hueLum, - ConstGradingBSplineCurveRcPtr lumSat, - ConstGradingBSplineCurveRcPtr satSat, - ConstGradingBSplineCurveRcPtr lumLum, - ConstGradingBSplineCurveRcPtr satLum, - ConstGradingBSplineCurveRcPtr hueFx ); + ConstGradingBSplineCurveRcPtr hueHue, + ConstGradingBSplineCurveRcPtr hueSat, + ConstGradingBSplineCurveRcPtr hueLum, + ConstGradingBSplineCurveRcPtr lumSat, + ConstGradingBSplineCurveRcPtr satSat, + ConstGradingBSplineCurveRcPtr lumLum, + ConstGradingBSplineCurveRcPtr satLum, + ConstGradingBSplineCurveRcPtr hueFx ); GradingHueCurveImpl(const ConstGradingHueCurveRcPtr & rhs); GradingHueCurveRcPtr createEditableCopy() const override; @@ -41,6 +41,7 @@ class GradingHueCurveImpl : public GradingHueCurve static const GradingBSplineCurveImpl DefaultHueHue; static const GradingBSplineCurveImpl DefaultHueSat; + static const GradingBSplineCurveImpl DefaultHueLum; static const GradingBSplineCurveImpl DefaultHueFx; static const GradingBSplineCurveImpl DefaultLumSat; static const GradingBSplineCurveImpl DefaultLumSatLin; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp index 0538f18972..10a3ef37cb 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp @@ -7,6 +7,7 @@ #include #include "GpuShaderUtils.h" +#include "ops/gradinghuecurve/GradingHueCurveOpCPU.h" #include "ops/gradinghuecurve/GradingHueCurveOpGPU.h" #include "ops/gradinghuecurve/GradingHueCurveOp.h" #include "transforms/GradingHueCurveTransform.h" @@ -187,16 +188,14 @@ void GradingHueCurveOp::removeDynamicProperties() ConstOpCPURcPtr GradingHueCurveOp::getCPUOp(bool /*fastLogExpPow*/) const { - // TODO: Add CPU renderer before merging back the adsk fork - //ConstGradingRGBCurveOpDataRcPtr data = rgbCurveData(); - //return GetGradingRGBCurveCPURenderer(data); - return ConstOpCPURcPtr(); + ConstGradingHueCurveOpDataRcPtr data = hueCurveData(); + return GetGradingHueCurveCPURenderer(data); } void GradingHueCurveOp::extractGpuShaderInfo(GpuShaderCreatorRcPtr & shaderCreator) const { ConstGradingHueCurveOpDataRcPtr data = hueCurveData(); - GetHueCurveGPUShaderProgram(shaderCreator, data); + GetGradingHueCurveGPUShaderProgram(shaderCreator, data); } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp new file mode 100644 index 0000000000..5054d1183c --- /dev/null +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#include +#include +#include + +#include + +#include "BitDepthUtils.h" +#include "MathUtils.h" +#include "ops/gradinghuecurve/GradingHueCurveOpCPU.h" +#include "ops/fixedfunction/FixedFunctionOpCPU.h" +#include "ops/fixedfunction/FixedFunctionOpData.h" + +namespace OCIO_NAMESPACE +{ + +namespace +{ + +namespace LogLinConstants +{ + static constexpr float xbrk = 0.0041318374739483946f; + static constexpr float shift = -0.000157849851665374f; + static constexpr float m = 1.f / (0.18f + shift); + static constexpr float gain = 363.034608563f; + static constexpr float offs = -7.f; + static constexpr float ybrk = -5.5f; + static constexpr float base2 = 1.4426950408889634f; // 1/log(2) +} + +inline void LinLog(float * out) +{ + out[2] = (out[2] < LogLinConstants::xbrk) ? + out[2] * LogLinConstants::gain + LogLinConstants::offs : + LogLinConstants::base2 * std::log((out[2] + LogLinConstants::shift) * LogLinConstants::m); +} + +inline void LogLin(float * out) +{ + out[2] = (out[2] < LogLinConstants::ybrk) ? + (out[2] - LogLinConstants::offs) / LogLinConstants::gain : + std::pow(2.0f, out[2]) * (0.18f + LogLinConstants::shift) - LogLinConstants::shift; +} + +inline void NoOp(float * /* out */) +{ +} + +typedef void (apply_nonlin_func)(float *out); + +class GradingHueCurveOpCPU : public OpCPU +{ +public: + GradingHueCurveOpCPU() = delete; + GradingHueCurveOpCPU(const GradingHueCurveOpCPU &) = delete; + + explicit GradingHueCurveOpCPU(ConstGradingHueCurveOpDataRcPtr & ghuec); + + bool isDynamic() const override; + bool hasDynamicProperty(DynamicPropertyType type) const override; + DynamicPropertyRcPtr getDynamicProperty(DynamicPropertyType type) const override; + +protected: + DynamicPropertyGradingHueCurveImplRcPtr m_ghuecurve; + bool m_isLinear = false; + + ConstOpCPURcPtr m_rgbToHsyOp; + ConstOpCPURcPtr m_hsyToRgbOp; + + apply_nonlin_func *m_applyLinLog = NoOp; + apply_nonlin_func *m_applyLogLin = NoOp; +}; + +GradingHueCurveOpCPU::GradingHueCurveOpCPU(ConstGradingHueCurveOpDataRcPtr & gcData) + : OpCPU() +{ + m_ghuecurve = gcData->getDynamicPropertyInternal(); + if (m_ghuecurve->isDynamic()) + { + m_ghuecurve = m_ghuecurve->createEditableCopy(); + } + + const GradingStyle style = gcData->getStyle(); + + FixedFunctionOpData::Style fwdStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + FixedFunctionOpData::Style invStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + switch(style) + { + case GRADING_LIN: + fwdStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + invStyle = FixedFunctionOpData::HSY_LIN_TO_RGB; + m_isLinear = true; + break; + case GRADING_LOG: + fwdStyle = FixedFunctionOpData::RGB_TO_HSY_LOG; + invStyle = FixedFunctionOpData::HSY_LOG_TO_RGB; + break; + case GRADING_VIDEO: + fwdStyle = FixedFunctionOpData::RGB_TO_HSY_VID; + invStyle = FixedFunctionOpData::HSY_VID_TO_RGB; + break; + } + + ConstFixedFunctionOpDataRcPtr fwdOpData = std::make_shared(fwdStyle); + m_rgbToHsyOp = GetFixedFunctionCPURenderer(fwdOpData, false /* fastLogExpPow */); + ConstFixedFunctionOpDataRcPtr invOpData = std::make_shared(invStyle); + m_hsyToRgbOp = GetFixedFunctionCPURenderer(invOpData, false /* fastLogExpPow */); + + if (style == GRADING_LIN && !gcData->getBypassLinToLog()) + { + m_applyLinLog = LinLog; + m_applyLogLin = LogLin; + } +} + +bool GradingHueCurveOpCPU::isDynamic() const +{ + return m_ghuecurve->isDynamic(); +} + +bool GradingHueCurveOpCPU::hasDynamicProperty(DynamicPropertyType type) const +{ + bool res = false; + if (type == DYNAMIC_PROPERTY_GRADING_HUECURVE) + { + res = m_ghuecurve->isDynamic(); + } + return res; +} + +DynamicPropertyRcPtr GradingHueCurveOpCPU::getDynamicProperty(DynamicPropertyType type) const +{ + if (type == DYNAMIC_PROPERTY_GRADING_HUECURVE) + { + if (m_ghuecurve->isDynamic()) + { + return m_ghuecurve; + } + } + else + { + throw Exception("Dynamic property type not supported by GradingHueCurve."); + } + + throw Exception("GradingHueCurve property is not dynamic."); +} + +class GradingHueCurveFwdOpCPU : public GradingHueCurveOpCPU +{ +public: + GradingHueCurveFwdOpCPU(const GradingHueCurveOpCPU &) = delete; + GradingHueCurveFwdOpCPU() = delete; + + explicit GradingHueCurveFwdOpCPU(ConstGradingHueCurveOpDataRcPtr & ghuec); + void apply(const void * inImg, void * outImg, long numPixels) const override; +}; + +GradingHueCurveFwdOpCPU::GradingHueCurveFwdOpCPU(ConstGradingHueCurveOpDataRcPtr & ghuec) + : GradingHueCurveOpCPU(ghuec) +{ +} + +static constexpr auto PixelSize = 4 * sizeof(float); + +void GradingHueCurveFwdOpCPU::apply(const void * inImg, void * outImg, long numPixels) const +{ + if (m_ghuecurve->getLocalBypass()) + { + if (inImg != outImg) + { + memcpy(outImg, inImg, numPixels * PixelSize); + } + return; + } + + const GradingBSplineCurveImpl::KnotsCoefs & knotsCoefs = m_ghuecurve->getKnotsCoefs(); + + const float * in = (float *)inImg; + float * out = (float *)outImg; + + for (long idx = 0; idx < numPixels; ++idx) + { + m_rgbToHsyOp->apply(in, out, 1L); + + m_applyLinLog(out); + + // HUE-SAT + const float hueSatGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(HUE_SAT), out[0])); + // HUE-LUM + float hueLumGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(HUE_LUM), out[0])); + // HUE-HUE + out[0] = knotsCoefs.evalCurve(static_cast(HUE_HUE), out[0]); + // SAT-SAT + out[1] = std::max(0.f, knotsCoefs.evalCurve(static_cast(SAT_SAT), out[1])); + // LUM-SAT + const float lumSatGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(LUM_SAT), out[2])); + + // Apply sat gain. + const float satGain = lumSatGain * hueSatGain; + out[1] *= satGain; + + // SAT-LUM + const float satLumGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(SAT_LUM), out[1])); + // LUM-LUM + out[2] = knotsCoefs.evalCurve(static_cast(LUM_LUM), out[2]); + + m_applyLogLin(out); + + // Limit hue-lum gain at low sat, since the hue is more noisy, + // and when sat is 0 the hue becomes unknown (and is not invertible). + hueLumGain = 1.f - (1.f - hueLumGain) * std::min(out[1], 1.f); + + // Apply lum gain. + out[2] = m_isLinear ? out[2] * hueLumGain * satLumGain : + out[2] + (hueLumGain + satLumGain - 2.f) * 0.1f; + + // HUE-FX + out[0] = out[0] - std::floor(out[0]); // wrap to [0,1) + out[0] = out[0] + knotsCoefs.evalCurve(static_cast(HUE_FX), out[0]); + + m_hsyToRgbOp->apply(out, out, 1L); + + in += 4; + out += 4; + } +} + +class GradingHueCurveRevOpCPU : public GradingHueCurveOpCPU +{ +public: + GradingHueCurveRevOpCPU(const GradingHueCurveOpCPU &) = delete; + GradingHueCurveRevOpCPU() = delete; + + explicit GradingHueCurveRevOpCPU(ConstGradingHueCurveOpDataRcPtr & ghuec); + void apply(const void * inImg, void * outImg, long numPixels) const override; +}; + +GradingHueCurveRevOpCPU::GradingHueCurveRevOpCPU(ConstGradingHueCurveOpDataRcPtr & ghuec) + : GradingHueCurveOpCPU(ghuec) +{ +} + +void GradingHueCurveRevOpCPU::apply(const void * inImg, void * outImg, long numPixels) const +{ + if (m_ghuecurve->getLocalBypass()) + { + if (inImg != outImg) + { + memcpy(outImg, inImg, numPixels * PixelSize); + } + return; + } + + const GradingBSplineCurveImpl::KnotsCoefs & knotsCoefs = m_ghuecurve->getKnotsCoefs(); + + const float * in = (float *)inImg; + float * out = (float *)outImg; + + for (long idx = 0; idx < numPixels; ++idx) + { + m_rgbToHsyOp->apply(in, out, 1L); + + // Invert HUE-FX. + out[0] = knotsCoefs.evalCurveRevHue(static_cast(HUE_FX), out[0], true); + + // Invert HUE-HUE. + out[0] = knotsCoefs.evalCurveRevHue(static_cast(HUE_HUE), out[0], false); + + // Use the inverted hue to calculate the HUE-SAT & HUE-LUM gains. + out[0] = out[0] - std::floor(out[0]); // wrap to [0,1) + const float hue_sat_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(HUE_SAT), out[0]) ); + float hue_lum_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(HUE_LUM), out[0]) ); + + // Use the output sat to calculate the SAT-LUM gain. + out[1] = std::max(0.f, out[1]); // guard against negative saturation + const float sat_lum_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(SAT_LUM), out[1]) ); + + hue_lum_gain = 1.f - (1.f - hue_lum_gain) * std::min(out[1], 1.f); + + // Invert the lum gain. + const float lum_gain = hue_lum_gain * sat_lum_gain; + out[2] = m_isLinear ? out[2] / std::max(0.01f, lum_gain) : + out[2] - (hue_lum_gain + sat_lum_gain - 2.f) * 0.1f; + + m_applyLinLog(out); + + // Invert LUM-LUM. + out[2] = knotsCoefs.evalCurveRev(static_cast(LUM_LUM), out[2]); + + // Use it to calc the LUM-SAT gain. + const float lum_sat_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(LUM_SAT), out[2]) ); + + m_applyLogLin(out); + + // Invert the sat gain. + const float sat_gain = lum_sat_gain * hue_sat_gain; + out[1] /= std::max(0.01f, sat_gain); + + // Invert SAT-SAT. + out[1] = std::max( 0.f, knotsCoefs.evalCurveRev(static_cast(SAT_SAT), out[1]) ); + + m_hsyToRgbOp->apply(out, out, 1L); + + in += 4; + out += 4; + } +} + +} // Anonymous namespace + +/////////////////////////////////////////////////////////////////////////////// + +ConstOpCPURcPtr GetGradingHueCurveCPURenderer(ConstGradingHueCurveOpDataRcPtr & prim) +{ + + switch (prim->getDirection()) + { + case TRANSFORM_DIR_FORWARD: + { + return std::make_shared(prim); + break; + } + case TRANSFORM_DIR_INVERSE: + { + return std::make_shared(prim); + break; + } + } + + throw Exception("Illegal GradingHueCurve direction."); +} + +} // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.h new file mode 100644 index 0000000000..934e84ce0f --- /dev/null +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#ifndef INCLUDED_OCIO_GRADINGHUECURVE_CPU_H +#define INCLUDED_OCIO_GRADINGHUECURVE_CPU_H + +#include + +#include + +#include "Op.h" +#include "ops/gradinghuecurve/GradingHueCurveOpData.h" + +namespace OCIO_NAMESPACE +{ + +ConstOpCPURcPtr GetGradingHueCurveCPURenderer(ConstGradingHueCurveOpDataRcPtr & hueCurve); + +} // namespace OCIO_NAMESPACE + +#endif diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h index 5d249a261c..5a5d3795b6 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h @@ -41,7 +41,7 @@ class GradingHueCurveOpData : public OpData void validate() const override; - Type getType() const override { return GradingRGBCurveType; } + Type getType() const override { return GradingHueCurveType; } bool isNoOp() const override; bool isIdentity() const override; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp index 7fb67fbe95..b20f07cbb2 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp @@ -79,6 +79,8 @@ struct GCProperties std::string m_coefs{ "coefs" }; std::string m_localBypass{ "localBypass" }; std::string m_eval{ "evalBSplineCurve" }; + std::string m_evalRev{ "evalBSplineCurveRev" }; + std::string m_evalRevHue{ "evalBSplineCurveRevHue" }; }; void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, @@ -107,7 +109,7 @@ void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, { // Declare uniform. GpuShaderText stDecl(shaderCreator->getLanguage()); - // Need 2 ints for each curves. + // Need 2 ints for each curve. stDecl.declareUniformArrayInt(name, 16); // TODO: Avoid magic numbers (8 Curves * 2 values) shaderCreator->addToDeclareShaderCode(stDecl.string().c_str()); } @@ -154,10 +156,12 @@ void SetGCProperties(GpuShaderCreatorRcPtr & shaderCreator, bool dynamic, GCProp propNames.m_localBypass = BuildResourceName(shaderCreator, opPrefix, propNames.m_localBypass); propNames.m_eval = BuildResourceName(shaderCreator, opPrefix, propNames.m_eval); + propNames.m_evalRev = BuildResourceName(shaderCreator, opPrefix, propNames.m_evalRev); + propNames.m_evalRevHue = BuildResourceName(shaderCreator, opPrefix, propNames.m_evalRevHue); } else { - // Non-dynamic ops need an helper function for each op. + // Non-dynamic ops need a helper function for each op. const auto resIndex = shaderCreator->getNextResourceIndex(); propNames.m_knotsOffsets = BuildResourceNameIndexed(shaderCreator, opPrefix, @@ -170,6 +174,10 @@ void SetGCProperties(GpuShaderCreatorRcPtr & shaderCreator, bool dynamic, GCProp propNames.m_coefs, resIndex); propNames.m_eval = BuildResourceNameIndexed(shaderCreator, opPrefix, propNames.m_eval, resIndex); + propNames.m_evalRev = BuildResourceNameIndexed(shaderCreator, opPrefix, + propNames.m_evalRev, resIndex); + propNames.m_evalRevHue = BuildResourceNameIndexed(shaderCreator, opPrefix, + propNames.m_evalRevHue, resIndex); } } @@ -207,6 +215,21 @@ void AddGCPropertiesUniforms(GpuShaderCreatorRcPtr & shaderCreator, AddUniform(shaderCreator, getLB, propNames.m_localBypass); } +void AddCurveFunctionName(GpuShaderCreatorRcPtr & shaderCreator, + GpuShaderText & st, + const std::string & funcName) +{ + st.newLine() << ""; + if (shaderCreator->getLanguage() == LANGUAGE_OSL_1 || shaderCreator->getLanguage() == GPU_LANGUAGE_MSL_2_0) + { + st.newLine() << st.floatKeyword() << " " << funcName << "(int curveIdx, float x, float identity_x)"; + } + else + { + st.newLine() << st.floatKeyword() << " " << funcName << "(in int curveIdx, in float x, in float identity_x)"; + } +} + void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, ConstGradingHueCurveOpDataRcPtr & gcData, const GCProperties & props, @@ -228,25 +251,40 @@ void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator st.declareFloatArrayConst(props.m_coefs, propGC->getNumCoefs(), propGC->getCoefsArray()); } - st.newLine() << ""; - if (shaderCreator->getLanguage() == LANGUAGE_OSL_1 || shaderCreator->getLanguage() == GPU_LANGUAGE_MSL_2_0) - { - st.newLine() << st.floatKeyword() << " " << props.m_eval << "(int curveIdx, float x, float identity_x)"; - } - else - { - st.newLine() << st.floatKeyword() << " " << props.m_eval << "(in int curveIdx, in float x, in float identity_x)"; - } - st.newLine() << "{"; - st.indent(); + // Both the forward and inverse hue curve eval need the forward spline eval, so always add that. - const bool isInv = gcData->getDirection() == TRANSFORM_DIR_INVERSE; - GradingBSplineCurveImpl::AddShaderEval(st, props.m_knotsOffsets, props.m_coefsOffsets, - props.m_knots, props.m_coefs, isInv); + AddCurveFunctionName(shaderCreator, st, props.m_eval); + st.newLine() << "{"; + st.indent(); + GradingBSplineCurveImpl::AddShaderEvalFwd(st, props.m_knotsOffsets, props.m_coefsOffsets, + props.m_knots, props.m_coefs); st.dedent(); st.newLine() << "}"; + if (gcData->getDirection() == TRANSFORM_DIR_INVERSE) + { + // Add inverse curve eval. + AddCurveFunctionName(shaderCreator, st, props.m_evalRev); + + st.newLine() << "{"; + st.indent(); + GradingBSplineCurveImpl::AddShaderEvalRev(st, props.m_knotsOffsets, props.m_coefsOffsets, + props.m_knots, props.m_coefs); + st.dedent(); + st.newLine() << "}"; + + // Add inverse hue curve eval. + AddCurveFunctionName(shaderCreator, st, props.m_evalRevHue); + + st.newLine() << "{"; + st.indent(); + GradingBSplineCurveImpl::AddShaderEvalRevHue(st, props.m_knotsOffsets, props.m_coefsOffsets, + props.m_knots, props.m_coefs); + st.dedent(); + st.newLine() << "}"; + } + shaderCreator->addToHelperShaderCode(st.string().c_str()); } @@ -262,6 +300,9 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, const std::string pix(shaderCreator->getPixelName()); if(drawCurveOnly) { + // Note that this is not within the localBypass if-statement since outColor + // needs to get processed even if isDefault is true for all curves since + // the default for the horizontal curves is all 1 rather than an identity. st.newLine() << pix << ".r = " << props.m_eval << "(1, " << pix << ".r, 1.);"; // HUE-SAT st.newLine() << pix << ".g = " << props.m_eval << "(1, " << pix << ".g, 1.);"; // HUE-SAT st.newLine() << pix << ".b = " << props.m_eval << "(1, " << pix << ".b, 1.);"; // HUE-SAT @@ -299,6 +340,7 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, st.newLine() << "}"; } + // Lin to Log (on Luma only). if (doLinToLog) { // NB: Although the linToLog and logToLin are correct inverses, the limits of @@ -309,18 +351,28 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, st.newLine() << ""; } + // Apply the hue curves. + st.newLine() << ""; - st.newLine() << "float hueSatGain = max(0., " << props.m_eval << "(1, " << pix << ".r, 1.));"; // HUE-SAT - st.newLine() << "float hueLumGain = max(0., " << props.m_eval << "(2, " << pix << ".r, 1.));"; // HUE-LUM - st.newLine() << pix << ".r = " << props.m_eval << "(0, " << pix << ".r, " << pix << ".r);"; // HUE-HUE - st.newLine() << "" << pix << ".g = max(0., " << props.m_eval << "(4, " << pix << ".g, " << pix << ".g));"; // SAT-SAT - st.newLine() << "float lumSatGain = max(0., " << props.m_eval << "(3, " << pix << ".b, 1.));"; // LUM-SAT + // HUE-SAT + st.newLine() << "float hueSatGain = max(0., " << props.m_eval << "(1, " << pix << ".r, 1.));"; + // HUE-LUM + st.newLine() << "float hueLumGain = max(0., " << props.m_eval << "(2, " << pix << ".r, 1.));"; + // HUE-HUE + st.newLine() << pix << ".r = " << props.m_eval << "(0, " << pix << ".r, " << pix << ".r);"; + // SAT-SAT + st.newLine() << "" << pix << ".g = max(0., " << props.m_eval << "(4, " << pix << ".g, " << pix << ".g));"; + // LUM-SAT + st.newLine() << "float lumSatGain = max(0., " << props.m_eval << "(3, " << pix << ".b, 1.));"; + // SAT-LUM st.newLine() << "float satGain = lumSatGain * hueSatGain;"; st.newLine() << "" << pix << ".g = satGain * " << pix << ".g;"; - st.newLine() << "float satLumGain = max(0., " << props.m_eval << "(6, " << pix << ".g, 1.));"; // SAT-LUM - st.newLine() << pix << ".b = " << props.m_eval << "(5, " << pix << ".b, " << pix << ".b);"; // LUM-LUM + st.newLine() << "float satLumGain = max(0., " << props.m_eval << "(6, " << pix << ".g, 1.));"; + // LUM-LUM + st.newLine() << pix << ".b = " << props.m_eval << "(5, " << pix << ".b, " << pix << ".b);"; st.newLine() << ""; + // Log to Lin. if (doLinToLog) { st.newLine() << ""; @@ -331,16 +383,22 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, st.newLine() << ""; st.newLine() << "hueLumGain = 1. - (1. - hueLumGain) * min( 1., " << pix << ".g );"; if (style == GRADING_LOG) - // Use shift rather than scale for log mode. - st.newLine() << pix << ".b = " << pix << ".b + (hueLumGain + satLumGain - 2.) * 0.1;"; + { + // Use shift rather than scale for log mode. + st.newLine() << pix << ".b = " << pix << ".b + (hueLumGain + satLumGain - 2.) * 0.1;"; + } else - // Note this is applied in linear space, for linear style. - st.newLine() << pix << ".b = " << pix << ".b * hueLumGain * satLumGain;"; + { + // Note this is applied in linear space, for linear style. + st.newLine() << pix << ".b = " << pix << ".b * hueLumGain * satLumGain;"; + } st.newLine() << ""; + // HUE-FX st.newLine() << pix << ".r = " << pix << ".r - floor( " << pix << ".r );"; - st.newLine() << pix << ".r = " << pix << ".r + " << props.m_eval << "(7, " << pix << ".r, 0.);"; // HUE-FX + st.newLine() << pix << ".r = " << pix << ".r + " << props.m_eval << "(7, " << pix << ".r, 0.);"; + // Add the conversion from HSY to RGB. { FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; switch(style) @@ -370,59 +428,154 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, } } -// TODO: Implement the inverse shader. -//void AddGCInverseShader(GpuShaderCreatorRcPtr & shaderCreator, -// GpuShaderText & st, -// const GCProperties & props, -// bool dyn, -// bool doLinToLog, -// GradingStyle style) -//{ -// if (dyn) -// { -// st.newLine() << "if (!" << props.m_localBypass << ")"; -// st.newLine() << "{"; -// st.indent(); -// } -// -// if (doLinToLog) -// { -// // NB: Although the linToLog and logToLin are correct inverses, the limits of -// // floating-point arithmetic cause errors in the lowest bit of the round trip. -// -// st.newLine() << "// Convert from lin to log."; -// AddLinToLogShader(shaderCreator, st); -// st.newLine() << ""; -// } -// -// const std::string pix(shaderCreator->getPixelName()); -// -// // Call the curve evaluation method for each curve. -// st.newLine() << pix << ".rgb.r = " << props.m_eval << "(3, " << pix << ".rgb.r);"; // MASTER -// st.newLine() << pix << ".rgb.g = " << props.m_eval << "(3, " << pix << ".rgb.g);"; // MASTER -// st.newLine() << pix << ".rgb.b = " << props.m_eval << "(3, " << pix << ".rgb.b);"; // MASTER -// st.newLine() << pix << ".rgb.r = " << props.m_eval << "(0, " << pix << ".rgb.r);"; // RED -// st.newLine() << pix << ".rgb.g = " << props.m_eval << "(1, " << pix << ".rgb.g);"; // GREEN -// st.newLine() << pix << ".rgb.b = " << props.m_eval << "(2, " << pix << ".rgb.b);"; // BLUE -// -// if (doLinToLog) -// { -// st.newLine() << ""; -// st.newLine() << "// Convert from log to lin."; -// AddLogToLinShader(shaderCreator, st); -// } -// -// if (dyn) -// { -// st.dedent(); -// st.newLine() << "}"; -// } -//} +void AddGCInverseShader(GpuShaderCreatorRcPtr & shaderCreator, + GpuShaderText & st, + const GCProperties & props, + bool dyn, + bool doLinToLog, + bool drawCurveOnly, + GradingStyle style) +{ + const std::string pix(shaderCreator->getPixelName()); + + if(drawCurveOnly) + { + // Note that this is not within the localBypass if-statement since outColor + // needs to get processed even if isDefault is true for all curves since + // the default for the horizontal curves is all 1 rather than an identity. + st.newLine() << pix << ".r = " << props.m_eval << "(1, " << pix << ".r, 1.);"; // HUE-SAT + st.newLine() << pix << ".g = " << props.m_eval << "(1, " << pix << ".g, 1.);"; // HUE-SAT + st.newLine() << pix << ".b = " << props.m_eval << "(1, " << pix << ".b, 1.);"; // HUE-SAT + return; + } + + if (dyn) + { + st.newLine() << "if (!" << props.m_localBypass << ")"; + st.newLine() << "{"; + st.indent(); + } + + // Add the conversion from RGB to HSY. + { + FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + switch(style) + { + case GRADING_LIN: + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + break; + case GRADING_LOG: + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LOG; + break; + case GRADING_VIDEO: + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_VID; + break; + } + + st.newLine() << "{"; // establish scope so local variable names won't conflict + st.indent(); + ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); + GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); + st.dedent(); + st.newLine() << "}"; + } + + // Apply the hue curves inverse. + + // Invert HUE-FX. + st.newLine() << pix << ".r = " << props.m_evalRevHue << "(7, " << pix << ".r, 0.);"; + + // Invert HUE-HUE. + st.newLine() << pix << ".r = " << props.m_evalRevHue << "(0, " << pix << ".r, " << pix << ".r);"; + st.newLine() << ""; + + // Use the inverted hue to calculate the HUE-SAT & HUE-LUM gains. + st.newLine() << pix << ".r = " << pix << ".r - floor( " << pix << ".r );"; + st.newLine() << "float hueSatGain = max(0., " << props.m_eval << "(1, " << pix << ".r, 1.));"; + st.newLine() << "float hueLumGain = max(0., " << props.m_eval << "(2, " << pix << ".r, 1.));"; + + // Use the output sat to calculate the SAT-LUM gain. + st.newLine() << "" << pix << ".g = max(0., " << pix << ".g);"; + st.newLine() << "float satLumGain = max(0., " << props.m_eval << "(6, " << pix << ".g, 1.));"; + + st.newLine() << ""; + st.newLine() << "hueLumGain = 1. - (1. - hueLumGain) * min( 1., " << pix << ".g );"; + + // Invert the lum gain. + if (style == GRADING_LOG) + { + // Use shift rather than scale for log mode. + st.newLine() << pix << ".b = " << pix << ".b - (hueLumGain + satLumGain - 2.) * 0.1;"; + } + else + { + // Note this is applied in linear space, for linear style. + st.newLine() << pix << ".b = " << pix << ".b / max(0.01, hueLumGain * satLumGain);"; + } + st.newLine() << ""; + + if (doLinToLog) + { + st.newLine() << "// Convert from lin to log."; + AddLinToLogShaderChannelBlue(shaderCreator, st); + st.newLine() << ""; + } + + // Invert LUM-LUM. + st.newLine() << pix << ".b = " << props.m_evalRev << "(5, " << pix << ".b, " << pix << ".b);"; + st.newLine() << ""; + + // Use it to calc the LUM-SAT gain. + st.newLine() << "float lumSatGain = max(0., " << props.m_eval << "(3, " << pix << ".b, 1.));"; + + if (doLinToLog) + { + st.newLine() << ""; + st.newLine() << "// Convert from log to lin."; + AddLogToLinShaderChannelBlue(shaderCreator, st); + } + + // Invert the sat gain. + st.newLine() << "float satGain = max(0.01, lumSatGain * hueSatGain);"; + st.newLine() << "" << pix << ".g = " << pix << ".g / satGain;"; + + // Invert SAT-SAT. + st.newLine() << "" << pix << ".g = max(0., " << props.m_evalRev << "(4, " << pix << ".g, " << pix << ".g));"; + + // Add the conversion from HSY to RGB. + { + FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + switch(style) + { + case GRADING_LIN: + hsyStyle = FixedFunctionOpData::HSY_LIN_TO_RGB; + break; + case GRADING_LOG: + hsyStyle = FixedFunctionOpData::HSY_LOG_TO_RGB; + break; + case GRADING_VIDEO: + hsyStyle = FixedFunctionOpData::HSY_VID_TO_RGB; + break; + } + st.newLine() << "{"; + st.indent(); + ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); + GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); + st.dedent(); + st.newLine() << "}"; + } + + if (dyn) + { + st.dedent(); + st.newLine() << "}"; + } +} } -void GetHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, - ConstGradingHueCurveOpDataRcPtr & gcData) +void GetGradingHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, + ConstGradingHueCurveOpDataRcPtr & gcData) { const bool dyn = gcData->isDynamic() && shaderCreator->getLanguage() != LANGUAGE_OSL_1; if (!dyn) @@ -488,8 +641,8 @@ void GetHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, case TRANSFORM_DIR_FORWARD: AddGCForwardShader(shaderCreator, st, properties, dyn, doLinToLog, gcData->getDrawCurveOnly(), style); break; - case TRANSFORM_DIR_INVERSE: // TODO: Implement the inverse shader. - //AddGCInverseShader(shaderCreator, st, properties, dyn, bypassLinToLog, style); + case TRANSFORM_DIR_INVERSE: + AddGCInverseShader(shaderCreator, st, properties, dyn, doLinToLog, gcData->getDrawCurveOnly(), style); break; } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h index 217e3f30b2..70fd5e3287 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h @@ -12,8 +12,8 @@ namespace OCIO_NAMESPACE { -void GetHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, - ConstGradingHueCurveOpDataRcPtr & gpData); +void GetGradingHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, + ConstGradingHueCurveOpDataRcPtr & gpData); } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index 8a1a3af919..f7cb46c580 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -487,9 +487,17 @@ GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size) return res; } -GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size, BSplineCurveType curveType) +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size, BSplineType splineType) { - auto newSpline = std::make_shared(size, curveType); + auto newSpline = std::make_shared(size, splineType); + GradingBSplineCurveRcPtr res = newSpline; + return res; +} + +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size, HueCurveType curveType) +{ + const BSplineType splineType = GradingHueCurve::GetBSplineTypeForHueCurveType(curveType); + auto newSpline = std::make_shared(size, splineType); GradingBSplineCurveRcPtr res = newSpline; return res; } @@ -507,9 +515,23 @@ GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values, BSplineCurveType curveType) +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values, BSplineType splineType) { - auto newSpline = std::make_shared(values.size(), curveType); + auto newSpline = std::make_shared(values.size(), splineType); + size_t i = 0; + for (const auto & c : values) + { + newSpline->getControlPoint(i++) = c; + } + GradingBSplineCurveRcPtr res; + res = newSpline; + return res; +} + +GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list values, HueCurveType curveType) +{ + const BSplineType splineType = GradingHueCurve::GetBSplineTypeForHueCurveType(curveType); + auto newSpline = std::make_shared(values.size(), splineType); size_t i = 0; for (const auto & c : values) { @@ -521,22 +543,22 @@ GradingBSplineCurveRcPtr GradingBSplineCurve::Create(std::initializer_list & controlPoints) - : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_curveType(B_SPLINE) + : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_splineType(B_SPLINE) { } -GradingBSplineCurveImpl::GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineCurveType curveType) - : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_curveType(curveType) +GradingBSplineCurveImpl::GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineType splineType) + : m_controlPoints(controlPoints), m_slopesArray(controlPoints.size(), 0.f), m_splineType(splineType) { } @@ -545,20 +567,20 @@ GradingBSplineCurveRcPtr GradingBSplineCurveImpl::createEditableCopy() const auto copy = std::make_shared(0); copy->m_controlPoints = m_controlPoints; copy->m_slopesArray = m_slopesArray; - copy->m_curveType = m_curveType; + copy->m_splineType = m_splineType; GradingBSplineCurveRcPtr res; res = copy; return res; } -BSplineCurveType GradingBSplineCurveImpl::getCurveType() const +BSplineType GradingBSplineCurveImpl::getSplineType() const { - return m_curveType; + return m_splineType; } -void GradingBSplineCurveImpl::setCurveType(BSplineCurveType curveType) +void GradingBSplineCurveImpl::setSplineType(BSplineType splineType) { - m_curveType = curveType; + m_splineType = splineType; } size_t GradingBSplineCurveImpl::getNumControlPoints() const noexcept @@ -578,7 +600,7 @@ void GradingBSplineCurveImpl::validateIndex(size_t index) const if (index >= numPoints) { std::ostringstream oss; - oss << "There are '"<< numPoints << "' control points. '" << index << "' is invalid."; + oss << "There are '"<< numPoints << "' control points. '" << index << "' is out of bounds."; throw Exception(oss.str().c_str()); } } @@ -641,29 +663,45 @@ void GradingBSplineCurveImpl::validate() const { std::ostringstream oss; oss << "Control point at index " << i << " has a x coordinate '" << x << "' that is "; - oss << "less from previous control point x cooordinate '" << lastX << "'."; + oss << "less than previous control point x cooordinate '" << lastX << "'."; throw Exception(oss.str().c_str()); } lastX = x; } + + // Don't allow only x values of 0 and 1 for periodic curves (since they are essentially only one point). + if (numPoints == 2) + { + // Periodic curve types only. + if( m_splineType == PERIODIC_1_B_SPLINE || + m_splineType == PERIODIC_0_B_SPLINE || + m_splineType == HUE_HUE_B_SPLINE ) + { + const float del_x = m_controlPoints[1].m_x - m_controlPoints[0].m_x; + if (std::abs(1.f - del_x) < 1e-3) + { + throw Exception("The periodic spline x coordinates may not wrap to the same value."); + } + } + } } bool GradingBSplineCurveImpl::isIdentity() const { bool isIdentity = true; - if( m_curveType == DIAGONAL_B_SPLINE || m_curveType == B_SPLINE || m_curveType == HUE_HUE_B_SPLINE ) + if( m_splineType == DIAGONAL_B_SPLINE || m_splineType == B_SPLINE || m_splineType == HUE_HUE_B_SPLINE ) { isIdentity = std::all_of(m_controlPoints.cbegin(), m_controlPoints.cend(), [](const GradingControlPoint& cp) { return cp.m_x == cp.m_y; }); } - else if( m_curveType == PERIODIC_0_B_SPLINE ) + else if( m_splineType == PERIODIC_0_B_SPLINE ) { isIdentity = std::all_of(m_controlPoints.cbegin(), m_controlPoints.cend(), [](const GradingControlPoint& cp) { return cp.m_y == 0.f; }); } - else if( m_curveType == HORIZONTAL1_B_SPLINE || m_curveType == PERIODIC_1_B_SPLINE ) + else if( m_splineType == HORIZONTAL1_B_SPLINE || m_splineType == PERIODIC_1_B_SPLINE ) { isIdentity = std::all_of(m_controlPoints.cbegin(), m_controlPoints.cend(), @@ -774,7 +812,7 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots const int N_IDENTITY_KNOTS = 2; const int N_IDENTITY_COEFS = 3; - + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = numKnots; knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = N_IDENTITY_KNOTS; knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = knotsCoefs.m_numCoefs; @@ -782,24 +820,23 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots knotsCoefs.m_knotsArray.begin()[numKnots] = 0.f; knotsCoefs.m_knotsArray.begin()[numKnots + 1] = 1.f; - + // Identity curve are linear or constant, so set the quadratic coefficient to zero. knotsCoefs.m_coefsArray.begin()[numCoefs] = 0.f; - + // Set the linear coefficient to match the slope of the identity curve. - const float linearCoef = m_curveType == BSplineCurveType::DIAGONAL_B_SPLINE || - m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE ? + const float linearCoef = m_splineType == BSplineType::DIAGONAL_B_SPLINE || + m_splineType == BSplineType::HUE_HUE_B_SPLINE ? 1.0f : 0.f; knotsCoefs.m_coefsArray.begin()[numCoefs + 1] = linearCoef; // Set the constant coefficient for an identity curve. - const float constantCoef = m_curveType == BSplineCurveType::PERIODIC_1_B_SPLINE || - m_curveType == BSplineCurveType::HORIZONTAL1_B_SPLINE ? + const float constantCoef = m_splineType == BSplineType::PERIODIC_1_B_SPLINE || + m_splineType == BSplineType::HORIZONTAL1_B_SPLINE ? 1.0f : 0.f; - + knotsCoefs.m_coefsArray.begin()[numCoefs + 2] = constantCoef; - knotsCoefs.m_numKnots += N_IDENTITY_KNOTS; knotsCoefs.m_numCoefs += N_IDENTITY_COEFS; @@ -808,14 +845,14 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots bool isPeriodic = false; bool isHorizontal = true; - if (m_curveType == BSplineCurveType::PERIODIC_1_B_SPLINE || - m_curveType == BSplineCurveType::PERIODIC_0_B_SPLINE || - m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE) + if (m_splineType == BSplineType::PERIODIC_1_B_SPLINE || + m_splineType == BSplineType::PERIODIC_0_B_SPLINE || + m_splineType == BSplineType::HUE_HUE_B_SPLINE) { isPeriodic = true; } - if (m_curveType == BSplineCurveType::DIAGONAL_B_SPLINE || - m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE) + if (m_splineType == BSplineType::DIAGONAL_B_SPLINE || + m_splineType == BSplineType::HUE_HUE_B_SPLINE) { isHorizontal = false; } @@ -824,7 +861,7 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots PrepHueCurveData(m_controlPoints, resultCtrlPnts, isPeriodic, isHorizontal); // For the purposes of slope estimation, consider the hue-hue spline to be horizontal. - if (m_curveType == BSplineCurveType::HUE_HUE_B_SPLINE) + if (m_splineType == BSplineType::HUE_HUE_B_SPLINE) { isHorizontal = true; } @@ -885,7 +922,7 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int curveIdx) const { - if(m_curveType == BSplineCurveType::B_SPLINE) + if(m_splineType == BSplineType::B_SPLINE) { computeKnotsAndCoefsForRGBCurve(knotsCoefs, curveIdx); } @@ -895,127 +932,214 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int } } -void GradingBSplineCurveImpl::AddShaderEval(GpuShaderText & st, - const std::string & knotsOffsets, - const std::string & coefsOffsets, - const std::string & knots, - const std::string & coefs, bool isInv) +void GradingBSplineCurveImpl::AddShaderEvalFwd(GpuShaderText & st, + const std::string & knotsOffsets, + const std::string & coefsOffsets, + const std::string & knots, + const std::string & coefs) { - if (!isInv) // Forward - { - st.newLine() << "int knotsOffs = " << knotsOffsets << "[curveIdx * 2];"; - st.newLine() << "int knotsCnt = " << knotsOffsets << "[curveIdx * 2 + 1];"; - st.newLine() << "int coefsOffs = " << coefsOffsets << "[curveIdx * 2];"; - st.newLine() << "int coefsCnt = " << coefsOffsets << "[curveIdx * 2 + 1];"; - st.newLine() << "int coefsSets = coefsCnt / 3;"; - // If the curve has the default/identity values the coef data is empty, so return the input. - st.newLine() << "if (coefsSets == 0)"; - st.newLine() << "{"; - st.newLine() << " return identity_x;"; - st.newLine() << "}"; - - st.newLine() << "float knStart = " << knots << "[knotsOffs];"; - st.newLine() << "float knEnd = " << knots << "[knotsOffs + knotsCnt - 1];"; - - st.newLine() << "if (x <= knStart)"; - st.newLine() << "{"; - st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets];"; - st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 2];"; - st.newLine() << " return (x - knStart) * B + C;"; - st.newLine() << "}"; - - st.newLine() << "else if (x >= knEnd)"; - st.newLine() << "{"; - st.newLine() << " float A = " << coefs << "[coefsOffs + coefsSets - 1];"; - st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets * 2 - 1];"; - st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 3 - 1];"; - st.newLine() << " float kn = " << knots << "[knotsOffs + knotsCnt - 2];"; - st.newLine() << " float t = knEnd - kn;"; - st.newLine() << " float slope = 2. * A * t + B;"; - st.newLine() << " float offs = ( A * t + B ) * t + C;"; - st.newLine() << " return (x - knEnd) * slope + offs;"; - st.newLine() << "}"; - - // else - st.newLine() << "int i = 0;"; - st.newLine() << "for (i = 0; i < knotsCnt - 2; ++i)"; - st.newLine() << "{"; - st.newLine() << " if (x < " << knots << "[knotsOffs + i + 1])"; - st.newLine() << " {"; - st.newLine() << " break;"; - st.newLine() << " }"; - st.newLine() << "}"; - - st.newLine() << "float A = " << coefs << "[coefsOffs + i];"; - st.newLine() << "float B = " << coefs << "[coefsOffs + coefsSets + i];"; - st.newLine() << "float C = " << coefs << "[coefsOffs + coefsSets * 2 + i];"; - st.newLine() << "float kn = " << knots << "[knotsOffs + i];"; - st.newLine() << "float t = x - kn;"; - st.newLine() << "return ( A * t + B ) * t + C;"; - } - else // Inverse - { - st.newLine() << "int knotsOffs = " << knotsOffsets << "[curveIdx * 2];"; - st.newLine() << "int knotsCnt = " << knotsOffsets << "[curveIdx * 2 + 1];"; - st.newLine() << "int coefsOffs = " << coefsOffsets << "[curveIdx * 2];"; - st.newLine() << "int coefsCnt = " << coefsOffsets << "[curveIdx * 2 + 1];"; - st.newLine() << "int coefsSets = coefsCnt / 3;"; - // If the curve has the default/identity values the coef data is empty, so return the input. - st.newLine() << "if (coefsSets == 0)"; - st.newLine() << "{"; - st.newLine() << " return identity_x;"; - st.newLine() << "}"; - - st.newLine() << "float knStart = " << knots << "[knotsOffs];"; - st.newLine() << "float knEnd = " << knots << "[knotsOffs + knotsCnt - 1];"; - st.newLine() << "float knStartY = " << coefs << "[coefsOffs + coefsSets * 2];"; - st.newLine() << "float knEndY;"; - st.newLine() << "{"; - st.newLine() << " float A = " << coefs << "[coefsOffs + coefsSets - 1];"; - st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets * 2 - 1];"; - st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 3 - 1];"; - st.newLine() << " float kn = " << knots << "[knotsOffs + knotsCnt - 2];"; - st.newLine() << " float t = knEnd - kn;"; - st.newLine() << " knEndY = ( A * t + B ) * t + C;"; - st.newLine() << "}"; - - st.newLine() << "if (x <= knStartY)"; - st.newLine() << "{"; - st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets];"; - st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 2];"; - st.newLine() << " return abs(B) < 1e-5 ? knStart : (x - C) / B + knStart;"; - st.newLine() << "}"; - - st.newLine() << "else if (x >= knEndY)"; - st.newLine() << "{"; - st.newLine() << " float A = " << coefs << "[coefsOffs + coefsSets - 1];"; - st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets * 2 - 1];"; - st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 3 - 1];"; - st.newLine() << " float kn = " << knots << "[knotsOffs + knotsCnt - 2];"; - st.newLine() << " float t = knEnd - kn;"; - st.newLine() << " float slope = 2. * A * t + B;"; - st.newLine() << " float offs = ( A * t + B ) * t + C;"; - st.newLine() << " return abs(slope) < 1e-5 ? knEnd : (x - offs) / slope + knEnd;"; - st.newLine() << "}"; - - // else - st.newLine() << "int i = 0;"; - st.newLine() << "for (i = 0; i < knotsCnt - 2; ++i)"; - st.newLine() << "{"; - st.newLine() << " if (x < " << coefs << "[coefsOffs + coefsSets * 2 + i + 1])"; - st.newLine() << " {"; - st.newLine() << " break;"; - st.newLine() << " }"; - st.newLine() << "}"; - - st.newLine() << "float A = " << coefs << "[coefsOffs + i];"; - st.newLine() << "float B = " << coefs << "[coefsOffs + coefsSets + i];"; - st.newLine() << "float C = " << coefs << "[coefsOffs + coefsSets * 2 + i];"; - st.newLine() << "float kn = " << knots << "[knotsOffs + i];"; - st.newLine() << "float C0 = C - x;"; - st.newLine() << "float discrim = sqrt(B * B - 4. * A * C0);"; - st.newLine() << "return kn + (-2. * C0) / (discrim + B);"; - } + // See GradingHue/RGBCurveOpGPU.cpp:AddCurveEvalMethodTextToShaderProgram. + // The input arguments are: + // curveIdx -- The index of the curve being evaluated. + // x -- The input value. + // identity_x -- The desired output if there is no curve to evaluate. + + st.newLine() << "int knotsOffs = " << knotsOffsets << "[curveIdx * 2];"; + st.newLine() << "int knotsCnt = " << knotsOffsets << "[curveIdx * 2 + 1];"; + st.newLine() << "int coefsOffs = " << coefsOffsets << "[curveIdx * 2];"; + st.newLine() << "int coefsCnt = " << coefsOffsets << "[curveIdx * 2 + 1];"; + st.newLine() << "int coefsSets = coefsCnt / 3;"; + // If the curve has the default/identity values, the coef data is empty, return the identity. + st.newLine() << "if (coefsSets == 0)"; + st.newLine() << "{"; + st.newLine() << " return identity_x;"; + st.newLine() << "}"; + + st.newLine() << "float knStart = " << knots << "[knotsOffs];"; + st.newLine() << "float knEnd = " << knots << "[knotsOffs + knotsCnt - 1];"; + + st.newLine() << "if (x <= knStart)"; + st.newLine() << "{"; + st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets];"; + st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 2];"; + st.newLine() << " return (x - knStart) * B + C;"; + st.newLine() << "}"; + + st.newLine() << "else if (x >= knEnd)"; + st.newLine() << "{"; + st.newLine() << " float A = " << coefs << "[coefsOffs + coefsSets - 1];"; + st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets * 2 - 1];"; + st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 3 - 1];"; + st.newLine() << " float kn = " << knots << "[knotsOffs + knotsCnt - 2];"; + st.newLine() << " float t = knEnd - kn;"; + st.newLine() << " float slope = 2. * A * t + B;"; + st.newLine() << " float offs = ( A * t + B ) * t + C;"; + st.newLine() << " return (x - knEnd) * slope + offs;"; + st.newLine() << "}"; + + // else + st.newLine() << "int i = 0;"; + st.newLine() << "for (i = 0; i < knotsCnt - 2; ++i)"; + st.newLine() << "{"; + st.newLine() << " if (x < " << knots << "[knotsOffs + i + 1])"; + st.newLine() << " {"; + st.newLine() << " break;"; + st.newLine() << " }"; + st.newLine() << "}"; + + st.newLine() << "float A = " << coefs << "[coefsOffs + i];"; + st.newLine() << "float B = " << coefs << "[coefsOffs + coefsSets + i];"; + st.newLine() << "float C = " << coefs << "[coefsOffs + coefsSets * 2 + i];"; + st.newLine() << "float kn = " << knots << "[knotsOffs + i];"; + st.newLine() << "float t = x - kn;"; + st.newLine() << "return ( A * t + B ) * t + C;"; +} + +void GradingBSplineCurveImpl::AddShaderEvalRev(GpuShaderText & st, + const std::string & knotsOffsets, + const std::string & coefsOffsets, + const std::string & knots, + const std::string & coefs) +{ + // See GradingHue/RGBCurveOpGPU.cpp:AddCurveEvalMethodTextToShaderProgram. + // The input arguments are: + // curveIdx -- The index of the curve being evaluated. + // x -- The input value. + // identity_x -- The desired output if there is no curve to evaluate. + + st.newLine() << "int knotsOffs = " << knotsOffsets << "[curveIdx * 2];"; + st.newLine() << "int knotsCnt = " << knotsOffsets << "[curveIdx * 2 + 1];"; + st.newLine() << "int coefsOffs = " << coefsOffsets << "[curveIdx * 2];"; + st.newLine() << "int coefsCnt = " << coefsOffsets << "[curveIdx * 2 + 1];"; + st.newLine() << "int coefsSets = coefsCnt / 3;"; + + st.newLine() << "if (coefsSets == 0)"; + st.newLine() << "{"; + st.newLine() << " return identity_x;"; + st.newLine() << "}"; + + st.newLine() << "float knStart = " << knots << "[knotsOffs];"; + st.newLine() << "float knEnd = " << knots << "[knotsOffs + knotsCnt - 1];"; + st.newLine() << "float knStartY = " << coefs << "[coefsOffs + coefsSets * 2];"; + st.newLine() << "float knEndY;"; + st.newLine() << "{"; + st.newLine() << " float A = " << coefs << "[coefsOffs + coefsSets - 1];"; + st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets * 2 - 1];"; + st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 3 - 1];"; + st.newLine() << " float kn = " << knots << "[knotsOffs + knotsCnt - 2];"; + st.newLine() << " float t = knEnd - kn;"; + st.newLine() << " knEndY = ( A * t + B ) * t + C;"; + st.newLine() << "}"; + + st.newLine() << "if (x <= knStartY)"; + st.newLine() << "{"; + st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets];"; + st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 2];"; + st.newLine() << " return abs(B) < 1e-5 ? knStart : (x - C) / B + knStart;"; + st.newLine() << "}"; + + st.newLine() << "else if (x >= knEndY)"; + st.newLine() << "{"; + st.newLine() << " float A = " << coefs << "[coefsOffs + coefsSets - 1];"; + st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets * 2 - 1];"; + st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 3 - 1];"; + st.newLine() << " float kn = " << knots << "[knotsOffs + knotsCnt - 2];"; + st.newLine() << " float t = knEnd - kn;"; + st.newLine() << " float slope = 2. * A * t + B;"; + st.newLine() << " float offs = ( A * t + B ) * t + C;"; + st.newLine() << " return abs(slope) < 1e-5 ? knEnd : (x - offs) / slope + knEnd;"; + st.newLine() << "}"; + + // else + st.newLine() << "int i = 0;"; + st.newLine() << "for (i = 0; i < knotsCnt - 2; ++i)"; + st.newLine() << "{"; + st.newLine() << " if (x < " << coefs << "[coefsOffs + coefsSets * 2 + i + 1])"; + st.newLine() << " {"; + st.newLine() << " break;"; + st.newLine() << " }"; + st.newLine() << "}"; + + st.newLine() << "float A = " << coefs << "[coefsOffs + i];"; + st.newLine() << "float B = " << coefs << "[coefsOffs + coefsSets + i];"; + st.newLine() << "float C = " << coefs << "[coefsOffs + coefsSets * 2 + i];"; + st.newLine() << "float kn = " << knots << "[knotsOffs + i];"; + st.newLine() << "float C0 = C - x;"; + st.newLine() << "float discrim = sqrt(B * B - 4. * A * C0);"; + st.newLine() << "return kn + (-2. * C0) / (discrim + B);"; +} + +void GradingBSplineCurveImpl::AddShaderEvalRevHue(GpuShaderText & st, + const std::string & knotsOffsets, + const std::string & coefsOffsets, + const std::string & knots, + const std::string & coefs) +{ + // See GradingHueCurveOpGPU.cpp:AddCurveEvalMethodTextToShaderProgram. + // The input arguments are: + // curveIdx -- The index of the curve being evaluated. + // x -- The input value. + // identity_x -- The desired output if there is no curve to evaluate. + + st.newLine() << "int knotsOffs = " << knotsOffsets << "[curveIdx * 2];"; + st.newLine() << "int knotsCnt = " << knotsOffsets << "[curveIdx * 2 + 1];"; + st.newLine() << "int coefsOffs = " << coefsOffsets << "[curveIdx * 2];"; + st.newLine() << "int coefsCnt = " << coefsOffsets << "[curveIdx * 2 + 1];"; + st.newLine() << "int coefsSets = coefsCnt / 3;"; + + st.newLine() << "if (coefsSets == 0)"; + st.newLine() << "{"; + st.newLine() << " return identity_x;"; + st.newLine() << "}"; + + st.newLine() << "float knStart = " << knots << "[knotsOffs];"; + st.newLine() << "float knEnd = " << knots << "[knotsOffs + knotsCnt - 1];"; + st.newLine() << "float knStartY = " << coefs << "[coefsOffs + coefsSets * 2];"; + st.newLine() << "float knEndY;"; + st.newLine() << "{"; + st.newLine() << " float A = " << coefs << "[coefsOffs + coefsSets - 1];"; + st.newLine() << " float B = " << coefs << "[coefsOffs + coefsSets * 2 - 1];"; + st.newLine() << " float C = " << coefs << "[coefsOffs + coefsSets * 3 - 1];"; + st.newLine() << " float kn = " << knots << "[knotsOffs + knotsCnt - 2];"; + st.newLine() << " float t = knEnd - kn;"; + st.newLine() << " knEndY = ( A * t + B ) * t + C;"; + // The HUE-FX curve is index 7 and requires special handling. + st.newLine() << " knEndY = (curveIdx == 7) ? knEndY + knEnd : knEndY;"; + st.newLine() << "}"; + + st.newLine() << "if (x < knStartY)"; + st.newLine() << "{"; + st.newLine() << " x = x + ceil(knStartY - x);"; + st.newLine() << "}"; + + st.newLine() << "else if (x > knEndY)"; + st.newLine() << "{"; + st.newLine() << " x = x - ceil(x - knEndY);"; + st.newLine() << "}"; + + st.newLine() << "int i = 0;"; + st.newLine() << "for (i = 0; i < knotsCnt - 2; ++i)"; + st.newLine() << "{"; + st.newLine() << " float curve_x = " << coefs << "[coefsOffs + coefsSets * 2 + i + 1];"; + st.newLine() << " curve_x = (curveIdx == 7) ? curve_x + " << knots << "[knotsOffs + i + 1] : curve_x;"; + st.newLine() << " if (x < curve_x)"; + st.newLine() << " {"; + st.newLine() << " break;"; + st.newLine() << " }"; + st.newLine() << "}"; + + st.newLine() << "float A = " << coefs << "[coefsOffs + i];"; + st.newLine() << "float B = " << coefs << "[coefsOffs + coefsSets + i];"; + st.newLine() << "float C = " << coefs << "[coefsOffs + coefsSets * 2 + i];"; + st.newLine() << "float kn = " << knots << "[knotsOffs + i];"; + st.newLine() << "if (curveIdx == 7)"; + st.newLine() << "{"; + st.newLine() << " C = C + kn;"; + st.newLine() << " B = B + 1.;"; + st.newLine() << "}"; + st.newLine() << "float C0 = C - x;"; + st.newLine() << "float discrim = sqrt(B * B - 4. * A * C0);"; + st.newLine() << "return kn + (-2. * C0) / (discrim + B);"; } GradingBSplineCurveImpl::KnotsCoefs::KnotsCoefs(size_t numCurves) @@ -1030,6 +1154,9 @@ GradingBSplineCurveImpl::KnotsCoefs::KnotsCoefs(size_t numCurves) // TODO: Update to return hue curve identity value. float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x) const { + // NB: When evaluating hue curves, x should be wrapped to [0,1) by the caller + // so there is no extrapolation. + const int coefsSets = m_coefsOffsetsArray[2 * c + 1] / 3; if (coefsSets == 0) { @@ -1079,6 +1206,11 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x) const // TODO: Update to return hue curve identity value. float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const { + // Note: This is only intended to invert the monotonic curve types. + // The horizontal curve types only need to be evaluated in the forward + // direction, even when inverting the hue curve transform. The exception + // is the HueFX curve but that has its own inversion function below. + const int coefsSets = m_coefsOffsetsArray[2 * c + 1] / 3; if (coefsSets == 0) { @@ -1103,12 +1235,14 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const if (y <= knStartY) { + // Extrapolate low side. const float B = m_coefsArray[coefsOffs + coefsSets]; const float C = m_coefsArray[coefsOffs + coefsSets * 2]; return std::fabs(B) < 1e-5f ? knStart : (y - C) / B + knStart; } else if (y >= knEndY) { + // Extrapolate high side. const float A = m_coefsArray[coefsOffs + coefsSets - 1]; const float B = m_coefsArray[coefsOffs + coefsSets * 2 - 1]; const float C = m_coefsArray[coefsOffs + coefsSets * 3 - 1]; @@ -1136,6 +1270,74 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const } } +float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRevHue(int c, float y, bool isHfx) const +{ + // This function is specifically to invert the HueFX and Hue-Hue curve types. + // + // The output of the HueFX curve is a "delta hue" signal that is + // added on to the incoming hue: HueOut = HueIn + HueFX(HueIn). + // The input to this function should be the HueOut, in other words, + // the sum of the HueIn and delta hue. It returns HueIn. + + const int coefsSets = m_coefsOffsetsArray[2 * c + 1] / 3; + if (coefsSets == 0) + { + return y; + } + const int coefsOffs = m_coefsOffsetsArray[2 * c]; + const int knotsCnt = m_knotsOffsetsArray[2 * c + 1]; + const int knotsOffs = m_knotsOffsetsArray[2 * c]; + + const float knStart = m_knotsArray[knotsOffs]; + const float knEnd = m_knotsArray[knotsOffs + knotsCnt - 1]; + float knStartY = m_coefsArray[coefsOffs + coefsSets * 2]; + knStartY = isHfx ? knStartY + knStart : knStartY; + float knEndY; + { + const float A = m_coefsArray[coefsOffs + coefsSets - 1]; + const float B = m_coefsArray[coefsOffs + coefsSets * 2 - 1]; + const float C = m_coefsArray[coefsOffs + coefsSets * 3 - 1]; + const float kn = m_knotsArray[knotsOffs + knotsCnt - 2]; + const float t = knEnd - kn; + knEndY = (A * t + B) * t + C; + knEndY = isHfx ? knEndY + knEnd : knEndY; + } + + if (y < knStartY) + { + // Wrap up into the valid hue range. + y += std::ceil(knStartY - y); + } + else if (y > knEndY) + { + // Wrap down into the valid hue range. + y -= std::ceil(y - knEndY); + } + + int i; + for (i = 0; i < knotsCnt - 2; ++i) + { + float curve_y = m_coefsArray[coefsOffs + coefsSets * 2 + i + 1]; + curve_y = isHfx ? curve_y + m_knotsArray[knotsOffs + i + 1] : curve_y; + + if (y < curve_y) + break; + } + const float A = m_coefsArray[coefsOffs + i]; + float B = m_coefsArray[coefsOffs + coefsSets + i]; + float C = m_coefsArray[coefsOffs + coefsSets * 2 + i]; + const float kn = m_knotsArray[knotsOffs + i]; + if (isHfx) + { + C += kn; // shift curve up so left edge is on the main diagonal + B += 1.f; // add diagonal line + } + const float C0 = C - y; + const float discrim = sqrt(B * B - 4.f * A * C0); + return kn + (-2.f * C0) / (discrim + B); + // Note: The caller should wrap to ensure the output is a hue on [0,1). +} + bool operator==(const GradingControlPoint & lhs, const GradingControlPoint & rhs) { return lhs.m_x == rhs.m_x && lhs.m_y == rhs.m_y; @@ -1148,12 +1350,18 @@ bool operator!=(const GradingControlPoint & lhs, const GradingControlPoint & rhs bool operator==(const GradingBSplineCurve & lhs, const GradingBSplineCurve & rhs) { + if (lhs.getSplineType() != rhs.getSplineType()) + { + return false; + } + const auto num = lhs.getNumControlPoints(); if (num == rhs.getNumControlPoints()) { for (size_t i = 0; i < num; ++i) { - if (lhs.getControlPoint(i) != rhs.getControlPoint(i)) + if ( lhs.getControlPoint(i) != rhs.getControlPoint(i) || + lhs.getSlope(i) != rhs.getSlope(i) ) { return false; } diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h index 4b0e36927f..edd36392be 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h @@ -18,14 +18,14 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve { public: explicit GradingBSplineCurveImpl(size_t size); - explicit GradingBSplineCurveImpl(size_t size, BSplineCurveType curveType); + explicit GradingBSplineCurveImpl(size_t size, BSplineType splineType); GradingBSplineCurveImpl(const std::vector & controlPoints); - GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineCurveType curveType); + GradingBSplineCurveImpl(const std::vector & controlPoints, BSplineType splineType); ~GradingBSplineCurveImpl() = default; GradingBSplineCurveRcPtr createEditableCopy() const override; - BSplineCurveType getCurveType() const override; - void setCurveType(BSplineCurveType curveType) override; + BSplineType getSplineType() const override; + void setSplineType(BSplineType splineType) override; size_t getNumControlPoints() const noexcept override; void setNumControlPoints(size_t size) override; const GradingControlPoint & getControlPoint(size_t index) const override; @@ -88,15 +88,17 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve // control points but the number of knots may be, at most, the number of control // points * 2 - 1. // - // There are 4 RGB curves (R, G, B, M) each represented by one RGBCurve. We want to keep - // the total for two curves well below the 200 knot, 600 coef limit. - // (TODO: 6 Hue curves (H/H, H/S, H/L, L/S, L/L, S/S)). + // There are four spline curves (R, G, B, M) in an RGBCurve and eight in a HueCurve. + // We want to keep the total for two instances well below the 200 knot, 600 coef limit. // // A value of 60 knots would allow about 30 control points spread across the 4 or 6 curves. // Note that the default RGB curves use 3 control points each and the hue curves may use as // many as 6 even for the default. However, there is an optimization below that does not // add knots for curves that are simply identity. // + // UPDATE: Hardware has improved since this was introduced. For OCIO 2.5, double the + // allowed knots/coefs from 60/180 to 120/360. + // // Maximum size of the knots array (for ALL curves). static constexpr int MAX_NUM_KNOTS = 120; // Maximum size of the coefs array (for ALL curves). @@ -111,15 +113,22 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve float evalCurve(int curveIdx, float x) const; float evalCurveRev(int curveIdx, float x) const; + float evalCurveRevHue(int c, float y, bool isHfx) const; }; // Compute knots and coefs for a curve and add result to knotsCoefs. It has to be called for // each curve using a given curve order. void computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int curveIdx) const; - static void AddShaderEval(GpuShaderText & st, - const std::string & knotsOffsets, const std::string & coefsOffsets, - const std::string & knots, const std::string & coefs, bool isInv); + static void AddShaderEvalFwd(GpuShaderText & st, + const std::string & knotsOffsets, const std::string & coefsOffsets, + const std::string & knots, const std::string & coefs); + static void AddShaderEvalRev(GpuShaderText & st, + const std::string & knotsOffsets, const std::string & coefsOffsets, + const std::string & knots, const std::string & coefs); + static void AddShaderEvalRevHue(GpuShaderText & st, + const std::string & knotsOffsets, const std::string & coefsOffsets, + const std::string & knots, const std::string & coefs); private: void computeKnotsAndCoefsForRGBCurve(KnotsCoefs & knotsCoefs, int curveIdx) const; void computeKnotsAndCoefsForHueCurve(KnotsCoefs & knotsCoefs, int curveIdx) const; @@ -129,7 +138,7 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve std::vector m_controlPoints; std::vector m_slopesArray; // Optional slope values for the control points. - BSplineCurveType m_curveType = BSplineCurveType::B_SPLINE; + BSplineType m_splineType = BSplineType::B_SPLINE; }; bool IsGradingCurveIdentity(const ConstGradingBSplineCurveRcPtr & curve); diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp index 6703092523..c7f382fd7c 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp @@ -237,11 +237,16 @@ void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator } st.newLine() << "{"; st.indent(); - - const bool isInv = gcData->getDirection() == TRANSFORM_DIR_INVERSE; - GradingBSplineCurveImpl::AddShaderEval(st, props.m_knotsOffsets, props.m_coefsOffsets, - props.m_knots, props.m_coefs, isInv); - + if (gcData->getDirection() == TRANSFORM_DIR_INVERSE) + { + GradingBSplineCurveImpl::AddShaderEvalRev(st, props.m_knotsOffsets, props.m_coefsOffsets, + props.m_knots, props.m_coefs); + } + else + { + GradingBSplineCurveImpl::AddShaderEvalFwd(st, props.m_knotsOffsets, props.m_coefsOffsets, + props.m_knots, props.m_coefs); + } st.dedent(); st.newLine() << "}"; diff --git a/src/bindings/python/CMakeLists.txt b/src/bindings/python/CMakeLists.txt index 94af56302e..978b6253d1 100644 --- a/src/bindings/python/CMakeLists.txt +++ b/src/bindings/python/CMakeLists.txt @@ -68,6 +68,7 @@ set(SOURCES transforms/PyFixedFunctionTransform.cpp transforms/PyGradingPrimaryTransform.cpp transforms/PyGradingRGBCurveTransform.cpp + transforms/PyGradingHueCurveTransform.cpp transforms/PyGradingToneTransform.cpp transforms/PyGroupTransform.cpp transforms/PyLogAffineTransform.cpp diff --git a/src/bindings/python/PyDynamicProperty.cpp b/src/bindings/python/PyDynamicProperty.cpp index c163161eac..626a5883de 100644 --- a/src/bindings/python/PyDynamicProperty.cpp +++ b/src/bindings/python/PyDynamicProperty.cpp @@ -27,6 +27,10 @@ void bindPyDynamicProperty(py::module & m) DOC(DynamicPropertyValue, AsGradingRGBCurve)) .def("setGradingRGBCurve", &PyDynamicProperty::setGradingRGBCurve, "val"_a, DOC(DynamicPropertyValue, AsGradingRGBCurve)) + .def("getGradingHueCurve", &PyDynamicProperty::getGradingHueCurve, + DOC(DynamicPropertyValue, AsGradingHueCurve)) + .def("setGradingHueCurve", &PyDynamicProperty::setGradingHueCurve, "val"_a, + DOC(DynamicPropertyValue, AsGradingHueCurve)) .def("getGradingTone", &PyDynamicProperty::getGradingTone, DOC(DynamicPropertyValue, AsGradingTone)) .def("setGradingTone", &PyDynamicProperty::setGradingTone, "val"_a, diff --git a/src/bindings/python/PyDynamicProperty.h b/src/bindings/python/PyDynamicProperty.h index 3180ed6a54..2115cfaf4d 100644 --- a/src/bindings/python/PyDynamicProperty.h +++ b/src/bindings/python/PyDynamicProperty.h @@ -84,6 +84,26 @@ struct OCIOHIDDEN PyDynamicProperty throw OCIO::Exception("Invalid dynamic property type (doesn't accept a GradingRGBCurve)."); } + const ConstGradingHueCurveRcPtr & getGradingHueCurve() + { + auto propGC = DynamicPropertyValue::AsGradingHueCurve(m_prop); + if (propGC) + { + return propGC->getValue(); + } + throw OCIO::Exception("Invalid dynamic property type (doesn't hold a GradingHueCurve)."); + } + + void setGradingHueCurve(const ConstGradingHueCurveRcPtr & v) + { + auto propGC = DynamicPropertyValue::AsGradingHueCurve(m_prop); + if (propGC) + { + return propGC->setValue(v); + } + throw OCIO::Exception("Invalid dynamic property type (doesn't accept a GradingHueCurve)."); + } + const GradingTone & getGradingTone() { auto propGT = DynamicPropertyValue::AsGradingTone(m_prop); diff --git a/src/bindings/python/PyGradingData.cpp b/src/bindings/python/PyGradingData.cpp index 815da41140..690733a422 100644 --- a/src/bindings/python/PyGradingData.cpp +++ b/src/bindings/python/PyGradingData.cpp @@ -68,6 +68,10 @@ void bindPyGradingData(py::module & m) py::class_( m.attr("GradingRGBCurve")); + auto clsGradingHueCurve = + py::class_( + m.attr("GradingHueCurve")); + clsGradingRGBM .def(py::init<>(), DOC(GradingRGBM, GradingRGBM)) @@ -247,6 +251,22 @@ void bindPyGradingData(py::module & m) }), "size"_a, DOC(GradingBSplineCurve, Create)) + .def(py::init([](size_t size, + HueCurveType curveType) + { + return GradingBSplineCurve::Create(size, curveType); + }), + "size"_a, + "huecurvetype"_a, + DOC(GradingBSplineCurve, Create)) + .def(py::init([](size_t size, + BSplineType splineType) + { + return GradingBSplineCurve::Create(size, splineType); + }), + "size"_a, + "splinetype"_a, + DOC(GradingBSplineCurve, Create)) .def(py::init([](const std::vector & values) { const auto size = values.size(); @@ -269,6 +289,29 @@ void bindPyGradingData(py::module & m) return c; }), DOC(GradingBSplineCurve, Create, 2)) + .def(py::init([](const std::vector & values, + HueCurveType curveType) + { + const auto size = values.size(); + if (size < 4) + { + throw Exception("GradingBSpline needs at least 4 values."); + } + if (size % 2 != 0) + { + throw Exception("GradingBSpline needs an even number of values."); + } + auto numCtrlPts = size / 2; + + GradingBSplineCurveRcPtr c = GradingBSplineCurve::Create(numCtrlPts, curveType); + for (size_t p = 0; p < numCtrlPts; ++p) + { + c->getControlPoint(p).m_x = values[2 * p]; + c->getControlPoint(p).m_y = values[2 * p + 1]; + } + return c; + }), + DOC(GradingBSplineCurve, Create, 2)) .def("__eq__", [](const GradingBSplineCurve &self, const GradingBSplineCurve &other) { @@ -286,7 +329,39 @@ void bindPyGradingData(py::module & m) .def("getControlPoints", [](GradingBSplineCurveRcPtr & self) { return GradingControlPointIterator(self); - }); + }) + .def("getSplineType", &GradingBSplineCurve::getSplineType, + DOC(GradingBSplineCurve, getSplineType)) + .def("setSplineType", &GradingBSplineCurve::setSplineType, "splinetype"_a, + DOC(GradingBSplineCurve, setSplineType)) + .def("slopesAreDefault", &GradingBSplineCurve::slopesAreDefault, + DOC(GradingBSplineCurve, slopesAreDefault)) + .def("getSlopes", [](GradingBSplineCurveRcPtr self) + { + std::vector slopes; + const size_t numPts = self->getNumControlPoints(); + slopes.resize(numPts); + for (size_t pt = 0; pt < numPts; ++pt) + { + slopes[pt] = self->getSlope(pt); + } + return slopes; + }, + DOC(GradingBSplineCurve, getSlopes)) + .def("setSlopes", [](GradingBSplineCurveRcPtr self, const std::vector & slopes) + { + const size_t numPts = self->getNumControlPoints(); + const auto size = slopes.size(); + if (size != numPts) + { + throw Exception("Length of slopes vector must match number of control points."); + } + for (size_t pt = 0; pt < numPts; ++pt) + { + self->setSlope(pt, slopes[pt]); + } + }, + DOC(GradingBSplineCurve, getSlopes)); defRepr(clsGradingBSplineCurve); @@ -345,6 +420,11 @@ void bindPyGradingData(py::module & m) return self != other; }, py::is_operator()) + .def("validate", &GradingRGBCurve::validate, + DOC(GradingRGBCurve, validate)) + .def("isIdentity", &GradingRGBCurve::isIdentity, + DOC(GradingRGBCurve, isIdentity)) + .def_property("red", [](const GradingRGBCurveRcPtr & rgbCurve) { @@ -386,7 +466,125 @@ void bindPyGradingData(py::module & m) CopyGradingBSpline(rgbCurve->getCurve(RGB_MASTER), master); }); - defRepr(clsGradingRGBCurve); + defRepr(clsGradingHueCurve); + + clsGradingHueCurve + .def(py::init([](GradingStyle style) + { + return GradingHueCurve::Create(style); + }), + "style"_a, + DOC(GradingHueCurve, GradingHueCurve)) + .def(py::init([](const GradingBSplineCurveRcPtr & hue_hue, + const GradingBSplineCurveRcPtr & hue_sat, + const GradingBSplineCurveRcPtr & hue_lum, + const GradingBSplineCurveRcPtr & lum_sat, + const GradingBSplineCurveRcPtr & sat_sat, + const GradingBSplineCurveRcPtr & lum_lum, + const GradingBSplineCurveRcPtr & sat_lum, + const GradingBSplineCurveRcPtr & hue_fx) + { + return GradingHueCurve::Create(hue_hue, hue_sat, hue_lum, lum_sat, + sat_sat, lum_lum, sat_lum, hue_fx); + }), + DOC(GradingHueCurve, GradingHueCurve, 2)) + + .def("__eq__", [](const GradingHueCurve &self, const GradingHueCurve &other) + { + return self == other; + }, py::is_operator()) + .def("__ne__", [](const GradingHueCurve &self, const GradingHueCurve &other) + { + return self != other; + }, py::is_operator()) + + .def("validate", &GradingHueCurve::validate, + DOC(GradingHueCurve, validate)) + .def("isIdentity", &GradingHueCurve::isIdentity, + DOC(GradingHueCurve, isIdentity)) + + .def_property("hue_hue", + [](const GradingHueCurveRcPtr & hueCurve) + { + return hueCurve->getCurve(HUE_HUE); + }, + [](const GradingHueCurveRcPtr & hueCurve, + const GradingBSplineCurveRcPtr & hue_hue) + { + CopyGradingBSpline(hueCurve->getCurve(HUE_HUE), hue_hue); + }) + .def_property("hue_sat", + [](const GradingHueCurveRcPtr & hueCurve) + { + return hueCurve->getCurve(HUE_SAT); + }, + [](const GradingHueCurveRcPtr & hueCurve, + const GradingBSplineCurveRcPtr & hue_sat) + { + CopyGradingBSpline(hueCurve->getCurve(HUE_SAT), hue_sat); + }) + .def_property("hue_lum", + [](const GradingHueCurveRcPtr & hueCurve) + { + return hueCurve->getCurve(HUE_LUM); + }, + [](const GradingHueCurveRcPtr & hueCurve, + const GradingBSplineCurveRcPtr & hue_lum) + { + CopyGradingBSpline(hueCurve->getCurve(HUE_LUM), hue_lum); + }) + .def_property("lum_sat", + [](const GradingHueCurveRcPtr & hueCurve) + { + return hueCurve->getCurve(LUM_SAT); + }, + [](const GradingHueCurveRcPtr & hueCurve, + const GradingBSplineCurveRcPtr & lum_sat) + { + CopyGradingBSpline(hueCurve->getCurve(LUM_SAT), lum_sat); + }) + .def_property("sat_sat", + [](const GradingHueCurveRcPtr & hueCurve) + { + return hueCurve->getCurve(SAT_SAT); + }, + [](const GradingHueCurveRcPtr & hueCurve, + const GradingBSplineCurveRcPtr & sat_sat) + { + CopyGradingBSpline(hueCurve->getCurve(SAT_SAT), sat_sat); + }) + .def_property("lum_lum", + [](const GradingHueCurveRcPtr & hueCurve) + { + return hueCurve->getCurve(LUM_LUM); + }, + [](const GradingHueCurveRcPtr & hueCurve, + const GradingBSplineCurveRcPtr & lum_lum) + { + CopyGradingBSpline(hueCurve->getCurve(LUM_LUM), lum_lum); + }) + .def_property("sat_lum", + [](const GradingHueCurveRcPtr & hueCurve) + { + return hueCurve->getCurve(SAT_LUM); + }, + [](const GradingHueCurveRcPtr & hueCurve, + const GradingBSplineCurveRcPtr & sat_lum) + { + CopyGradingBSpline(hueCurve->getCurve(SAT_LUM), sat_lum); + }) + .def_property("hue_fx", + [](const GradingHueCurveRcPtr & hueCurve) + { + return hueCurve->getCurve(HUE_FX); + }, + [](const GradingHueCurveRcPtr & hueCurve, + const GradingBSplineCurveRcPtr & hue_fx) + { + CopyGradingBSpline(hueCurve->getCurve(HUE_FX), hue_fx); + }); + + defRepr(clsGradingHueCurve); } } // namespace OCIO_NAMESPACE diff --git a/src/bindings/python/PyOpenColorIO.h b/src/bindings/python/PyOpenColorIO.h index a803c540e3..298b05ff0f 100644 --- a/src/bindings/python/PyOpenColorIO.h +++ b/src/bindings/python/PyOpenColorIO.h @@ -115,7 +115,7 @@ struct polymorphic_type_hook { { type = &typeid(OCIO::AllocationTransform); } - if(dynamic_cast(src)) + else if(dynamic_cast(src)) { type = &typeid(OCIO::BuiltinTransform); } @@ -159,7 +159,11 @@ struct polymorphic_type_hook { { type = &typeid(OCIO::GradingRGBCurveTransform); } - if(dynamic_cast(src)) + else if (dynamic_cast(src)) + { + type = &typeid(OCIO::GradingHueCurveTransform); + } + else if(dynamic_cast(src)) { type = &typeid(OCIO::GradingToneTransform); } diff --git a/src/bindings/python/PyTransform.cpp b/src/bindings/python/PyTransform.cpp index 997bf984ec..40ea4fb4b4 100644 --- a/src/bindings/python/PyTransform.cpp +++ b/src/bindings/python/PyTransform.cpp @@ -43,6 +43,7 @@ void bindPyTransform(py::module & m) bindPyFixedFunctionTransform(m); bindPyGradingPrimaryTransform(m); bindPyGradingRGBCurveTransform(m); + bindPyGradingHueCurveTransform(m); bindPyGradingToneTransform(m); bindPyGroupTransform(m); bindPyLogAffineTransform(m); diff --git a/src/bindings/python/PyTransform.h b/src/bindings/python/PyTransform.h index 817c3a6b49..89fa49d20d 100644 --- a/src/bindings/python/PyTransform.h +++ b/src/bindings/python/PyTransform.h @@ -25,6 +25,7 @@ void bindPyFileTransform(py::module & m); void bindPyFixedFunctionTransform(py::module & m); void bindPyGradingPrimaryTransform(py::module & m); void bindPyGradingRGBCurveTransform(py::module & m); +void bindPyGradingHueCurveTransform(py::module & m); void bindPyGradingToneTransform(py::module & m); void bindPyGroupTransform(py::module & m); void bindPyLogAffineTransform(py::module & m); diff --git a/src/bindings/python/PyTypes.cpp b/src/bindings/python/PyTypes.cpp index 5b22b56195..c6cd710eb2 100644 --- a/src/bindings/python/PyTypes.cpp +++ b/src/bindings/python/PyTypes.cpp @@ -139,6 +139,10 @@ void bindPyTypes(py::module & m) m, "GradingRGBCurve", DOC(GradingRGBCurve)); + py::class_( + m, "GradingHueCurve", + DOC(GradingHueCurve)); + py::class_( m, "Transform", DOC(Transform)); @@ -207,6 +211,12 @@ void bindPyTypes(py::module & m) m, "GradingRGBCurveTransform", DOC(GradingRGBCurveTransform)); + py::class_( + m, "GradingHueCurveTransform", + DOC(GradingHueCurveTransform)); + py::class_( @@ -401,6 +411,8 @@ void bindPyTypes(py::module & m) DOC(PyOpenColorIO, TransformType, TRANSFORM_TYPE_GRADING_PRIMARY)) .value("TRANSFORM_TYPE_GRADING_RGB_CURVE", TRANSFORM_TYPE_GRADING_RGB_CURVE, DOC(PyOpenColorIO, TransformType, TRANSFORM_TYPE_GRADING_RGB_CURVE)) + .value("TRANSFORM_TYPE_GRADING_HUE_CURVE", TRANSFORM_TYPE_GRADING_HUE_CURVE, + DOC(PyOpenColorIO, TransformType, TRANSFORM_TYPE_GRADING_HUE_CURVE)) .value("TRANSFORM_TYPE_GRADING_TONE", TRANSFORM_TYPE_GRADING_TONE, DOC(PyOpenColorIO, TransformType, TRANSFORM_TYPE_GRADING_TONE)) .value("TRANSFORM_TYPE_GROUP", TRANSFORM_TYPE_GROUP, @@ -597,6 +609,12 @@ void bindPyTypes(py::module & m) DOC(PyOpenColorIO, FixedFunctionStyle, FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20)) .value("FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20", FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20, DOC(PyOpenColorIO, FixedFunctionStyle, FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20)) + .value("FIXED_FUNCTION_RGB_TO_HSY_LIN", FIXED_FUNCTION_RGB_TO_HSY_LIN, + DOC(PyOpenColorIO, FixedFunctionStyle, FIXED_FUNCTION_RGB_TO_HSY_LIN)) + .value("FIXED_FUNCTION_RGB_TO_HSY_LOG", FIXED_FUNCTION_RGB_TO_HSY_LOG, + DOC(PyOpenColorIO, FixedFunctionStyle, FIXED_FUNCTION_RGB_TO_HSY_LOG)) + .value("FIXED_FUNCTION_RGB_TO_HSY_VID", FIXED_FUNCTION_RGB_TO_HSY_VID, + DOC(PyOpenColorIO, FixedFunctionStyle, FIXED_FUNCTION_RGB_TO_HSY_VID)) .export_values(); py::enum_( @@ -663,6 +681,8 @@ void bindPyTypes(py::module & m) DOC(PyOpenColorIO, DynamicPropertyType, DYNAMIC_PROPERTY_GRADING_PRIMARY)) .value("DYNAMIC_PROPERTY_GRADING_RGBCURVE", DYNAMIC_PROPERTY_GRADING_RGBCURVE, DOC(PyOpenColorIO, DynamicPropertyType, DYNAMIC_PROPERTY_GRADING_RGBCURVE)) + .value("DYNAMIC_PROPERTY_GRADING_HUECURVE", DYNAMIC_PROPERTY_GRADING_HUECURVE, + DOC(PyOpenColorIO, DynamicPropertyType, DYNAMIC_PROPERTY_GRADING_HUECURVE)) .value("DYNAMIC_PROPERTY_GRADING_TONE", DYNAMIC_PROPERTY_GRADING_TONE, DOC(PyOpenColorIO, DynamicPropertyType, DYNAMIC_PROPERTY_GRADING_TONE)) .export_values(); @@ -683,6 +703,46 @@ void bindPyTypes(py::module & m) DOC(PyOpenColorIO, RGBCurveType, RGB_NUM_CURVES)) .export_values(); + py::enum_( + m, "HueCurveType", + DOC(PyOpenColorIO, HueCurveType)) + + .value("HUE_HUE", HUE_HUE, + DOC(PyOpenColorIO, HueCurveType, HUE_HUE)) + .value("HUE_SAT", HUE_SAT, + DOC(PyOpenColorIO, HueCurveType, HUE_SAT)) + .value("HUE_LUM", HUE_LUM, + DOC(PyOpenColorIO, HueCurveType, HUE_LUM)) + .value("LUM_SAT", LUM_SAT, + DOC(PyOpenColorIO, HueCurveType, LUM_SAT)) + .value("SAT_SAT", SAT_SAT, + DOC(PyOpenColorIO, HueCurveType, SAT_SAT)) + .value("LUM_LUM", LUM_LUM, + DOC(PyOpenColorIO, HueCurveType, LUM_LUM)) + .value("SAT_LUM", SAT_LUM, + DOC(PyOpenColorIO, HueCurveType, SAT_LUM)) + .value("HUE_FX", HUE_FX, + DOC(PyOpenColorIO, HueCurveType, HUE_FX)) + .export_values(); + + py::enum_( + m, "BSplineType", + DOC(PyOpenColorIO, BSplineType)) + + .value("B_SPLINE", B_SPLINE, + DOC(PyOpenColorIO, BSplineType, B_SPLINE)) + .value("DIAGONAL_B_SPLINE", DIAGONAL_B_SPLINE, + DOC(PyOpenColorIO, BSplineType, DIAGONAL_B_SPLINE)) + .value("HUE_HUE_B_SPLINE", HUE_HUE_B_SPLINE, + DOC(PyOpenColorIO, BSplineType, HUE_HUE_B_SPLINE)) + .value("PERIODIC_1_B_SPLINE", PERIODIC_1_B_SPLINE, + DOC(PyOpenColorIO, BSplineType, PERIODIC_1_B_SPLINE)) + .value("PERIODIC_0_B_SPLINE", PERIODIC_0_B_SPLINE, + DOC(PyOpenColorIO, BSplineType, PERIODIC_0_B_SPLINE)) + .value("HORIZONTAL1_B_SPLINE", HORIZONTAL1_B_SPLINE, + DOC(PyOpenColorIO, BSplineType, HORIZONTAL1_B_SPLINE)) + .export_values(); + py::enum_( m, "UniformDataType", DOC(PyOpenColorIO, UniformDataType)) diff --git a/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp b/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp new file mode 100644 index 0000000000..547c82a760 --- /dev/null +++ b/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#include "PyTransform.h" + +namespace OCIO_NAMESPACE +{ + +void bindPyGradingHueCurveTransform(py::module & m) +{ + GradingHueCurveTransformRcPtr DEFAULT = GradingHueCurveTransform::Create(GRADING_LOG); + + auto clsGradingHueCurveTransform = + py::class_( + m.attr("GradingHueCurveTransform")) + + .def(py::init([](const ConstGradingHueCurveRcPtr & values, + GradingStyle style, + bool dynamic, + TransformDirection dir) + { + GradingHueCurveTransformRcPtr p = GradingHueCurveTransform::Create(style); + p->setStyle(style); + p->setValue(values); + if (dynamic) { p->makeDynamic(); } + p->setDirection(dir); + p->validate(); + return p; + }), + "values"_a, + "style"_a = DEFAULT->getStyle(), + "dynamic"_a = DEFAULT->isDynamic(), + "dir"_a = DEFAULT->getDirection(), + DOC(GradingHueCurveTransform, Create)) + .def(py::init([](GradingStyle style, bool dynamic, TransformDirection dir) + { + GradingHueCurveTransformRcPtr p = GradingHueCurveTransform::Create(style); + p->setStyle(style); + if (dynamic) + { + p->makeDynamic(); + } + p->setDirection(dir); + p->validate(); + return p; + }), + "style"_a = DEFAULT->getStyle(), + "dynamic"_a = DEFAULT->isDynamic(), + "dir"_a = DEFAULT->getDirection(), + DOC(GradingHueCurveTransform, Create)) + + .def("getFormatMetadata", + (FormatMetadata & (GradingHueCurveTransform::*)()) + &GradingHueCurveTransform::getFormatMetadata, + py::return_value_policy::reference_internal, + DOC(GradingHueCurveTransform, getFormatMetadata)) + .def("getStyle", &GradingHueCurveTransform::getStyle, + DOC(GradingHueCurveTransform, getStyle)) + .def("setStyle", &GradingHueCurveTransform::setStyle, "style"_a, + DOC(GradingHueCurveTransform, setStyle)) + .def("getValue", &GradingHueCurveTransform::getValue, + DOC(GradingHueCurveTransform, getValue)) + .def("setValue", &GradingHueCurveTransform::setValue, "values"_a, + DOC(GradingHueCurveTransform, setValue)) + .def("getSlope", &GradingHueCurveTransform::getSlope, "curve"_a, "index"_a, + DOC(GradingHueCurveTransform, getSlope)) + .def("setSlope", &GradingHueCurveTransform::setSlope, "curve"_a, "index"_a, "slope"_a, + DOC(GradingHueCurveTransform, setSlope)) + .def("slopesAreDefault", &GradingHueCurveTransform::slopesAreDefault, "curve"_a, + DOC(GradingHueCurveTransform, slopesAreDefault)) +// TODO: Add more pythonic version? +// .def("getSlopes", [](GradingHueCurveTransformRcPtr self, HueCurveType curveType) +// { +// std::vector slopes; +// GradingHueCurveRcPtr curve = self->getValue(); +// const size_t numPts = self->getNumControlPoints(); +// slopes.resize(numPts); +// for (size_t pt = 0; pt < numPts; ++pt) +// { +// slopes[pt] = self->getSlope(curveType, pt); +// } +// return slopes; +// }, +// DOC(GradingHueCurveTransform, getSlopes)) +// .def("setSlopes", [](GradingHueCurveTransformRcPtr self, +// HueCurveType curveType, +// const std::vector & slopes) +// { +// const size_t numPts = self->getNumControlPoints(); +// const auto size = slopes.size(); +// if (size != numPts) +// { +// throw Exception("Length of slopes vector must match number of control points."); +// } +// for (size_t pt = 0; pt < numPts; ++pt) +// { +// self->setSlope(curveType, pt, slopes[pt]); +// } +// }, +// DOC(GradingHueCurveTransform, getSlopes)); + .def("getBypassLinToLog", &GradingHueCurveTransform::getBypassLinToLog, + DOC(GradingHueCurveTransform, getBypassLinToLog)) + .def("setBypassLinToLog", &GradingHueCurveTransform::setBypassLinToLog, "bypass"_a, + DOC(GradingHueCurveTransform, setBypassLinToLog)) + .def("getDrawCurveOnly", &GradingHueCurveTransform::getDrawCurveOnly, + DOC(GradingHueCurveTransform, getDrawCurveOnly)) + .def("setDrawCurveOnly", &GradingHueCurveTransform::setDrawCurveOnly, "drawcurveonly"_a, + DOC(GradingHueCurveTransform, setDrawCurveOnly)) + .def("isDynamic", &GradingHueCurveTransform::isDynamic, + DOC(GradingHueCurveTransform, isDynamic)) + .def("makeDynamic", &GradingHueCurveTransform::makeDynamic, + DOC(GradingHueCurveTransform, makeDynamic)) + .def("makeNonDynamic", &GradingHueCurveTransform::makeNonDynamic, + DOC(GradingHueCurveTransform, makeNonDynamic)); + + defRepr(clsGradingHueCurveTransform); +} + +} // namespace OCIO_NAMESPACE diff --git a/tests/cpu/CMakeLists.txt b/tests/cpu/CMakeLists.txt index 863f034aeb..6d8103b3d1 100755 --- a/tests/cpu/CMakeLists.txt +++ b/tests/cpu/CMakeLists.txt @@ -255,6 +255,7 @@ set(TESTS ops/gradingprimary/GradingPrimaryOp_tests.cpp ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp ops/gradinghuecurve/GradingHueCurve_tests.cpp + ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp ops/gradinghuecurve/GradingHueCurveOp_tests.cpp ops/gradingrgbcurve/GradingRGBCurve_tests.cpp diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index 6a310aae04..fe40f187a2 100644 --- a/tests/cpu/Config_tests.cpp +++ b/tests/cpu/Config_tests.cpp @@ -2093,12 +2093,12 @@ OCIO_ADD_TEST(Config, version) { OCIO_CHECK_THROW_WHAT(config->setVersion(2, 9), OCIO::Exception, "The minor version 9 is not supported for major version 2. " - "Maximum minor version is 4"); + "Maximum minor version is 5"); OCIO_CHECK_NO_THROW(config->setMajorVersion(2)); OCIO_CHECK_THROW_WHAT(config->setMinorVersion(9), OCIO::Exception, "The minor version 9 is not supported for major version 2. " - "Maximum minor version is 4"); + "Maximum minor version is 5"); } { @@ -4615,6 +4615,106 @@ OCIO_ADD_TEST(Config, grading_rgbcurve_serialization) } } +OCIO_ADD_TEST(Config, grading_huecurve_serialization) +{ + { + const std::string strEnd = + " from_scene_reference: !\n" + " children:\n" + " - ! {style: log}\n" + " - ! {style: log, direction: inverse}\n" + " - ! {style: linear}\n" + " - ! {style: linear, direction: inverse}\n" + " - ! {name: test, style: video}\n" + " - ! {style: video, direction: inverse}\n"; + + const std::string str = PROFILE_START_V<2, 5>() + strEnd; + + std::istringstream is; + is.str(str); + + OCIO::ConstConfigRcPtr config; + OCIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is)); + OCIO_CHECK_NO_THROW(config->validate()); + + // Write the config. + + std::stringstream ss; + OCIO_CHECK_NO_THROW(ss << *config.get()); + OCIO_CHECK_EQUAL(ss.str(), str); + } + + { + const std::string strEnd = + " from_scene_reference: !\n" + " children:\n" + " - !\n" + " style: log\n" + " hue_hue: {control_points: [0, 0, 0.5, 0.5, 1, 1.123456]}\n" + " - !\n" + " style: log\n" + " hue_sat: {control_points: [0, 0, 0.5, 0.5, 1, 1.5]}\n" + " lum_lum: {control_points: [-1, -1, 0, 0.1, 0.5, 0.6, 1, 1.1]}\n" + " direction: inverse\n" + " - !\n" + " style: linear\n" + " sat_sat: {control_points: [0, 0, 0.1, 0.2, 0.5, 0.5, 0.7, 0.6, 1, 1.5]}\n" + " lum_lum: {control_points: [-1, -1, 0, 0.1, 0.5, 0.6, 1, 1.1]}\n" + " - !\n" + " style: video\n" + " hue_hue: {control_points: [-0.2, 0, 0.5, 0.5, 1.2, 1.5]}\n" + " hue_lum: {control_points: [0, 0, 0.2, 0.5, 1, 1.5]}\n" + " lum_sat: {control_points: [0, 0, 0.1, 0.5, 1, 1.5], slopes: [0, 1, 1.1]}\n" + " sat_lum: {control_points: [-1, -1, 0, 0.1, 0.5, 0.6, 1, 1.1]}\n" + " direction: inverse\n"; + + const std::string str = PROFILE_START_V<2, 5>() + strEnd; + + std::istringstream is; + is.str(str); + + OCIO::ConstConfigRcPtr config; + OCIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is)); + OCIO_CHECK_NO_THROW(config->validate()); + + // Write the config. + + std::stringstream ss; + OCIO_CHECK_NO_THROW(ss << *config.get()); + OCIO_CHECK_EQUAL(ss.str(), str); + } + + { + const std::string strEnd = + " from_reference: !\n" + " children:\n" + " - ! {style: log}\n"; + const std::string str = PROFILE_START_V<2, 4>() + strEnd; + + std::istringstream is; + is.str(str); + + OCIO_CHECK_THROW_WHAT(OCIO::Config::CreateFromStream(is), OCIO::Exception, + "Only config version 2.5 (or higher) can have GradingHueCurveTransform"); + } + + { + const std::string strEnd = + " from_reference: !\n" + " children:\n" + " - !\n" + " style: log\n" + " sat_sat: {control_points: [0, 0, 0.1, 0.5, 1, 1.5], slopes: [0, 1, 1.1, 1]}\n"; + const std::string str = PROFILE_START_V<2, 5>() + strEnd; + + std::istringstream is; + is.str(str); + + OCIO_CHECK_THROW_WHAT(OCIO::Config::CreateFromStream(is), OCIO::Exception, + "Number of slopes must match number of control points"); + } +} + OCIO_ADD_TEST(Config, grading_tone_serialization) { { @@ -5259,6 +5359,46 @@ R"([OpenColorIO Warning]: FixedFunction style is experimental and may be removed OCIO_CHECK_THROW_WHAT(config->validate(), OCIO::Exception, "FixedFunctionTransform validation failed: Parameter 100.5 (peak_luminance) cannot include any fractional component"); } + + { + const std::string strEnd = + " from_scene_reference: !\n" + " children:\n" + " - ! {style: RGB_TO_HSY_LOG}\n" + " - ! {style: RGB_TO_HSY_LOG, direction: inverse}\n" + " - ! {style: RGB_TO_HSY_LIN}\n" + " - ! {style: RGB_TO_HSY_LIN, direction: inverse}\n" + " - ! {style: RGB_TO_HSY_VID}\n" + " - ! {style: RGB_TO_HSY_VID, direction: inverse}\n"; + + { + const std::string str = PROFILE_START_V<2, 4>() + strEnd; + + std::istringstream is; + is.str(str); + + OCIO_CHECK_THROW_WHAT(OCIO::Config::CreateFromStream(is), OCIO::Exception, + "Only config version 2.5 (or higher) can have FixedFunctionTransform style 'RGB_TO_HSY_LOG'."); + } + + { + const std::string str = PROFILE_START_V<2, 5>() + strEnd; + + std::istringstream is; + is.str(str); + + OCIO::ConstConfigRcPtr config; + OCIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is)); + OCIO_CHECK_NO_THROW(config->validate()); + + // Write the config. + std::stringstream ss; + OCIO_CHECK_NO_THROW(ss << *config.get()); + OCIO_CHECK_EQUAL(ss.str(), str); + } + + } + } OCIO_ADD_TEST(Config, exposure_contrast_serialization) diff --git a/tests/cpu/DynamicProperty_tests.cpp b/tests/cpu/DynamicProperty_tests.cpp index 9c1e26c9e3..c603eb7e2b 100644 --- a/tests/cpu/DynamicProperty_tests.cpp +++ b/tests/cpu/DynamicProperty_tests.cpp @@ -257,7 +257,7 @@ OCIO_ADD_TEST(DynamicPropertyImpl, equal_grading_rgb_curve) OCIO_CHECK_ASSERT(!(*dp0 == *dpImplDouble)); } -OCIO_ADD_TEST(DynamicPropertyImpl, grading_rgb_curve_knots) +OCIO_ADD_TEST(DynamicPropertyImpl, grading_rgb_curve_knots_coefs) { auto curve11 = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f }, { 5.f, 10.f },{ 6.f, 10.f },{ 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f }, @@ -399,6 +399,161 @@ OCIO_ADD_TEST(DynamicPropertyImpl, grading_rgb_curve_knots) OCIO_CHECK_EQUAL(dpPointer, dpPointerAfterSet); } +void checkKnotsAndCoefs( + OCIO::DynamicPropertyGradingHueCurveImplRcPtr & dp, + int set, + const float * true_knots, + const float * true_coefsA, + const float * true_coefsB, + const float * true_coefsC, + unsigned line) +{ + const int * knotsOffsets = dp->getKnotsOffsetsArray(); + const int * coefsOffsets = dp->getCoefsOffsetsArray(); + const float * knots = dp->getKnotsArray(); + const float * coefs = dp->getCoefsArray(); + + const int numKnots = knotsOffsets[set*2 + 1]; + for (int i = 0; i < numKnots; i++) + { + const int offset = knotsOffsets[set*2]; + OCIO_CHECK_CLOSE_FROM(knots[offset + i], true_knots[i], 1e-6, line); + } + const int numCoefSets = coefsOffsets[set*2 + 1] / 3; + for (int i = 0; i < numCoefSets; i++) + { + const int offset = coefsOffsets[set*2]; + OCIO_CHECK_CLOSE_FROM(coefs[offset + i], true_coefsA[i], 3e-4, line); + OCIO_CHECK_CLOSE_FROM(coefs[offset + numCoefSets + i], true_coefsB[i], 1e-5, line); + OCIO_CHECK_CLOSE_FROM(coefs[offset + 2*numCoefSets + i], true_coefsC[i], 1e-5, line); + } +} + +OCIO_ADD_TEST(DynamicPropertyImpl, grading_hue_curve_knots_coefs) +{ + + auto hh = OCIO::GradingBSplineCurve::Create({ {-0.1f, -0.15f}, {0.2f, 0.3f}, {0.5f, 0.25f}, + {0.8f, 0.7f}, {0.85f, 0.8f}, {1.05f, 0.9f} }, OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create({ {-0.15f, 1.25f}, {0.f, 0.8f}, {0.2f, 0.9f}, + {0.4f, 1.8f}, {0.6f, 1.4f}, {0.8f, 1.3f}, {0.9f, 1.1f}, {1.1f, 0.7f} }, OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.22f, 0.077f}, {0.36f, 0.092f}, + {0.51f, 0.27f}, {0.67f, 0.f}, {0.83f, 0.f} }, OCIO::HUE_LUM); + // The rest are identities, but not the default curves. + auto ls = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {1.f, 1.f} }, + OCIO::LUM_SAT); + auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.25f, 0.25f}, {1.f, 1.f} }, + OCIO::SAT_SAT); + auto ll = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.25f, 0.25f}, {0.5f, 0.5f}, {1.f, 1.f} }, + OCIO::LUM_LUM); + auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.25f, 1.f}, {0.5f, 1.f}, {1.f, 1.f} }, + OCIO::SAT_LUM); + auto hfx = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.1f, 0.f}, {0.2f, 0.f}, + {0.4f, 0.f}, {0.6f, 0.f}, {0.8f, 0.f} }, OCIO::HUE_FX); + + auto curves = OCIO::GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); + + // Fit the polynomials. + OCIO::DynamicPropertyGradingHueCurveImplRcPtr dp = + std::make_shared(curves, false); + + const int numOffsets = dp->GetNumOffsetValues(); + OCIO_CHECK_EQUAL(16, numOffsets); + + const int * coefsOffsets = dp->getCoefsOffsetsArray(); + const int * knotsOffsets = dp->getKnotsOffsetsArray(); + + // These are offset0, count0, offset1, count1, offset2, count2, ... + const int true_knotsOffsets[] = {0, 15, 15, 19, 34, 12, 46, 2, 48, 2, 50, 2, 52, 2, 54, 2}; + const int true_coefsOffsets[] = {0, 42, 42, 54, 96, 33, 129, 3, 132, 3, 135, 3, 138, 3, 141, 3}; + for (int i=0; i < numOffsets; i++) + { + OCIO_CHECK_EQUAL(knotsOffsets[i], true_knotsOffsets[i]); + OCIO_CHECK_EQUAL(coefsOffsets[i], true_coefsOffsets[i]); + } + + OCIO_CHECK_EQUAL(56, dp->getNumKnots()); + OCIO_CHECK_EQUAL(144, dp->getNumCoefs()); + + { + // Hue-Hue + + const float true_knots[15] = {-0.1f, -0.01816964f, 0.05f, 0.125f, 0.2f, 0.35f, 0.5f, 0.57558291f, 0.8f, 0.82731918f, + 0.85f, 0.87808036f, 0.9f, 0.98183036f, 1.05f }; + // Quadratic coefs. + const float true_coefsA[14] = { -2.29385141e+00f, 3.43265663e+00f, 2.95979024e+01f, -3.34850652e+01f, -2.11943922e-02f, + 2.11186821e-02f, 9.58311056e+00f, 3.05897301e-01f, 1.69849435e+01f, -2.62364216e+01f, + -5.36565978e+00f, -1.21350984e+01f, -2.29385141e+00f, 3.43265663e+00f}; + // Linear coefs. + const float true_coefsB[14] = { 5.00000000e-01f, 1.24586640e-01f, 5.92592593e-01f, 5.03227795e+00f, 9.51817043e-03f, + 3.15985276e-03f, 9.49545739e-03f, 1.45813415e+00f, 1.59543132e+00f, 2.52346065e+00f, + 1.33333333e+00f, 1.03199405e+00f, 5.00000000e-01f, 1.24586640e-01f }; + // Constant coefs. + const float true_coefsC[14] = { -0.15f, -0.12444493f, -0.1f, 0.11093265f, 0.3f, 0.30095085f, 0.3019f, 0.35736386f, + 0.7f, 0.75626237f, 0.8f, 0.83320962f, 0.85f, 0.87555507f }; + + checkKnotsAndCoefs(dp, 0, true_knots, true_coefsA, true_coefsB, true_coefsC, __LINE__); + } + { + // Hue-Sat + + const float true_knots[19] = { -0.1f, -0.03071429f, 0.f, 0.0625f, 0.1f, 0.13333333f, 0.2f, 0.34913793f, 0.4f, + 0.46896552f, 0.6f, 0.69f, 0.8f, 0.82770833f, 0.85f, 0.86535714f, 0.9f, 0.96928571f, 1.f }; + const float true_coefsA[18] = { -3.32474227f, 31.91860465f, 3.5f, 14.16666667f, 32.30769231f, 4.61538462f, + 13.9662072f, -68.17470665f, -25.2f, 10.21052632f, 2.92592593f, -1.78787879f, + -5.32581454f, -12.07165109f, -63.8372093f, 6.64948454f, -3.32474227f, 31.91860465f }; + const float true_coefsB[18] = { -3.f, -3.46071429f, -1.5f, -1.0625f, 0.f, 2.15384615f, 2.76923077f, 6.93501326f, 0.f, -3.47586207f, + -0.8f, -0.27333333f, -0.66666667f, -0.96180556f, -1.5f, -3.46071429f, -3.f, -3.46071429f }; + const float true_coefsC[18] = { 1.1f, 0.8761824f, 0.8f, 0.71992187f, 0.7f, 0.73589744f, 0.9f, 1.62363544f, 1.8f, + 1.68014269f, 1.4f, 1.3517f, 1.3f, 1.27743887f, 1.25f, 1.2119088f, 1.1f, 0.8761824f }; + + checkKnotsAndCoefs(dp, 1, true_knots, true_coefsA, true_coefsB, true_coefsC, __LINE__); + } + { + // Hue-Lum + // Test for the "Adjust slopes that are not shape-preserving" path in EstimateHueSlopes. + + const float true_knots[12] = { -0.17f, 0.f, 0.07049104f, 0.22f, 0.29691485f, 0.36f, 0.435f, 0.51f, 0.59f, 0.67f, 0.83f, 1.f }; + + const float true_coefsA[11] = { 0.f, 4.21997107f, -1.47264319f, -0.70657119f, 1.10402357f, 13.97025263f, + -15.20489902f, -21.09375f, 21.09375f, 0.f, 0.f }; + const float true_coefsB[11] = { 0.f, 0.f, 0.59494032f, 0.15459362f, 0.04590198f, 0.18519696f, 2.28073485f, + 0.f, -3.375f, 0.f, 0.f }; + const float true_coefsC[11] = { 0.f, 0.f, 0.02096898f, 0.077f, 0.08471054f, 0.092f, 0.18447244f, 0.27f, + 0.135f, 0.f, 0.f }; + + checkKnotsAndCoefs(dp, 2, true_knots, true_coefsA, true_coefsB, true_coefsC, __LINE__); + } + { + // Horizontal identities + + const float true_knots[2] = { 0.f, 1.f }; + const float true_coefsA[1] = { 0.f }; + const float true_coefsB[1] = { 0.f }; + const float true_coefsC[1] = { 1.f }; + const float true_coefsCfx[1] = { 0.f }; + + // Lum-Sat + checkKnotsAndCoefs(dp, 3, true_knots, true_coefsA, true_coefsB, true_coefsC, __LINE__); + // Sat-Lum + checkKnotsAndCoefs(dp, 6, true_knots, true_coefsA, true_coefsB, true_coefsC, __LINE__); + // Hue-Fx + checkKnotsAndCoefs(dp, 7, true_knots, true_coefsA, true_coefsB, true_coefsCfx, __LINE__); + } + { + // Diagonal identities + + const float true_knots[2] = { 0.f, 1.f }; + const float true_coefsA[1] = { 0.f }; + const float true_coefsB[1] = { 1.f }; + const float true_coefsC[1] = { 0.f }; + + // Sat-Sat + checkKnotsAndCoefs(dp, 4, true_knots, true_coefsA, true_coefsB, true_coefsC, __LINE__); + // Lum-Lum + checkKnotsAndCoefs(dp, 5, true_knots, true_coefsA, true_coefsB, true_coefsC, __LINE__); + } +} + OCIO_ADD_TEST(DynamicPropertyImpl, get_as) { OCIO::GradingPrimary gplog{ OCIO::GRADING_LOG }; diff --git a/tests/cpu/fileformats/FileFormatCTF_tests.cpp b/tests/cpu/fileformats/FileFormatCTF_tests.cpp index e4906fd40b..d104b02b81 100644 --- a/tests/cpu/fileformats/FileFormatCTF_tests.cpp +++ b/tests/cpu/fileformats/FileFormatCTF_tests.cpp @@ -6,6 +6,7 @@ #include "fileformats/FileFormatCTF.cpp" #include "ops/fixedfunction/FixedFunctionOp.h" #include "ops/gradingrgbcurve/GradingRGBCurve.h" +#include "ops/gradinghuecurve/GradingHueCurve.h" #include "testutils/UnitTest.h" #include "UnitTestLogUtils.h" #include "UnitTestUtils.h" @@ -3803,14 +3804,20 @@ void WriteGroupCLF(OCIO::ConstGroupTransformRcPtr group, std::ostringstream & ou group->write(cfg, OCIO::FILEFORMAT_CLF, outputTransform); } -void ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::Style style, - const std::string & vers, int lineNo) +void ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::Style style, + const std::string & vers, int lineNo, std::string params="") { // Validate the load & save for any FixedFunction style without parameters. std::ostringstream ffStr; ffStr << ""; + << OCIO::FixedFunctionOpData::ConvertStyleToString(style, false) << "\""; //>"; + + if (!params.empty()) + { + ffStr << " params=\"" << params << "\""; + } + ffStr << ">"; std::ostringstream strebuf; strebuf << "\n" @@ -3819,6 +3826,7 @@ void ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::Style style, << " \n" << "\n"; + // Test parsing. OCIO::LocalCachedFileRcPtr cachedFile; OCIO_CHECK_NO_THROW_FROM(cachedFile = ParseString(strebuf.str()), lineNo); OCIO::ConstOpDataVec & fileOps = cachedFile->m_transform->getOps(); @@ -3841,6 +3849,7 @@ void ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::Style style, OCIO_CHECK_NO_THROW_FROM(OCIO::CreateFixedFunctionTransform(group, constOp), lineNo); OCIO_REQUIRE_EQUAL_FROM(group->getNumTransforms(), 1, lineNo); + // Test serialization. std::ostringstream outputTransform; OCIO_CHECK_NO_THROW_FROM(WriteGroupCTF(group, outputTransform), lineNo); @@ -3859,26 +3868,62 @@ void ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::Style style, OCIO_ADD_TEST(FileFormatCTF, ff_load_save_ctf) { - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::ACES_RED_MOD_03_FWD, "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::ACES_RED_MOD_03_INV, "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::ACES_RED_MOD_10_FWD, "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::ACES_RED_MOD_10_INV, "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::ACES_GLOW_03_FWD , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::ACES_GLOW_03_INV , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::ACES_GLOW_10_FWD , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::ACES_GLOW_10_INV , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::ACES_DARK_TO_DIM_10_FWD, "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::ACES_DARK_TO_DIM_10_INV, "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::RGB_TO_HSV , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::HSV_TO_RGB , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::XYZ_TO_xyY , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::xyY_TO_XYZ , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::XYZ_TO_uvY , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::uvY_TO_XYZ , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::XYZ_TO_LUV , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::LUV_TO_XYZ , "2", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::PQ_TO_LIN , "2.4", __LINE__); - ValidateFixedFunctionStyleNoParam(OCIO::FixedFunctionOpData::LIN_TO_PQ , "2.4", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_RED_MOD_03_FWD , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_RED_MOD_03_INV , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_RED_MOD_10_FWD , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_RED_MOD_10_INV , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_GLOW_03_FWD , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_GLOW_03_INV , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_GLOW_10_FWD , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_GLOW_10_INV , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_DARK_TO_DIM_10_FWD, "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_DARK_TO_DIM_10_INV, "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_GAMUT_COMP_13_FWD , "2.1", __LINE__, + "1.147 1.264 1.312 0.815 0.803 0.88 1.2"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::REC2100_SURROUND_FWD , "2", __LINE__, + "1"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::REC2100_SURROUND_INV , "2", __LINE__, + "1"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::RGB_TO_HSV , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::HSV_TO_RGB , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::XYZ_TO_xyY , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::xyY_TO_XYZ , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::XYZ_TO_uvY , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::uvY_TO_XYZ , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::XYZ_TO_LUV , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::LUV_TO_XYZ , "2", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::LIN_TO_PQ , "2.4", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::PQ_TO_LIN , "2.4", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::LIN_TO_GAMMA_LOG , "2.4", __LINE__, + "0 0.25 0.5 1 0 2.718 0.17883277 0.807825590164 1 -0.07116723"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::GAMMA_LOG_TO_LIN , "2.4", __LINE__, + "0 0.25 0.5 1 0 2.718 0.17883277 0.807825590164 1 -0.07116723"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::LIN_TO_DOUBLE_LOG , "2.4", __LINE__, + "10 0.25 0.5 -1 0 -1 1.25 1 1 1 0.5 1 0"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::DOUBLE_LOG_TO_LIN , "2.4", __LINE__, + "10 0.25 0.5 -1 0 -1 1.25 1 1 1 0.5 1 0"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_OUTPUT_TRANSFORM_20_FWD , "2.4", __LINE__, + "1000 0.68 0.32 0.265 0.69 0.15 0.06 0.3127 0.329"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_OUTPUT_TRANSFORM_20_INV , "2.4", __LINE__, + "1000 0.68 0.32 0.265 0.69 0.15 0.06 0.3127 0.329"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_RGB_TO_JMh_20 , "2.4", __LINE__, + "0.68 0.32 0.265 0.69 0.15 0.06 0.3127 0.329"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_JMh_TO_RGB_20 , "2.4", __LINE__, + "0.68 0.32 0.265 0.69 0.15 0.06 0.3127 0.329"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_TONESCALE_COMPRESS_20_FWD, "2.4", __LINE__, + "1000"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_TONESCALE_COMPRESS_20_INV, "2.4", __LINE__, + "1000"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_GAMUT_COMPRESS_20_FWD , "2.4", __LINE__, + "1000 0.68 0.32 0.265 0.69 0.15 0.06 0.3127 0.329"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::ACES_GAMUT_COMPRESS_20_INV , "2.4", __LINE__, + "1000 0.68 0.32 0.265 0.69 0.15 0.06 0.3127 0.329"); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::RGB_TO_HSY_LOG , "2.5", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::HSY_LOG_TO_RGB , "2.5", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::RGB_TO_HSY_LIN , "2.5", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::HSY_LIN_TO_RGB , "2.5", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::RGB_TO_HSY_VID , "2.5", __LINE__); + ValidateFixedFunctionStyle(OCIO::FixedFunctionOpData::HSY_VID_TO_RGB , "2.5", __LINE__); } OCIO_ADD_TEST(FileFormatCTF, load_ff_fail_version) @@ -4365,7 +4410,7 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_primary_errors) )"), OCIO::Exception, "Dynamic parameter 'CONTRAST' is not supported in 'GradingPrimary'"); } -OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_lin) +OCIO_ADD_TEST(FileFormatCTF, load_grading_rgbcurves_lin) { std::istringstream ctfLin; ctfLin.str(R"( @@ -4454,7 +4499,7 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_lin) OCIO_CHECK_EQUAL(master->getSlope(2), 1.1f); } -OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_log) +OCIO_ADD_TEST(FileFormatCTF, load_grading_rgbcurves_log) { std::istringstream ctfLog; ctfLog.str(R"( @@ -4529,6 +4574,101 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_log) OCIO_CHECK_EQUAL(master->getControlPoint(3).m_y, -1.5f); } +OCIO_ADD_TEST(FileFormatCTF, load_grading_huecurves_log) +{ + std::istringstream ctfLog; + ctfLog.str(R"( + + + + + 0.015625 0 + 0.5 0.5 + 2 2 + + + + + 0.015625 1 + 2.5 0.5 + 3.5 1.5 + + + + + -4 -4 + 4.5 0.5 + 5 3 + + + + + 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + + + + + 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + + + + + 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + + + + + 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + + + + + 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + + + + +)"); + + // Load file. + std::string emptyString; + OCIO::LocalFileFormat tester; + OCIO::CachedFileRcPtr file; + OCIO_CHECK_NO_THROW(file = tester.read(ctfLog, emptyString, OCIO::INTERP_DEFAULT)); + OCIO::LocalCachedFileRcPtr cachedFile = OCIO_DYNAMIC_POINTER_CAST(file); + OCIO_REQUIRE_ASSERT(cachedFile); + const auto & fileOps = cachedFile->m_transform->getOps(); + + OCIO_REQUIRE_EQUAL(fileOps.size(), 1); + const auto op0 = fileOps[0]; + const auto gradingCurves0 = std::dynamic_pointer_cast(op0); + OCIO_REQUIRE_ASSERT(gradingCurves0); + OCIO_CHECK_EQUAL(gradingCurves0->getStyle(), OCIO::GRADING_LOG); + OCIO_CHECK_EQUAL(gradingCurves0->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); + OCIO_CHECK_ASSERT(!gradingCurves0->getBypassLinToLog()); + OCIO_CHECK_ASSERT(gradingCurves0->isDynamic()); + auto curves = gradingCurves0->getValue(); + auto hh = curves->getCurve(OCIO::HUE_HUE); + OCIO_CHECK_EQUAL(hh->getNumControlPoints(), 3); + OCIO_CHECK_EQUAL(hh->getControlPoint(0).m_x, 0.015625f); + OCIO_CHECK_EQUAL(hh->getControlPoint(0).m_y, 0.0f); + OCIO_CHECK_EQUAL(hh->getControlPoint(1).m_x, 0.5f); + OCIO_CHECK_EQUAL(hh->getControlPoint(1).m_y, 0.5f); + OCIO_CHECK_EQUAL(hh->getControlPoint(2).m_x, 2.0f); + OCIO_CHECK_EQUAL(hh->getControlPoint(2).m_y, 2.0f); + + auto ls = curves->getCurve(OCIO::LUM_SAT); + OCIO_CHECK_EQUAL(ls->getNumControlPoints(), 4); + OCIO_CHECK_EQUAL(ls->getControlPoint(0).m_x, 11.0f); + OCIO_CHECK_EQUAL(ls->getControlPoint(0).m_y, 11.0f); + OCIO_CHECK_EQUAL(ls->getControlPoint(1).m_x, 12.5f); + OCIO_CHECK_EQUAL(ls->getControlPoint(1).m_y, 10.5f); + OCIO_CHECK_EQUAL(ls->getControlPoint(2).m_x, 13.5f); + OCIO_CHECK_EQUAL(ls->getControlPoint(2).m_y, 0.5f); + OCIO_CHECK_EQUAL(ls->getControlPoint(3).m_x, 26.5f); + OCIO_CHECK_EQUAL(ls->getControlPoint(3).m_y, -1.5f); +} + OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_errors) { // Wrong version. @@ -4540,6 +4680,13 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_errors) )"), OCIO::Exception, "CTF file version '1.8' does not support operator 'GradingRGBCurve'"); + OCIO_CHECK_THROW_WHAT(ParseString(R"( + + + + +)"), OCIO::Exception, "CTF file version '2' does not support operator 'GradingHueCurve'"); + // Missing style attribute. OCIO_CHECK_THROW_WHAT(ParseString(R"( @@ -4547,6 +4694,13 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_errors) +)"), OCIO::Exception, "Required attribute 'style' is missing"); + + OCIO_CHECK_THROW_WHAT(ParseString(R"( + + + + )"), OCIO::Exception, "Required attribute 'style' is missing"); // Wrong dynamic property param. @@ -4595,7 +4749,7 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_errors) -)"), OCIO::Exception, "Control point at index 2 has a x coordinate '-1' that is less from " +)"), OCIO::Exception, "Control point at index 2 has a x coordinate '-1' that is less than " "previous control point x cooordinate '0'"); // Number of slopes matches control points. @@ -4632,7 +4786,6 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_errors) OCIO_REQUIRE_EQUAL(parts.size(), 2); OCIO_CHECK_NE(std::string::npos, StringUtils::Find(parts[0], "Unrecognized element 'Grn'")); OCIO_CHECK_NE(std::string::npos, StringUtils::Find(parts[1], "Unrecognized element 'ControlPoints'")); - } OCIO_ADD_TEST(CTFTransform, load_grading_tone) @@ -7112,6 +7265,100 @@ OCIO_ADD_TEST(CTFTransform, grading_rgbcurve_lin_ctf) } } +OCIO_ADD_TEST(CTFTransform, grading_huecurve_lin_ctf) +{ + auto gradingCurves = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LIN); + + // Note: The default curves are not saved. + OCIO::GradingHueCurveRcPtr curves = OCIO::GradingHueCurve::Create(OCIO::GRADING_LIN); + curves->getCurve(OCIO::LUM_SAT)->getControlPoint(0).m_y = 1.1; + curves->getCurve(OCIO::LUM_LUM)->setNumControlPoints(4); + curves->getCurve(OCIO::LUM_LUM)->getControlPoint(3).m_x = 16.f; + curves->getCurve(OCIO::LUM_LUM)->getControlPoint(3).m_y = 10.f; + curves->getCurve(OCIO::LUM_LUM)->setSlope(0, 1.f); + curves->getCurve(OCIO::LUM_LUM)->setSlope(1, 0.75f); + curves->getCurve(OCIO::LUM_LUM)->setSlope(2, 1.1f); + curves->getCurve(OCIO::LUM_LUM)->setSlope(3, 1.f); + gradingCurves->setValue(curves); + { + OCIO::GroupTransformRcPtr group = OCIO::GroupTransform::Create(); + group->getFormatMetadata().addAttribute(OCIO::METADATA_ID, "UIDGradingCurves"); + group->appendTransform(gradingCurves); + + std::ostringstream outputTransform; + OCIO_CHECK_NO_THROW(WriteGroupCTF(group, outputTransform)); + + const std::string expected{ R"( + + + + + -7 1.1 + 0 1 + 7 1 + + + + + -7 -7 + 0 0 + 7 7 + 16 10 + + + 1 0.75 1.1 1 + + + + +)" }; + + const StringUtils::StringVec osvec = StringUtils::SplitByLines(outputTransform.str()); + const StringUtils::StringVec resvec = StringUtils::SplitByLines(expected); + OCIO_CHECK_EQUAL(osvec.size(), resvec.size()); + for(unsigned int i = 0; i < resvec.size(); ++i) + { + if ( (i >= 5 && i <= 7) || + (i >= 12 && i <= 15) || + (i == 18)) + { + OCIO_CHECK_STR_FLOAT_VEC_CLOSE(osvec[i], resvec[i], 1e-5f); + } + else + { + OCIO_CHECK_EQUAL(osvec[i], resvec[i]); + } + + } + } + + // All curves are default curves, no curve is saved. + curves = OCIO::GradingHueCurve::Create(OCIO::GRADING_LIN); + gradingCurves->setValue(curves); + gradingCurves->setBypassLinToLog(true); + // Make it dynamic so it is not identity. + gradingCurves->makeDynamic(); + { + OCIO::GroupTransformRcPtr group = OCIO::GroupTransform::Create(); + group->getFormatMetadata().addAttribute(OCIO::METADATA_ID, "UIDGradingCurves"); + group->appendTransform(gradingCurves); + + std::ostringstream outputTransform; + OCIO_CHECK_NO_THROW(WriteGroupCTF(group, outputTransform)); + + const std::string expected{ R"( + + + + + +)" }; + + OCIO_CHECK_EQUAL(expected.size(), outputTransform.str().size()); + OCIO_CHECK_EQUAL(expected, outputTransform.str()); + } +} + OCIO_ADD_TEST(CTFTransform, grading_tone_log_ctf) { auto gradingTone = OCIO::GradingToneTransform::Create(OCIO::GRADING_LOG); diff --git a/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp b/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp index 6a45844af0..9593c9cd8e 100644 --- a/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp +++ b/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp @@ -1070,29 +1070,38 @@ OCIO_ADD_TEST(FixedFunctionOpCPU, RGB_TO_HSV) OCIO_ADD_TEST(FixedFunctionOpCPU, RGB_TO_HSY_LIN) { + const int numPixels = 8; const std::vector hsyFrame { - 4.70554752e-01f, 9.12594033f, 3.26650218e-02f, 0.f, - 0.75f, 0.22196741f, 0.38596f, 1.f, - 0.08333333f, 0.12976444f, 0.034974f, 0.f, - 0.96296296f, 9.7034f, -0.1862f, 1.f }; + 0.470554752f, 9.12594033f, 0.0326650218f, 0.f, // hsy alpha == 1 + 0.75f, 0.22196741f, 0.38596f, 0.f, + 0.08333333f, 0.12976444f, 0.034974f, 0.f, + 0.333333333333f, 0.606036032f, 0.0056680f, 1.f, // hsy mid alpha + 0.241666666667f, 0.8372990325f, 0.0034440f, 1.f, + 0.734693877551f, 0.752099600f, 0.0005572f, 0.f, // hsy alpha == 0 + 0.96296296f, 9.7034f, -0.1862f, 0.f, + 0.730158730159f, 0.811517000f, -0.0009310f, 0.f }; const std::vector rgbFrame { -0.075290f, 0.078996f, -0.108397f, 0.f, - 0.3f, 0.4f, 0.5f, 1.f, + 0.3f, 0.4f, 0.5f, 0.f, 0.05f, 0.03f, 0.04f, 0.f, - 0.3f, -0.4f, 0.5f, 1.f }; + 0.01f, 0.01f, -0.05f, 1.f, + 0.05f, -0.005f, -0.05f, 1.f, + -0.048f, 0.01f, 0.05f, 0.f, + 0.3f, -0.4f, 0.5f, 0.f, + -0.055f, 0.01f, 0.05f, 0.f }; OCIO::ConstFixedFunctionOpDataRcPtr dataFwdLin = std::make_shared(OCIO::FixedFunctionOpData::RGB_TO_HSY_LIN); std::vector img = rgbFrame; - ApplyFixedFunction(&img[0], &hsyFrame[0], 4, dataFwdLin, 1e-6f, __LINE__); + ApplyFixedFunction(&img[0], &hsyFrame[0], numPixels, dataFwdLin, 1e-6f, __LINE__); OCIO::ConstFixedFunctionOpDataRcPtr dataFInvLin = std::make_shared(OCIO::FixedFunctionOpData::HSY_LIN_TO_RGB); img = hsyFrame; - ApplyFixedFunction(&img[0], &rgbFrame[0], 4, dataFInvLin, 1e-6f, __LINE__); + ApplyFixedFunction(&img[0], &rgbFrame[0], numPixels, dataFInvLin, 1e-6f, __LINE__); } OCIO_ADD_TEST(FixedFunctionOpCPU, RGB_TO_HSY_LOG) diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp new file mode 100644 index 0000000000..63c3c4a08c --- /dev/null +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#include "ops/gradinghuecurve/GradingHueCurveOpCPU.cpp" + +#include "testutils/UnitTest.h" +#include "utils/StringUtils.h" + +namespace OCIO = OCIO_NAMESPACE; + +namespace +{ +void ValidateImage(const float * expected, const float * res, long numPix, unsigned line) +{ + static constexpr float error = 2e-5f; + + for (long i = 0; i < numPix; ++i) + { + for (long j = 0; j < 4; ++j) + { + if (OCIO::IsNan(expected[i * 4 + j])) + { + OCIO_CHECK_ASSERT(OCIO::IsNan(res[i * 4 + j])); + } + else if (expected[i * 4 + j] != res[i * 4 + j]) + { + OCIO_CHECK_CLOSE_FROM(expected[i * 4 + j], res[i * 4 + j], error, line); + } + } + } +} +} + +OCIO_ADD_TEST(GradingHueCurveOpCPU, identity) +{ + constexpr long numPixels = 9; + const float qnan = std::numeric_limits::quiet_NaN(); + const float inf = std::numeric_limits::infinity(); + const float image[4 * numPixels] = { -0.50f, -0.25f, 0.50f, 0.0f, + 0.75f, 1.00f, 1.25f, 1.0f, + 1.25f, 1.50f, 1.75f, 0.0f, + 0.0f, 0.0f, 0.0f, qnan, + 0.0f, 0.0f, 0.0f, inf, + 0.0f, 0.0f, 0.0f, -inf }; + + const float expected[4 * numPixels] = { -0.50f, -0.25f, 0.50f, 0.0f, + 0.75f, 1.00f, 1.25f, 1.0f, + 1.25f, 1.50f, 1.75f, 0.0f, + 0.0f, 0.0f, 0.0f, qnan, + 0.0f, 0.0f, 0.0f, inf, + 0.0f, 0.0f, 0.0f, -inf }; + + // TODO: Nan/Inf handling + + float res[4 * numPixels]{ 0.f }; + + auto gc = std::make_shared(OCIO::GRADING_LIN); + OCIO::ConstOpCPURcPtr op; + OCIO::ConstGradingHueCurveOpDataRcPtr gcc = gc; + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + + // Check that the right OpCPU is created. Check that class name contains CurveFwdOp. + { + const OCIO::OpCPU & c = *op; + std::string typeName = typeid(c).name(); + OCIO_CHECK_NE(std::string::npos, StringUtils::Find(typeName, "CurveFwdOp")); + } + OCIO_CHECK_NO_THROW(op->apply(image, res, numPixels)); + ValidateImage(expected, res, numPixels, __LINE__); + + gc->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + + // Check that the right OpCPU is created. Check that class name contains CurveRevOp. + { + const OCIO::OpCPU & c = *op; + std::string typeName = typeid(c).name(); + OCIO_CHECK_NE(std::string::npos, StringUtils::Find(typeName, "CurveRevOp")); + } + OCIO_CHECK_NO_THROW(op->apply(image, res, numPixels)); + ValidateImage(expected, res, numPixels, __LINE__); +} + +OCIO_ADD_TEST(GradingHueCurveOpCPU, log_identity) +{ + // Identity curves (for log or video) that are different from the default curves. + auto hh = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.1f, 0.1f}, {0.2f, 0.2f}, + {0.4f, 0.4f}, {0.6f, 0.6f}, {0.8f, 0.8f} }, OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.1f, 1.f}, {0.2f, 1.f}, + {0.4f, 1.f}, {0.6f, 1.f}, {0.8f, 1.f} }, OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.1f, 1.f}, {0.2f, 1.f}, + {0.4f, 1.f}, {0.6f, 1.f}, {0.8f, 1.f} }, OCIO::HUE_LUM); + auto ls = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {1.f, 1.f} }, + OCIO::LUM_SAT); + auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.25f, 0.25f}, {1.f, 1.f} }, + OCIO::SAT_SAT); + auto ll = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.25f, 0.25f}, {0.5f, 0.5f}, {1.f, 1.f} }, + OCIO::LUM_LUM); + auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.25f, 1.f}, {0.5f, 1.f}, {1.f, 1.f} }, + OCIO::SAT_LUM); + auto hfx = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.1f, 0.f}, {0.2f, 0.f}, + {0.4f, 0.f}, {0.6f, 0.f}, {0.8f, 0.f} }, OCIO::HUE_FX); + + auto gc = std::make_shared(OCIO::GRADING_LOG, + hh, hs, hl, ls, ss, ll, sl, hfx); + OCIO::ConstOpCPURcPtr op; + OCIO::ConstGradingHueCurveOpDataRcPtr gcc = gc; + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + + constexpr long num_samples = 2; + float res[4 * num_samples]{ 0.f }; + + constexpr float input_32f[] = { + -0.2f, 0.2f, 0.5f, 0.0f, + 0.8f, 1.0f, 2.0f, 0.5f }; + + constexpr float expected_32f[] = { + -0.2f, 0.2f, 0.5f, 0.0f, + 0.8f, 1.0f, 2.0f, 0.5f }; + + // Test in forward direction. + + OCIO_CHECK_NO_THROW(op->apply(input_32f, res, num_samples)); + ValidateImage(expected_32f, res, num_samples, __LINE__); + + // Test in inverse direction. + + gc->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + OCIO_CHECK_NO_THROW(op->apply(expected_32f, res, num_samples)); + ValidateImage(input_32f, res, num_samples, __LINE__); +} + +OCIO_ADD_TEST(GradingHueCurveOpCPU, hh_hfx_curves) +{ + // Validate that the hue curves round-trip. + + // Identity curves. + auto hs = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.9f, 1.f} }, OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.9f, 1.f} }, OCIO::HUE_LUM); + auto ls = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.9f, 1.f} }, OCIO::LUM_SAT); + auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.9f, 0.9f} }, OCIO::SAT_SAT); + auto ll = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.9f, 0.9f} }, OCIO::LUM_LUM); + auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.9f, 1.f} }, OCIO::SAT_LUM); + + // Set hh and hfx to non-identities. + auto hh = OCIO::GradingBSplineCurve::Create({ {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, + {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, OCIO::HUE_HUE); + auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, + { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); + + auto gc = std::make_shared(OCIO::GRADING_LOG, + hh, hs, hl, ls, ss, ll, sl, hfx); + OCIO::ConstOpCPURcPtr op; + OCIO::ConstGradingHueCurveOpDataRcPtr gcc = gc; + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + + constexpr long num_samples = 4; + float res[4 * num_samples]{ 0.f }; + + const float input_32f[] = { + 0.1f, 0.5f, 0.7f, 0.0f, + 0.6f, 0.9f, 0.8f, 0.5f, + 0.4f, 0.35f, 0.3f, 0.f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + const float expected_32f[] = { + 0.3984785676f, 0.3790940642f, 1.0187726020f, 0.0f, + 0.6117081642f, 0.8883015513f, 0.8814064860f, 0.5f, + 0.3847683966f, 0.3567464352f, 0.2780219615f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + // Test in forward direction. + + OCIO_CHECK_NO_THROW(op->apply(input_32f, res, num_samples)); + ValidateImage(expected_32f, res, num_samples, __LINE__); + + // Test in inverse direction. + + gc->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + OCIO_CHECK_NO_THROW(op->apply(res, res, num_samples)); + ValidateImage(input_32f, res, num_samples, __LINE__); +} + +OCIO_ADD_TEST(GradingHueCurveOpCPU_1, log_all_curves) +{ + // All curves are non-identities. + auto hh = OCIO::GradingBSplineCurve::Create({ {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, + {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create({ {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, + {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create({ {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, + {0.5f, 0.8f}, {0.8f, 0.5f} }, OCIO::HUE_LUM); + auto ls = OCIO::GradingBSplineCurve::Create({ {0.05f, 1.5f}, {0.5f, 0.9f}, {1.1f, 1.4f}, + }, OCIO::LUM_SAT); + auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, + OCIO::SAT_SAT); + auto ll = OCIO::GradingBSplineCurve::Create({ {-0.02f, -0.04f}, {0.2f, 0.1f}, {0.8f, 0.95f}, {1.1f, 1.2f} }, + OCIO::LUM_LUM); + auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, + OCIO::SAT_LUM); + auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, + { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); + + auto gc = std::make_shared(OCIO::GRADING_LOG, + hh, hs, hl, ls, ss, ll, sl, hfx); + OCIO::ConstOpCPURcPtr op; + OCIO::ConstGradingHueCurveOpDataRcPtr gcc = gc; + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + + constexpr long num_samples = 5; + float res[4 * num_samples]{ 0.f }; + + const float input_32f[] = { + 0.1f, 0.5f, 0.7f, 0.0f, + 0.6f, 0.9f, 0.8f, 0.5f, + 0.4f, 0.35f,0.3f, 0.f, + 0.4f,-0.2f,-0.05f, 0.f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + const float expected_32f[] = { + 0.651269494808f, 0.630018105394f, 1.331314732772f, 0.0f, + 0.787401154155f, 1.286561695129f, 1.274118545611f, 0.5f, + 0.317389674917f, 0.297787779440f, 0.242718507572f, 0.0f, + 0.830653473122f, 0.449246419743f, -0.173027078802f, 0.0f, + 0.004989255546f, -0.033773428950f, -0.019725339077f, 1.0f + }; + + // Test in forward direction. + + OCIO_CHECK_NO_THROW(op->apply(input_32f, res, num_samples)); + ValidateImage(expected_32f, res, num_samples, __LINE__); + + // Test in inverse direction. + + gc->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + OCIO_CHECK_NO_THROW(op->apply(res, res, num_samples)); + ValidateImage(input_32f, res, num_samples, __LINE__); +} + +OCIO_ADD_TEST(GradingHueCurveOpCPU, lin_all_curves) +{ + // All curves are non-identities. + auto hh = OCIO::GradingBSplineCurve::Create({ {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, + {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create({ {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, + {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create({ {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, + {0.5f, 0.8f}, {0.8f, 0.5f} }, OCIO::HUE_LUM); + auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, + OCIO::SAT_SAT); + auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, + OCIO::SAT_LUM); + auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, + { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); + // Adjust these two, relative to previous test, to work in f-stops. + auto ls = OCIO::GradingBSplineCurve::Create({ {-6.f, 0.9f}, {-3.f, 0.8f}, {0.f, 1.2f}, + {2.f, 1.f}, {4.f, 0.6f}, {6.f, 0.55f} }, OCIO::LUM_SAT); + auto ll = OCIO::GradingBSplineCurve::Create({ {-8.f, -7.f}, {-2.f, -3.f}, {2.f, 3.5f}, {8.f, 7.f} }, + OCIO::LUM_LUM); + + auto gc = std::make_shared(OCIO::GRADING_LIN, + hh, hs, hl, ls, ss, ll, sl, hfx); + OCIO::ConstOpCPURcPtr op; + OCIO::ConstGradingHueCurveOpDataRcPtr gcc = gc; + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + + constexpr long num_samples = 5; + float res[4 * num_samples]{ 0.f }; + + const float input_32f[] = { + 0.1f, 0.5f, 0.7f, 0.0f, + 0.6f, 0.9f, 0.8f, 0.5f, + 2.4f, 2.35f, 2.3f, 0.f, + 0.4f, 0.2f, -0.05f, 0.f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + const float expected_32f[] = { + 0.527229344453f, 0.490778616791f, 1.693653961874f, 0.f, + 1.253512415394f, 2.240034381083f, 2.215442212203f, 0.5f, + 6.983003751281f, 6.772174817271f, 6.179875164501f, 0.f, + 0.527554073346f, 0.360480028655f, -0.135388205576f, 0.f, + 0.011308048228f, -0.001711436982f, 0.003006990049f, 1.f + }; + + // Test in forward direction. + + OCIO_CHECK_NO_THROW(op->apply(input_32f, res, num_samples)); + ValidateImage(expected_32f, res, num_samples, __LINE__); + + // Test in inverse direction. + + gc->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + OCIO_CHECK_NO_THROW(op->apply(res, res, num_samples)); + ValidateImage(input_32f, res, num_samples, __LINE__); +} diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp index fa2fa6589b..ad00ade2ff 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp @@ -8,10 +8,10 @@ namespace OCIO = OCIO_NAMESPACE; -// Test coming from GradingRGBCurveOpData_tests.cpp OCIO_ADD_TEST(GradingHueCurveOpData, accessors) { - // Create GradingHueCurveOpData and check values. Changes them and check. + // Create GradingHueCurveOpData and check values. Change them and check. + OCIO::GradingHueCurveOpData gc( OCIO::GRADING_LOG ); static constexpr char expected[]{ "log forward " @@ -39,8 +39,9 @@ OCIO_ADD_TEST(GradingHueCurveOpData, accessors) gc.setBypassLinToLog(true); OCIO_CHECK_ASSERT(gc.getBypassLinToLog()); - // Get dynamic property as a generic dynamic property and as a type one and verify they are + // Get dynamic property as a generic dynamic property and as a typed one and verify they are // the same and can be made dynamic. + OCIO_CHECK_ASSERT(!gc.isDynamic()); auto dp = gc.getDynamicProperty(); OCIO_REQUIRE_ASSERT(dp); @@ -57,6 +58,7 @@ OCIO_ADD_TEST(GradingHueCurveOpData, accessors) OCIO_CHECK_EQUAL(gc.getDirection(), OCIO::TRANSFORM_DIR_INVERSE); // Test operator==. + OCIO::GradingHueCurveOpData gc1{ OCIO::GRADING_LIN }; OCIO::GradingHueCurveOpData gc2{ OCIO::GRADING_LIN }; @@ -74,19 +76,20 @@ OCIO_ADD_TEST(GradingHueCurveOpData, accessors) auto v1 = gc1.getValue()->createEditableCopy(); auto hueHue = v1->getCurve(OCIO::HUE_HUE); hueHue->setNumControlPoints(4); - hueHue->getControlPoint(3).m_x = hueHue->getControlPoint(2).m_x + 1.f; + hueHue->getControlPoint(3).m_x = hueHue->getControlPoint(2).m_x + 0.25f; hueHue->getControlPoint(3).m_y = hueHue->getControlPoint(2).m_y + 0.5f; gc1.setValue(v1); OCIO_CHECK_ASSERT(!(gc1 == gc2)); auto v2 = gc2.getValue()->createEditableCopy(); auto hueHue2 = v2->getCurve(OCIO::HUE_HUE); hueHue2->setNumControlPoints(4); - hueHue2->getControlPoint(3).m_x = hueHue2->getControlPoint(2).m_x + 1.f; + hueHue2->getControlPoint(3).m_x = hueHue2->getControlPoint(2).m_x + 0.25f; hueHue2->getControlPoint(3).m_y = hueHue2->getControlPoint(2).m_y + 0.5f; gc2.setValue(v2); OCIO_CHECK_ASSERT(gc1 == gc2); gc1.setSlope(OCIO::HUE_SAT, 2, 0.9f); + OCIO_CHECK_ASSERT(!(gc1 == gc2)); OCIO_CHECK_EQUAL(gc1.getSlope(OCIO::HUE_SAT, 2), 0.9f); OCIO_CHECK_ASSERT(gc1.slopesAreDefault(OCIO::HUE_LUM)); OCIO_CHECK_ASSERT(!gc1.slopesAreDefault(OCIO::HUE_SAT)); @@ -96,23 +99,40 @@ OCIO_ADD_TEST(GradingHueCurveOpData, accessors) // Check isInverse. - // We have equal ops, inverse one. - gc1.setDirection(OCIO::TRANSFORM_DIR_FORWARD); + // Make two equal non-identity ops, invert one. + OCIO::GradingHueCurveOpData gc3{ OCIO::GRADING_LIN }; + auto v3 = gc3.getValue()->createEditableCopy(); + auto spline = v3->getCurve(OCIO::HUE_LUM); + spline->setNumControlPoints(2); + spline->getControlPoint(0) = OCIO::GradingControlPoint(0.f, 2.f); + spline->getControlPoint(1) = OCIO::GradingControlPoint(0.9f, 2.f); + gc3.setValue(v3); + OCIO_CHECK_ASSERT(!gc3.isIdentity()); // Need a shared pointer for the parameter. - OCIO::ConstGradingHueCurveOpDataRcPtr gcptr2 = gc1.clone(); - gc1.setDirection(OCIO::TRANSFORM_DIR_INVERSE); - OCIO_CHECK_ASSERT(gc1.isInverse(gcptr2)); - // Change value of one: no longer inverse. - hueHue->getControlPoint(3).m_y += 0.1f; - gc1.setValue(v1); - OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); + OCIO::ConstGradingHueCurveOpDataRcPtr gcptr3 = gc3.clone(); + gc3.setDirection(OCIO::TRANSFORM_DIR_INVERSE); + // They start as inverses. + OCIO_CHECK_ASSERT(gc3.isInverse(gcptr3)); + + // Change value of one: no longer an inverse. + spline->getControlPoint(1).m_y += 0.25f; + gc3.setValue(v3); + OCIO_CHECK_ASSERT(!gc3.isInverse(gcptr3)); // Restore value. - hueHue->getControlPoint(3).m_y -= 0.1f; - gc1.setValue(v1); - OCIO_CHECK_ASSERT(gc1.isInverse(gcptr2)); - // Change direction: no longer inverse. - gc1.setDirection(OCIO::TRANSFORM_DIR_FORWARD); - OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); + spline->getControlPoint(1).m_y -= 0.25f; + gc3.setValue(v3); + OCIO_CHECK_ASSERT(gc3.isInverse(gcptr3)); + + // Change slope of one: no longer an inverse. + gc3.setSlope(OCIO::HUE_SAT, 2, 0.9f); + OCIO_CHECK_ASSERT(!gc3.isInverse(gcptr3)); + // Restore value. + gc3.setSlope(OCIO::HUE_SAT, 2, 0.f); + OCIO_CHECK_ASSERT(gc3.isInverse(gcptr3)); + + // Change direction: no longer an inverse. + gc3.setDirection(OCIO::TRANSFORM_DIR_FORWARD); + OCIO_CHECK_ASSERT(!gc3.isInverse(gcptr3)); } OCIO_ADD_TEST(GradingHueCurveOpData, validate) @@ -123,22 +143,28 @@ OCIO_ADD_TEST(GradingHueCurveOpData, validate) // Curves with a single control point are not valid. auto curve = OCIO::GradingBSplineCurve::Create(1); - auto curves = OCIO::GradingHueCurve::Create(curve, curve, curve, curve, curve, curve, curve, curve); - OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, - "There must be at least 2 control points."); + OCIO_CHECK_THROW_WHAT(auto curves = OCIO::GradingHueCurve::Create( + curve, curve, curve, curve, curve, curve, curve, curve), + OCIO::Exception, "There must be at least 2 control points."); + + // A periodic curve may not have only two control points that wrap to the same point. + curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 1.f,0.f } }, OCIO::HUE_FX); + OCIO_CHECK_THROW_WHAT(auto curves = OCIO::GradingHueCurve::Create( + curve, curve, curve, curve, curve, curve, curve, curve), + OCIO::Exception, "The periodic spline x coordinates may not wrap to the same value."); // Curve x coordinates have to increase. curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.7f,0.3f }, { 0.5f,0.7f },{ 1.f,1.f } }); - - curves = OCIO::GradingHueCurve::Create(curve, curve, curve, curve, curve, curve, curve, curve); - OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, - "has a x coordinate '0.5' that is less from previous control " - "point x cooordinate '0.7'."); + OCIO_CHECK_THROW_WHAT(auto curves = OCIO::GradingHueCurve::Create( + curve, curve, curve, curve, curve, curve, curve, curve), + OCIO::Exception, "has a x coordinate '0.5' that is less than previous control " + "point x cooordinate '0.7'."); // Fix the curve x coordinate. curve->getControlPoint(1).m_x = 0.3f; - curves = OCIO::GradingHueCurve::Create(curve, curve, curve, curve, curve, curve, curve, curve); - OCIO_CHECK_NO_THROW(gc.setValue(curves)); - OCIO_CHECK_NO_THROW(gc.validate()); + // But the curves are not of the correct BSplineType. + OCIO_CHECK_THROW_WHAT(auto curves = OCIO::GradingHueCurve::Create( + curve, curve, curve, curve, curve, curve, curve, curve), + OCIO::Exception, "GradingHueCurve validation failed: 'hue_hue' curve is of the wrong BSplineType."); } diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOp_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOp_tests.cpp index 41dee69048..1c6e56661d 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOp_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOp_tests.cpp @@ -12,127 +12,132 @@ namespace OCIO = OCIO_NAMESPACE; OCIO_ADD_TEST(GradingHueCurveOp, create) { - //OCIO::TransformDirection direction = OCIO::TRANSFORM_DIR_FORWARD; - //OCIO::GradingHueCurveOpDataRcPtr data = - // std::make_shared(); - //OCIO::OpRcPtrVec ops; -// - //OCIO_CHECK_NO_THROW(OCIO::CreateGradingHueCurveOp(ops, data, direction)); - //OCIO_REQUIRE_EQUAL(ops.size(), 1); - //OCIO_REQUIRE_ASSERT(ops[0]); - //OCIO_CHECK_EQUAL(ops[0]->getInfo(), ""); - //OCIO_CHECK_ASSERT(ops[0]->isIdentity()); - //OCIO_CHECK_ASSERT(ops[0]->isNoOp()); - - //data->getDynamicPropertyInternal()->makeDynamic(); - //OCIO_CHECK_NO_THROW(OCIO::CreateGradingRGBCurveOp(ops, data, direction)); - //OCIO_REQUIRE_EQUAL(ops.size(), 2); - //OCIO_REQUIRE_ASSERT(ops[1]); - //OCIO_CHECK_EQUAL(ops[1]->getInfo(), ""); - //OCIO_CHECK_ASSERT(!ops[1]->isIdentity()); - //OCIO_CHECK_ASSERT(!ops[1]->isNoOp()); + OCIO::TransformDirection direction = OCIO::TRANSFORM_DIR_FORWARD; + OCIO::GradingHueCurveOpDataRcPtr data = + std::make_shared(OCIO::GRADING_LOG); + OCIO::OpRcPtrVec ops; + + OCIO_CHECK_NO_THROW(OCIO::CreateGradingHueCurveOp(ops, data, direction)); + OCIO_REQUIRE_EQUAL(ops.size(), 1); + OCIO_REQUIRE_ASSERT(ops[0]); + OCIO_CHECK_EQUAL(ops[0]->getInfo(), ""); + OCIO_CHECK_ASSERT(ops[0]->isIdentity()); + OCIO_CHECK_ASSERT(ops[0]->isNoOp()); + + data->getDynamicPropertyInternal()->makeDynamic(); + OCIO_CHECK_NO_THROW(OCIO::CreateGradingHueCurveOp(ops, data, direction)); + OCIO_REQUIRE_EQUAL(ops.size(), 2); + OCIO_REQUIRE_ASSERT(ops[1]); + OCIO_CHECK_EQUAL(ops[1]->getInfo(), ""); + OCIO_CHECK_ASSERT(!ops[1]->isIdentity()); + OCIO_CHECK_ASSERT(!ops[1]->isNoOp()); } OCIO_ADD_TEST(GradingHueCurveOp, create_transform) { - //OCIO::TransformDirection direction = OCIO::TRANSFORM_DIR_FORWARD; - //OCIO::GradingHueCurveOpDataRcPtr data = - // std::make_shared(); - ////data->getDynamicPropertyInternal()->makeDynamic(); - //OCIO::OpRcPtrVec ops; -// - //OCIO_CHECK_NO_THROW(OCIO::CreateGradingHueCurveOp(ops, data, direction)); - //OCIO_REQUIRE_EQUAL(ops.size(), 1); - - //OCIO::GroupTransformRcPtr group = OCIO::GroupTransform::Create(); -// - //OCIO::ConstOpRcPtr op(ops[0]); -// - //OCIO::CreateGradingRGBCurveTransform(group, op); - //OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 1); - //auto transform = group->getTransform(0); - //OCIO_REQUIRE_ASSERT(transform); - //auto gcTransform = OCIO_DYNAMIC_POINTER_CAST(transform); - //OCIO_REQUIRE_ASSERT(gcTransform); - //OCIO_CHECK_EQUAL(gcTransform->getStyle(), OCIO::GRADING_LOG); - //OCIO_CHECK_ASSERT(gcTransform->isDynamic()); + OCIO::TransformDirection direction = OCIO::TRANSFORM_DIR_FORWARD; + OCIO::GradingHueCurveOpDataRcPtr data = + std::make_shared(OCIO::GRADING_LOG); + data->getDynamicPropertyInternal()->makeDynamic(); + OCIO::OpRcPtrVec ops; + + OCIO_CHECK_NO_THROW(OCIO::CreateGradingHueCurveOp(ops, data, direction)); + OCIO_REQUIRE_EQUAL(ops.size(), 1); + + OCIO::GroupTransformRcPtr group = OCIO::GroupTransform::Create(); + + OCIO::ConstOpRcPtr op(ops[0]); + + OCIO::CreateGradingHueCurveTransform(group, op); + OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 1); + auto transform = group->getTransform(0); + OCIO_REQUIRE_ASSERT(transform); + auto gcTransform = OCIO_DYNAMIC_POINTER_CAST(transform); + OCIO_REQUIRE_ASSERT(gcTransform); + OCIO_CHECK_EQUAL(gcTransform->getStyle(), OCIO::GRADING_LOG); + OCIO_CHECK_ASSERT(gcTransform->isDynamic()); } OCIO_ADD_TEST(GradingHueCurveOp, build_ops) { - //OCIO::ConstConfigRcPtr config = OCIO::Config::CreateRaw(); -// - //auto gcTransform = OCIO::GradingHueCurveTransform::Create(); - //OCIO_CHECK_ASSERT(gcTransform.get()); -// - //// Identity does create an op. - //OCIO::OpRcPtrVec ops; - //OCIO::BuildOps(ops, *(config.get()), config->getCurrentContext(), gcTransform, - // OCIO::TRANSFORM_DIR_FORWARD); - //OCIO_REQUIRE_EQUAL(ops.size(), 1); - //OCIO_CHECK_ASSERT(ops[0]->isIdentity()); - //OCIO_CHECK_ASSERT(ops[0]->isNoOp()); - //ops.clear(); -// - //// Make it dynamic and keep default values. - //gcTransform->makeDynamic(); -// - //OCIO::BuildOps(ops, *(config.get()), config->getCurrentContext(), gcTransform, - // OCIO::TRANSFORM_DIR_FORWARD); -// - //OCIO_REQUIRE_EQUAL(ops.size(), 1); - //OCIO_REQUIRE_ASSERT(ops[0]); - //OCIO_CHECK_EQUAL(ops[0]->getInfo(), ""); - //OCIO::ConstGradingRGBCurveOpRcPtr gco = OCIO_DYNAMIC_POINTER_CAST(ops[0]); - //auto data = gco->data(); - //OCIO_REQUIRE_ASSERT(data); - //auto gcd = OCIO_DYNAMIC_POINTER_CAST(data); - //OCIO_REQUIRE_ASSERT(gcd); - //OCIO_CHECK_ASSERT(gcd->isDynamic()); -// - //auto valsOp = gcd->getValue(); - //OCIO_CHECK_EQUAL(3, valsOp->getCurve(OCIO::RGB_GREEN)->getNumControlPoints()); -// - //// Create processor with dynamic identity before changing the transform. - //auto proc = config->getProcessor(gcTransform); - //OCIO_CHECK_ASSERT(proc->hasDynamicProperty(OCIO::DYNAMIC_PROPERTY_GRADING_RGBCURVE)); - //OCIO_CHECK_ASSERT(!proc->hasDynamicProperty(OCIO::DYNAMIC_PROPERTY_EXPOSURE)); -// - //auto cpu = proc->getDefaultCPUProcessor(); -// - //// Sharing of dynamic properties is done through processor, changing the source will not - //// change the op. - //auto curve = OCIO::GradingBSplineCurve::Create({ { 0.f,1.f },{ 0.2f,0.3f }, - // { 0.5f,0.8f },{ 2.f,1.5f } }); - //auto rgbCurve = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); - //gcTransform->setValue(rgbCurve); -// - //// Still use the default identity curves. - //valsOp = gcd->getValue(); - //OCIO_CHECK_EQUAL(3, valsOp->getCurve(OCIO::RGB_GREEN)->getNumControlPoints()); -// - //// Get dynamic property from the CPU proc. - //OCIO::DynamicPropertyRcPtr dp; - //OCIO_CHECK_NO_THROW(dp = cpu->getDynamicProperty(OCIO::DYNAMIC_PROPERTY_GRADING_RGBCURVE)); - //OCIO_REQUIRE_ASSERT(dp); - //// Get typed value accessor. - //auto dpgc = OCIO_DYNAMIC_POINTER_CAST(dp); - //OCIO_REQUIRE_ASSERT(dpgc); -// - //float pixel[]{ 0.0f, 0.2f, 2.0f }; - //cpu->applyRGB(pixel); - //// Default values are identity. - //static constexpr float error = 1e-5f; - //OCIO_CHECK_CLOSE(pixel[0], 0.0f, error); - //OCIO_CHECK_CLOSE(pixel[1], 0.2f, error); - //OCIO_CHECK_CLOSE(pixel[2], 2.0f, error); -// - //// Use other curve that has 4 control points. - //dpgc->setValue(rgbCurve); -// - //// Control point has moved. - //cpu->applyRGB(pixel); - //OCIO_CHECK_CLOSE(pixel[0], 1.11148262f, error); - //OCIO_CHECK_CLOSE(pixel[1], 0.04518771f, error); - //OCIO_CHECK_CLOSE(pixel[2], 1.32527864f, error); + OCIO::ConstConfigRcPtr config = OCIO::Config::CreateRaw(); + + auto gcTransform = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); + OCIO_CHECK_ASSERT(gcTransform.get()); + + // Identity does create an op. + OCIO::OpRcPtrVec ops; + OCIO::BuildOps(ops, *(config.get()), config->getCurrentContext(), gcTransform, + OCIO::TRANSFORM_DIR_FORWARD); + OCIO_REQUIRE_EQUAL(ops.size(), 1); + OCIO_CHECK_ASSERT(ops[0]->isIdentity()); + OCIO_CHECK_ASSERT(ops[0]->isNoOp()); + ops.clear(); + + // Make it dynamic and keep default values. + gcTransform->makeDynamic(); + + OCIO::BuildOps(ops, *(config.get()), config->getCurrentContext(), gcTransform, + OCIO::TRANSFORM_DIR_FORWARD); + + OCIO_REQUIRE_EQUAL(ops.size(), 1); + OCIO_REQUIRE_ASSERT(ops[0]); + OCIO_CHECK_EQUAL(ops[0]->getInfo(), ""); + OCIO::ConstGradingHueCurveOpRcPtr gco = OCIO_DYNAMIC_POINTER_CAST(ops[0]); + auto data = gco->data(); + OCIO_REQUIRE_ASSERT(data); + auto gcd = OCIO_DYNAMIC_POINTER_CAST(data); + OCIO_REQUIRE_ASSERT(gcd); + OCIO_CHECK_ASSERT(gcd->isDynamic()); + + auto valsOp = gcd->getValue(); + OCIO_CHECK_EQUAL(6, valsOp->getCurve(OCIO::HUE_HUE)->getNumControlPoints()); + + // Create processor with dynamic identity before changing the transform. + auto proc = config->getProcessor(gcTransform); + OCIO_CHECK_ASSERT(proc->hasDynamicProperty(OCIO::DYNAMIC_PROPERTY_GRADING_HUECURVE)); + OCIO_CHECK_ASSERT(!proc->hasDynamicProperty(OCIO::DYNAMIC_PROPERTY_EXPOSURE)); + + auto cpu = proc->getDefaultCPUProcessor(); + + // Create a non-identity curve. + auto hueCurve = OCIO::GradingHueCurve::Create(OCIO::GRADING_LOG); + OCIO::GradingBSplineCurveRcPtr spline = hueCurve->getCurve(OCIO::HUE_LUM); + // Add a constant offset to all hues. + spline->setNumControlPoints(2); + spline->getControlPoint(0) = OCIO::GradingControlPoint(0.f, 2.f); + spline->getControlPoint(1) = OCIO::GradingControlPoint(0.9f, 2.f); + OCIO_CHECK_ASSERT(!hueCurve->isIdentity()); + + // Sharing of dynamic properties is done through processor, changing the source transform + // does not change the op that was created from it. + gcTransform->setValue(hueCurve); + valsOp = gcd->getValue(); + // (Still has its original value.) + OCIO_CHECK_EQUAL(1.f, valsOp->getCurve(OCIO::HUE_LUM)->getControlPoint(0).m_y); + + // Get dynamic property from the CPU processor. + OCIO::DynamicPropertyRcPtr dp; + OCIO_CHECK_NO_THROW(dp = cpu->getDynamicProperty(OCIO::DYNAMIC_PROPERTY_GRADING_HUECURVE)); + OCIO_REQUIRE_ASSERT(dp); + // Get typed value accessor. + auto dpgc = OCIO_DYNAMIC_POINTER_CAST(dp); + OCIO_REQUIRE_ASSERT(dpgc); + + float pixel[]{ 0.0f, 0.2f, 2.0f }; + cpu->applyRGB(pixel); + // Default values are identity. + static constexpr float error = 1e-5f; + OCIO_CHECK_CLOSE(pixel[0], 0.0f, error); + OCIO_CHECK_CLOSE(pixel[1], 0.2f, error); + OCIO_CHECK_CLOSE(pixel[2], 2.0f, error); + + // Set the modified curve. + dpgc->setValue(hueCurve); + + // The pixel value is different. + cpu->applyRGB(pixel); + OCIO_CHECK_CLOSE(pixel[0], 0.1f, error); + OCIO_CHECK_CLOSE(pixel[1], 0.3f, error); + OCIO_CHECK_CLOSE(pixel[2], 2.1f, error); } diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurve_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurve_tests.cpp index 96cbf64b14..713c63afa9 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurve_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurve_tests.cpp @@ -11,120 +11,162 @@ namespace OCIO = OCIO_NAMESPACE; OCIO_ADD_TEST(GradingHueCurve, basic) { - //auto curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.2f,0.2f }, - // { 0.5f,0.7f },{ 1.f,1.f } }); - //OCIO::ConstGradingBSplineCurveRcPtr curveR = curve; - //OCIO_CHECK_EQUAL(0.2f, curveR->getControlPoint(1).m_y); - //curve->getControlPoint(1).m_y = 0.3f; - //OCIO_CHECK_EQUAL(0.3f, curveR->getControlPoint(1).m_y); - //OCIO::ConstGradingBSplineCurveRcPtr curveG = OCIO::GradingBSplineCurve::Create(4); - //OCIO::ConstGradingBSplineCurveRcPtr curveB = OCIO::GradingBSplineCurve::Create(3); - //OCIO::ConstGradingBSplineCurveRcPtr curveM = OCIO::GradingBSplineCurve::Create(2); - //// The Create function takes 4 pointers to curves and creates new curves that are copies of - //// the 4 parameters. - //auto rgbCurve = OCIO::GradingRGBCurve::Create(curveR, curveG, curveB, curveM); - //OCIO_REQUIRE_ASSERT(rgbCurve); - //OCIO_REQUIRE_ASSERT(rgbCurve->getCurve(OCIO::RGB_RED)); - //OCIO_REQUIRE_ASSERT(rgbCurve->getCurve(OCIO::RGB_GREEN)); - //OCIO_REQUIRE_ASSERT(rgbCurve->getCurve(OCIO::RGB_BLUE)); - //OCIO_REQUIRE_ASSERT(rgbCurve->getCurve(OCIO::RGB_MASTER)); - //OCIO_CHECK_THROW_WHAT(rgbCurve->getCurve(OCIO::RGB_NUM_CURVES), OCIO::Exception, - // "Invalid curve."); - //auto copiedCurve = rgbCurve->getCurve(OCIO::RGB_RED); - //OCIO_CHECK_EQUAL(0.3f, copiedCurve->getControlPoint(1).m_y); - //curve->getControlPoint(1).m_y = 0.4f; - //OCIO_CHECK_EQUAL(0.3f, copiedCurve->getControlPoint(1).m_y); -// - //// Test default curves. - //auto rgbCurveLin = OCIO::GradingRGBCurve::Create(OCIO::GRADING_LIN); - //auto rgbCurveLog = OCIO::GradingRGBCurve::Create(OCIO::GRADING_LOG); - //auto rgbCurveVideo = OCIO::GradingRGBCurve::Create(OCIO::GRADING_VIDEO); - //OCIO_REQUIRE_ASSERT(rgbCurveLin && rgbCurveLog && rgbCurveVideo); - //OCIO_CHECK_ASSERT(*rgbCurveLog == *rgbCurveVideo); - //OCIO_CHECK_ASSERT(*rgbCurveLog != *rgbCurveLin); - //OCIO_CHECK_ASSERT(*rgbCurveLog->getCurve(OCIO::RGB_RED) == - // *rgbCurveLog->getCurve(OCIO::RGB_GREEN)); - //OCIO_CHECK_ASSERT(*rgbCurveLog->getCurve(OCIO::RGB_RED) == - // *rgbCurveLog->getCurve(OCIO::RGB_BLUE)); - //OCIO_CHECK_ASSERT(*rgbCurveLog->getCurve(OCIO::RGB_RED) == - // *rgbCurveLog->getCurve(OCIO::RGB_MASTER)); - //OCIO_CHECK_EQUAL(3, rgbCurveLog->getCurve(OCIO::RGB_RED)->getNumControlPoints()); - //OCIO_CHECK_EQUAL(0.f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(0).m_x); - //OCIO_CHECK_EQUAL(0.f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(0).m_y); - //OCIO_CHECK_EQUAL(0.5f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(1).m_x); - //OCIO_CHECK_EQUAL(0.5f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(1).m_y); - //OCIO_CHECK_EQUAL(1.f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(2).m_x); - //OCIO_CHECK_EQUAL(1.f, rgbCurveLog->getCurve(OCIO::RGB_RED)->getControlPoint(2).m_y); -// - //OCIO_CHECK_ASSERT(*rgbCurveLin->getCurve(OCIO::RGB_RED) == - // *rgbCurveLin->getCurve(OCIO::RGB_GREEN)); - //OCIO_CHECK_ASSERT(*rgbCurveLin->getCurve(OCIO::RGB_RED) == - // *rgbCurveLin->getCurve(OCIO::RGB_BLUE)); - //OCIO_CHECK_ASSERT(*rgbCurveLin->getCurve(OCIO::RGB_RED) == - // *rgbCurveLin->getCurve(OCIO::RGB_MASTER)); - //OCIO_CHECK_EQUAL(3, rgbCurveLin->getCurve(OCIO::RGB_RED)->getNumControlPoints()); - //OCIO_CHECK_EQUAL(-7.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(0).m_x); - //OCIO_CHECK_EQUAL(-7.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(0).m_y); - //OCIO_CHECK_EQUAL(0.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(1).m_x); - //OCIO_CHECK_EQUAL(0.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(1).m_y); - //OCIO_CHECK_EQUAL(7.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(2).m_x); - //OCIO_CHECK_EQUAL(7.f, rgbCurveLin->getCurve(OCIO::RGB_RED)->getControlPoint(2).m_y); -// - //auto rgbCurveLinCopy = OCIO::GradingRGBCurve::Create(rgbCurveLin); - //OCIO_REQUIRE_ASSERT(rgbCurveLinCopy); - //OCIO_CHECK_ASSERT(*rgbCurveLin == *rgbCurveLinCopy); -// - //rgbCurveLinCopy = rgbCurveLin->createEditableCopy(); - //OCIO_REQUIRE_ASSERT(rgbCurveLinCopy); - //OCIO_CHECK_ASSERT(*rgbCurveLin == *rgbCurveLinCopy); -// - //std::ostringstream oss; - //oss << *rgbCurveLin; - //OCIO_CHECK_EQUAL(std::string("]>, " - // "green=]>, " - // "blue=]>, " - // "master=]>>"), - // oss.str()); + auto curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.2f,0.2f }, + { 0.5f,0.7f },{ 1.f,1.f } }, OCIO::HUE_HUE); + OCIO::ConstGradingBSplineCurveRcPtr curveHH = curve; + OCIO_CHECK_EQUAL(0.2f, curveHH->getControlPoint(1).m_y); + curve->getControlPoint(1).m_y = 0.3f; + OCIO_CHECK_EQUAL(0.3f, curveHH->getControlPoint(1).m_y); + OCIO::ConstGradingBSplineCurveRcPtr curveHS = OCIO::GradingBSplineCurve::Create(4, OCIO::HUE_SAT); + OCIO::ConstGradingBSplineCurveRcPtr curveHL = OCIO::GradingBSplineCurve::Create(3, OCIO::HUE_LUM); + OCIO::ConstGradingBSplineCurveRcPtr curveLS = OCIO::GradingBSplineCurve::Create(2, OCIO::LUM_SAT); + OCIO::ConstGradingBSplineCurveRcPtr curveSS = OCIO::GradingBSplineCurve::Create(2, OCIO::SAT_SAT); + OCIO::ConstGradingBSplineCurveRcPtr curveLL = OCIO::GradingBSplineCurve::Create(2, OCIO::LUM_LUM); + OCIO::ConstGradingBSplineCurveRcPtr curveSL = OCIO::GradingBSplineCurve::Create(2, OCIO::SAT_LUM); + OCIO::ConstGradingBSplineCurveRcPtr curveHFX = OCIO::GradingBSplineCurve::Create(2, OCIO::HUE_FX); + + // The Create function takes 8 pointers to curves and creates new curves that are copies of + // the 8 parameters. First try to create one using curveHH instead of curveSS. + OCIO_CHECK_THROW_WHAT(auto badHueCurve = OCIO::GradingHueCurve::Create(curveHH, curveHS, curveHL, curveLS, + curveHH, curveLL, curveSL, curveHFX), + OCIO::Exception, + "GradingHueCurve validation failed: 'sat_sat' curve is of the wrong BSplineType."); + + // Now create a valid one. + auto hueCurve = OCIO::GradingHueCurve::Create(curveHH, curveHS, curveHL, curveLS, + curveSS, curveLL, curveSL, curveHFX); + OCIO_REQUIRE_ASSERT(hueCurve); + OCIO_CHECK_NO_THROW(hueCurve->validate()); + + OCIO_REQUIRE_ASSERT(hueCurve->getCurve(OCIO::HUE_HUE)); + OCIO_REQUIRE_ASSERT(hueCurve->getCurve(OCIO::HUE_SAT)); + OCIO_REQUIRE_ASSERT(hueCurve->getCurve(OCIO::HUE_LUM)); + OCIO_REQUIRE_ASSERT(hueCurve->getCurve(OCIO::LUM_SAT)); + OCIO_REQUIRE_ASSERT(hueCurve->getCurve(OCIO::SAT_SAT)); + OCIO_REQUIRE_ASSERT(hueCurve->getCurve(OCIO::LUM_LUM)); + OCIO_REQUIRE_ASSERT(hueCurve->getCurve(OCIO::SAT_LUM)); + OCIO_REQUIRE_ASSERT(hueCurve->getCurve(OCIO::HUE_FX)); + OCIO_CHECK_THROW_WHAT(hueCurve->getCurve(OCIO::HUE_NUM_CURVES), OCIO::Exception, + "The HueCurveType provided is illegal"); + + // Validate that the Create function made copies of its curve arguments. + OCIO::GradingBSplineCurveRcPtr copiedCurve = hueCurve->getCurve(OCIO::HUE_HUE); + OCIO_CHECK_EQUAL(0.3f, copiedCurve->getControlPoint(1).m_y); + curve->getControlPoint(1).m_y = 0.4f; + OCIO_CHECK_EQUAL(0.4f, curve->getControlPoint(1).m_y); + OCIO_CHECK_EQUAL(0.3f, copiedCurve->getControlPoint(1).m_y); + + // Set the type to the wrong BSpline type and re-validate. + copiedCurve->setSplineType(OCIO::DIAGONAL_B_SPLINE); + OCIO_CHECK_THROW_WHAT(hueCurve->validate(), OCIO::Exception, + "GradingHueCurve validation failed: 'hue_hue' curve is of the wrong BSplineType."); + + // Test default curves. + auto hueCurveLin = OCIO::GradingHueCurve::Create(OCIO::GRADING_LIN); + auto hueCurveLog = OCIO::GradingHueCurve::Create(OCIO::GRADING_LOG); + auto hueCurveVideo = OCIO::GradingHueCurve::Create(OCIO::GRADING_VIDEO); + OCIO_REQUIRE_ASSERT(hueCurveLin && hueCurveLog && hueCurveVideo); + OCIO_CHECK_ASSERT(*hueCurveLog == *hueCurveVideo); + OCIO_CHECK_ASSERT(*hueCurveLog != *hueCurveLin); + + OCIO_CHECK_ASSERT(*hueCurveLog->getCurve(OCIO::HUE_LUM) == + *hueCurveLog->getCurve(OCIO::HUE_SAT)); + OCIO_CHECK_ASSERT(*hueCurveLog->getCurve(OCIO::SAT_SAT) == + *hueCurveLog->getCurve(OCIO::LUM_LUM)); + OCIO_CHECK_ASSERT(*hueCurveLog->getCurve(OCIO::LUM_SAT) == + *hueCurveLog->getCurve(OCIO::SAT_LUM)); + OCIO_CHECK_ASSERT(*hueCurveLog->getCurve(OCIO::HUE_HUE) != + *hueCurveLog->getCurve(OCIO::HUE_SAT)); + OCIO_CHECK_EQUAL(3, hueCurveLog->getCurve(OCIO::LUM_LUM)->getNumControlPoints()); + OCIO_CHECK_EQUAL(0.f, hueCurveLog->getCurve(OCIO::LUM_LUM)->getControlPoint(0).m_x); + OCIO_CHECK_EQUAL(0.f, hueCurveLog->getCurve(OCIO::LUM_LUM)->getControlPoint(0).m_y); + OCIO_CHECK_EQUAL(0.5f, hueCurveLog->getCurve(OCIO::LUM_LUM)->getControlPoint(1).m_x); + OCIO_CHECK_EQUAL(0.5f, hueCurveLog->getCurve(OCIO::LUM_LUM)->getControlPoint(1).m_y); + OCIO_CHECK_EQUAL(1.f, hueCurveLog->getCurve(OCIO::LUM_LUM)->getControlPoint(2).m_x); + OCIO_CHECK_EQUAL(1.f, hueCurveLog->getCurve(OCIO::LUM_LUM)->getControlPoint(2).m_y); + + OCIO_CHECK_ASSERT(*hueCurveLin->getCurve(OCIO::HUE_LUM) == + *hueCurveLin->getCurve(OCIO::HUE_SAT)); + OCIO_CHECK_ASSERT(*hueCurveLin->getCurve(OCIO::SAT_SAT) != + *hueCurveLin->getCurve(OCIO::LUM_LUM)); + OCIO_CHECK_ASSERT(*hueCurveLin->getCurve(OCIO::LUM_SAT) != + *hueCurveLin->getCurve(OCIO::SAT_LUM)); + OCIO_CHECK_ASSERT(*hueCurveLin->getCurve(OCIO::HUE_HUE) != + *hueCurveLin->getCurve(OCIO::HUE_SAT)); + OCIO_CHECK_EQUAL(3, hueCurveLin->getCurve(OCIO::LUM_LUM)->getNumControlPoints()); + OCIO_CHECK_EQUAL(-7.f, hueCurveLin->getCurve(OCIO::LUM_LUM)->getControlPoint(0).m_x); + OCIO_CHECK_EQUAL(-7.f, hueCurveLin->getCurve(OCIO::LUM_LUM)->getControlPoint(0).m_y); + OCIO_CHECK_EQUAL(0.f, hueCurveLin->getCurve(OCIO::LUM_LUM)->getControlPoint(1).m_x); + OCIO_CHECK_EQUAL(0.f, hueCurveLin->getCurve(OCIO::LUM_LUM)->getControlPoint(1).m_y); + OCIO_CHECK_EQUAL(7.f, hueCurveLin->getCurve(OCIO::LUM_LUM)->getControlPoint(2).m_x); + OCIO_CHECK_EQUAL(7.f, hueCurveLin->getCurve(OCIO::LUM_LUM)->getControlPoint(2).m_y); + + // Validate that the other Create function made copies of its arguments. + auto hueCurveLinCopy = OCIO::GradingHueCurve::Create(hueCurveLin); + OCIO_REQUIRE_ASSERT(hueCurveLinCopy); + OCIO_CHECK_ASSERT(hueCurveLin != hueCurveLinCopy); + // Use overloaded op== to compare the contents of the curves. + OCIO_CHECK_ASSERT(*hueCurveLin == *hueCurveLinCopy); + + // Test createEditableCopy. + hueCurveLinCopy = hueCurveLin->createEditableCopy(); + OCIO_REQUIRE_ASSERT(hueCurveLinCopy); + OCIO_CHECK_ASSERT(hueCurveLin != hueCurveLinCopy); + OCIO_CHECK_ASSERT(*hueCurveLin == *hueCurveLinCopy); + + // Test op<<. + std::ostringstream oss; + oss << *hueCurveLin; + OCIO_CHECK_EQUAL(std::string( + "" + "]>, " + "hue_sat=" + "]>, " + "hue_lum=" + "]>, " + "lum_sat=]>, " + "sat_sat=]>, " + "lum_lum=]>, " + "sat_lum=]>, " + "hue_fx=" + "]>>"), + oss.str()); } OCIO_ADD_TEST(GradingHueCurve, curves) { - //auto curves = OCIO::GradingRGBCurve::Create(OCIO::GRADING_VIDEO); - //OCIO_CHECK_ASSERT(curves->isIdentity()); - //// Use non const curve accessor to modify one of the spline of the curves. - //OCIO::GradingBSplineCurveRcPtr spline = curves->getCurve(OCIO::RGB_GREEN); - //spline->setNumControlPoints(4); - //spline->getControlPoint(3).m_x = 1.1f; - //spline->getControlPoint(3).m_y = 2.f; - //OCIO_CHECK_ASSERT(!curves->isIdentity()); - //spline->getControlPoint(3).m_x = 2.f; - //OCIO_CHECK_ASSERT(curves->isIdentity()); - //OCIO_CHECK_EQUAL(curves->getCurve(OCIO::RGB_GREEN)->getNumControlPoints(), 4); -// - //// Changing the pointer does not change the curves. - //auto newSpline = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 1.f,2.f } }); - //spline = newSpline; - //OCIO_CHECK_EQUAL(curves->getCurve(OCIO::RGB_GREEN)->getNumControlPoints(), 4); + auto curves = OCIO::GradingHueCurve::Create(OCIO::GRADING_VIDEO); + OCIO_CHECK_ASSERT(curves->isIdentity()); + // Use non const curve accessor to modify one of the spline curves. + OCIO::GradingBSplineCurveRcPtr spline = curves->getCurve(OCIO::HUE_SAT); + OCIO_CHECK_EQUAL(curves->getCurve(OCIO::HUE_SAT)->getNumControlPoints(), 6); + // For this spline type, all y values must be 1. to be an identity. + OCIO_CHECK_ASSERT(curves->isIdentity()); + spline->getControlPoint(3).m_x = 0.9f; + spline->getControlPoint(3).m_y = 1.1f; + OCIO_CHECK_ASSERT(!curves->isIdentity()); + spline->getControlPoint(3).m_y = 1.f; + OCIO_CHECK_ASSERT(curves->isIdentity()); + spline->setNumControlPoints(4); + OCIO_CHECK_EQUAL(4, spline->getNumControlPoints()); + + // Changing the pointer does not change the curves. + auto newSpline = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 1.f,2.f } }); + spline = newSpline; + OCIO_CHECK_EQUAL(curves->getCurve(OCIO::HUE_SAT)->getNumControlPoints(), 4); } OCIO_ADD_TEST(GradingHueCurve, max_ctrl_pnts) { - //auto curveR = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, - // { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); -// - //auto curveG = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, - // { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); -// - //auto curveB = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, - // { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); -// - //auto curveM = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, - // { 8.f, 10.f },{ 9.f, 10.5f },{ 11.f, 15.f },{ 12.f, 50.f },{ 14.f, 60.f },{ 15.f, 85.f } }); -// - //auto rgbCurve = OCIO::GradingRGBCurve::Create(curveR, curveG, curveB, curveM); - //OCIO_REQUIRE_ASSERT(rgbCurve); -// - //OCIO::DynamicPropertyGradingRGBCurveImplRcPtr res; - //OCIO_CHECK_THROW_WHAT(res = std::make_shared(rgbCurve, false), - // OCIO::Exception, "RGB curve: maximum number of control points reached"); + auto hueCurve = OCIO::GradingHueCurve::Create(OCIO::GRADING_VIDEO); + for (int c = 0; c < OCIO::HUE_NUM_CURVES; ++c) + { + // Use non const curve accessor to modify the curves. + OCIO::GradingBSplineCurveRcPtr spline = hueCurve->getCurve(static_cast(c)); + spline->setNumControlPoints(26); + } + + OCIO::DynamicPropertyGradingHueCurveImplRcPtr res; + OCIO_CHECK_THROW_WHAT(res = std::make_shared(hueCurve, false), + OCIO::Exception, "Hue curve: maximum number of control points reached"); } diff --git a/tests/cpu/ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp b/tests/cpu/ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp index 79ed001733..2be380eeb4 100644 --- a/tests/cpu/ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp +++ b/tests/cpu/ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp @@ -8,6 +8,10 @@ namespace OCIO = OCIO_NAMESPACE; +// +// Please see DynamicProperty_tests.cpp for tests of the actual spline fitting. +// + OCIO_ADD_TEST(GradingBSplineCurve, basic) { auto curve = OCIO::GradingBSplineCurve::Create(3); @@ -48,9 +52,9 @@ OCIO_ADD_TEST(GradingBSplineCurve, basic) OCIO_CHECK_EQUAL(1.f, curve->getControlPoint(3).m_y); OCIO_CHECK_THROW_WHAT(curve->getControlPoint(42), OCIO::Exception, - "There are '4' control points. '42' is invalid."); + "There are '4' control points. '42' is out of bounds."); OCIO_CHECK_THROW_WHAT(curve->setSlope(42, 0.2f), OCIO::Exception, - "There are '4' control points. '42' is invalid."); + "There are '4' control points. '42' is out of bounds."); std::ostringstream oss; oss << *curve; @@ -67,10 +71,33 @@ OCIO_ADD_TEST(GradingBSplineCurve, validate) curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.7f,0.3f }, { 0.5f,0.7f },{ 1.f,1.f } }); OCIO_CHECK_THROW_WHAT(curve->validate(), OCIO::Exception, - "has a x coordinate '0.5' that is less from previous control " + "has a x coordinate '0.5' that is less than previous control " "point x cooordinate '0.7'."); curve->getControlPoint(1).m_x = 0.3f; OCIO_CHECK_NO_THROW(curve->validate()); } +OCIO_ADD_TEST(GradingBSplineCurve, equals) +{ + // Identical curves. + auto curve1 = OCIO::GradingBSplineCurve::Create({ {0.f,0.f},{0.2f,0.3f},{0.5f,0.7f},{1.f,1.f} }); + auto curve2 = OCIO::GradingBSplineCurve::Create({ {0.f,0.f},{0.2f,0.3f},{0.5f,0.7f},{1.f,1.f} }); + OCIO_CHECK_ASSERT(*curve1 == *curve2); + + // Curve has different spline type. + auto curve3 = OCIO::GradingBSplineCurve::Create({ {0.f,0.f},{0.2f,0.3f},{0.5f,0.7f},{1.f,1.f} }, + OCIO::DIAGONAL_B_SPLINE); + OCIO_CHECK_ASSERT(!(*curve1 == *curve3)); + + // Curve has different slopes. + curve2->setSlope(3, 0.9f); + OCIO_CHECK_NO_THROW(curve2->validate()); + OCIO_CHECK_ASSERT(!(*curve1 == *curve2)); + + // Curve has a different control point value. + auto curve4 = OCIO::GradingBSplineCurve::Create({ {0.f,0.f},{0.2f,0.3f},{0.5f,0.7f},{1.f,1.f} }); + OCIO_CHECK_ASSERT(*curve1 == *curve4); + curve4->getControlPoint(2).m_y = 0.9f; + OCIO_CHECK_ASSERT(!(*curve1 == *curve4)); +} diff --git a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOpData_tests.cpp b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOpData_tests.cpp index 2a373a5723..4063151167 100644 --- a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOpData_tests.cpp +++ b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOpData_tests.cpp @@ -91,23 +91,40 @@ OCIO_ADD_TEST(GradingRGBCurveOpData, accessors) // Check isInverse. - // We have equal ops, inverse one. - gc1.setDirection(OCIO::TRANSFORM_DIR_FORWARD); + // Make two equal non-identity ops, invert one. + OCIO::GradingRGBCurveOpData gc3{ OCIO::GRADING_LIN }; + auto v3 = gc3.getValue()->createEditableCopy(); + auto spline = v3->getCurve(OCIO::RGB_RED); + spline->setNumControlPoints(2); + spline->getControlPoint(0) = OCIO::GradingControlPoint(0.f, 2.f); + spline->getControlPoint(1) = OCIO::GradingControlPoint(0.9f, 2.f); + gc3.setValue(v3); + OCIO_CHECK_ASSERT(!gc3.isIdentity()); // Need a shared pointer for the parameter. - OCIO::ConstGradingRGBCurveOpDataRcPtr gcptr2 = gc1.clone(); - gc1.setDirection(OCIO::TRANSFORM_DIR_INVERSE); - OCIO_CHECK_ASSERT(gc1.isInverse(gcptr2)); - // Change value of one: no longer inverse. - red->getControlPoint(3).m_y += 0.1f; - gc1.setValue(v1); - OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); + OCIO::ConstGradingRGBCurveOpDataRcPtr gcptr3 = gc3.clone(); + gc3.setDirection(OCIO::TRANSFORM_DIR_INVERSE); + // They start as inverses. + OCIO_CHECK_ASSERT(gc3.isInverse(gcptr3)); + + // Change value of one: no longer an inverse. + spline->getControlPoint(1).m_y += 0.25f; + gc3.setValue(v3); + OCIO_CHECK_ASSERT(!gc3.isInverse(gcptr3)); // Restore value. - red->getControlPoint(3).m_y -= 0.1f; - gc1.setValue(v1); - OCIO_CHECK_ASSERT(gc1.isInverse(gcptr2)); - // Change direction: no longer inverse. - gc1.setDirection(OCIO::TRANSFORM_DIR_FORWARD); - OCIO_CHECK_ASSERT(!gc1.isInverse(gcptr2)); + spline->getControlPoint(1).m_y -= 0.25f; + gc3.setValue(v3); + OCIO_CHECK_ASSERT(gc3.isInverse(gcptr3)); + + // Change slope of one: no longer an inverse. + gc3.setSlope(OCIO::RGB_BLUE, 2, 0.9f); + OCIO_CHECK_ASSERT(!gc3.isInverse(gcptr3)); + // Restore value. + gc3.setSlope(OCIO::RGB_BLUE, 2, 0.f); + OCIO_CHECK_ASSERT(gc3.isInverse(gcptr3)); + + // Change direction: no longer an inverse. + gc3.setDirection(OCIO::TRANSFORM_DIR_FORWARD); + OCIO_CHECK_ASSERT(!gc3.isInverse(gcptr3)); } OCIO_ADD_TEST(GradingRGBCurveOpData, validate) @@ -127,7 +144,7 @@ OCIO_ADD_TEST(GradingRGBCurveOpData, validate) { 0.5f,0.7f },{ 1.f,1.f } }); curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, - "has a x coordinate '0.5' that is less from previous control " + "has a x coordinate '0.5' that is less than previous control " "point x cooordinate '0.7'."); // Fix the curve x coordinate. diff --git a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp index d23109b6bb..167463a2eb 100644 --- a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp +++ b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurve_tests.cpp @@ -107,7 +107,6 @@ OCIO_ADD_TEST(GradingRGBCurve, curves) OCIO_CHECK_EQUAL(curves->getCurve(OCIO::RGB_GREEN)->getNumControlPoints(), 4); } -// TODO:: Move this test in the HueCurve version since it is easier to reach the max control points. OCIO_ADD_TEST(GradingRGBCurve, max_ctrl_pnts) { auto curveR = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f },{ 5.f, 10.f },{ 6.f, 10.f }, diff --git a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp index e7889d1c76..2168798eb5 100644 --- a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp +++ b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp @@ -14,271 +14,278 @@ OCIO_ADD_TEST(GradingHueCurveTransform, basic) { // Create transform and validate default values for all styles. - auto gctLin = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); - //OCIO_CHECK_EQUAL(gctLin->getStyle(), OCIO::GRADING_LIN); + auto gctLin = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LIN); + OCIO_CHECK_EQUAL(gctLin->getStyle(), OCIO::GRADING_LIN); OCIO_CHECK_EQUAL(gctLin->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); gctLin->setDirection(OCIO::TRANSFORM_DIR_INVERSE); OCIO_CHECK_EQUAL(gctLin->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); - //OCIO_CHECK_ASSERT(!gctLin->getBypassLinToLog()); - //OCIO_CHECK_ASSERT(!gctLin->isDynamic()); - //auto red = gctLin->getValue()->getCurve(OCIO::RGB_RED); - //OCIO_CHECK_EQUAL(red->getNumControlPoints(), 3); - //OCIO_CHECK_EQUAL(red->getControlPoint(0), OCIO::GradingControlPoint(-7.f, -7.f)); - //OCIO_CHECK_EQUAL(red->getControlPoint(1), OCIO::GradingControlPoint(0.f, 0.f)); - //OCIO_CHECK_EQUAL(red->getControlPoint(2), OCIO::GradingControlPoint(7.f, 7.f)); - //OCIO_CHECK_EQUAL(*gctLin->getValue()->getCurve(OCIO::RGB_GREEN), *red); - //OCIO_CHECK_EQUAL(*gctLin->getValue()->getCurve(OCIO::RGB_BLUE), *red); - //OCIO_CHECK_EQUAL(*gctLin->getValue()->getCurve(OCIO::RGB_MASTER), *red); - //OCIO_CHECK_ASSERT(gctLin.get()); - //OCIO_CHECK_NO_THROW(gctLin->validate()); - - //auto gctLog = OCIO::GradingRGBCurveTransform::Create(OCIO::GRADING_LOG); - //OCIO_CHECK_EQUAL(gctLog->getStyle(), OCIO::GRADING_LOG); - //OCIO_CHECK_EQUAL(gctLog->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); - //OCIO_CHECK_ASSERT(!gctLog->getBypassLinToLog()); - //OCIO_CHECK_ASSERT(!gctLog->isDynamic()); - //red = gctLog->getValue()->getCurve(OCIO::RGB_RED); - //OCIO_CHECK_EQUAL(red->getNumControlPoints(), 3); - //OCIO_CHECK_EQUAL(red->getControlPoint(0), OCIO::GradingControlPoint(0.f, 0.f)); - //OCIO_CHECK_EQUAL(red->getControlPoint(1), OCIO::GradingControlPoint(0.5f, 0.5f)); - //OCIO_CHECK_EQUAL(red->getControlPoint(2), OCIO::GradingControlPoint(1.f, 1.f)); - //OCIO_CHECK_EQUAL(*gctLog->getValue()->getCurve(OCIO::RGB_GREEN), *red); - //OCIO_CHECK_EQUAL(*gctLog->getValue()->getCurve(OCIO::RGB_BLUE), *red); - //OCIO_CHECK_EQUAL(*gctLog->getValue()->getCurve(OCIO::RGB_MASTER), *red); - //OCIO_CHECK_NO_THROW(gctLog->validate()); -// - //auto gctVid = OCIO::GradingRGBCurveTransform::Create(OCIO::GRADING_VIDEO); - //OCIO_CHECK_EQUAL(gctVid->getStyle(), OCIO::GRADING_VIDEO); - //OCIO_CHECK_EQUAL(gctVid->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); - //OCIO_CHECK_ASSERT(!gctVid->getBypassLinToLog()); - //OCIO_CHECK_ASSERT(!gctVid->isDynamic()); - //red = gctVid->getValue()->getCurve(OCIO::RGB_RED); - //OCIO_CHECK_EQUAL(red->getNumControlPoints(), 3); - //OCIO_CHECK_EQUAL(red->getControlPoint(0), OCIO::GradingControlPoint(0.f, 0.f)); - //OCIO_CHECK_EQUAL(red->getControlPoint(1), OCIO::GradingControlPoint(0.5f, 0.5f)); - //OCIO_CHECK_EQUAL(red->getControlPoint(2), OCIO::GradingControlPoint(1.f, 1.f)); - //OCIO_CHECK_EQUAL(*gctVid->getValue()->getCurve(OCIO::RGB_GREEN), *red); - //OCIO_CHECK_EQUAL(*gctVid->getValue()->getCurve(OCIO::RGB_BLUE), *red); - //OCIO_CHECK_EQUAL(*gctVid->getValue()->getCurve(OCIO::RGB_MASTER), *red); - //OCIO_CHECK_NO_THROW(gctVid->validate()); -// - //// Change values. - //auto t = gctVid->createEditableCopy(); - //auto gct = OCIO_DYNAMIC_POINTER_CAST(t); - //gct->setStyle(OCIO::GRADING_LIN); - //OCIO_CHECK_EQUAL(gct->getStyle(), OCIO::GRADING_LIN); - //gct->setDirection(OCIO::TRANSFORM_DIR_INVERSE); - //OCIO_CHECK_EQUAL(gct->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); - //gct->setBypassLinToLog(true); - //OCIO_CHECK_ASSERT(gct->getBypassLinToLog()); - //gct->makeDynamic(); - //OCIO_CHECK_ASSERT(gct->isDynamic()); - //gct->setValue(gctLin->getValue()); - //red = gct->getValue()->getCurve(OCIO::RGB_RED); - //OCIO_CHECK_EQUAL(red->getControlPoint(0), OCIO::GradingControlPoint(-7.f, -7.f)); - //OCIO_CHECK_NO_THROW(gct->validate()); -// - //// Access out of range point. - //OCIO_CHECK_THROW_WHAT(red->getControlPoint(4), OCIO::Exception, - // "There are '3' control points. '4' is invalid."); -// - //// X has to be increasing. - //auto invalidCurve = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.5f, 0.2f }, - // { 0.2f, 0.7f }, { 1.0f, 1.0f } }); - //auto newCurve = OCIO::GradingRGBCurve::Create(red, red, invalidCurve, red); - //OCIO_CHECK_THROW_WHAT(gct->setValue(newCurve), OCIO::Exception, - // "has a x coordinate '0.2' that is less from previous control " - // "point x cooordinate '0.5'."); -// - //// Check slopes. - //gct->setSlope(OCIO::RGB_BLUE, 2, 0.9f); - //OCIO_CHECK_NO_THROW(gct->validate()); - //OCIO_CHECK_EQUAL(gct->getSlope(OCIO::RGB_BLUE, 2), 0.9f); - //OCIO_CHECK_THROW_WHAT(gct->setSlope(OCIO::RGB_BLUE, 4, 2.f), OCIO::Exception, - // "There are '3' control points. '4' is invalid."); - //OCIO_CHECK_ASSERT(gct->slopesAreDefault(OCIO::RGB_GREEN)); - //OCIO_CHECK_ASSERT(!gct->slopesAreDefault(OCIO::RGB_BLUE)); + OCIO_CHECK_ASSERT(!gctLin->getBypassLinToLog()); + OCIO_CHECK_ASSERT(!gctLin->isDynamic()); + auto crv = gctLin->getValue()->getCurve(OCIO::LUM_SAT); + OCIO_CHECK_EQUAL(crv->getNumControlPoints(), 3); + OCIO_CHECK_EQUAL(crv->getControlPoint(0), OCIO::GradingControlPoint(-7.f, 1.f)); + OCIO_CHECK_EQUAL(crv->getControlPoint(1), OCIO::GradingControlPoint(0.f, 1.f)); + OCIO_CHECK_EQUAL(crv->getControlPoint(2), OCIO::GradingControlPoint(7.f, 1.f)); + crv = gctLin->getValue()->getCurve(OCIO::HUE_LUM); + OCIO_CHECK_EQUAL(*gctLin->getValue()->getCurve(OCIO::HUE_SAT), *crv); + OCIO_CHECK_ASSERT(gctLin.get()); + OCIO_CHECK_NO_THROW(gctLin->validate()); + + auto gctLog = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); + OCIO_CHECK_EQUAL(gctLog->getStyle(), OCIO::GRADING_LOG); + OCIO_CHECK_EQUAL(gctLog->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); + OCIO_CHECK_ASSERT(!gctLog->getBypassLinToLog()); + OCIO_CHECK_ASSERT(!gctLog->isDynamic()); + crv = gctLog->getValue()->getCurve(OCIO::LUM_SAT); + OCIO_CHECK_EQUAL(crv->getNumControlPoints(), 3); + OCIO_CHECK_EQUAL(crv->getControlPoint(0), OCIO::GradingControlPoint(0.f, 1.f)); + OCIO_CHECK_EQUAL(crv->getControlPoint(1), OCIO::GradingControlPoint(0.5f, 1.f)); + OCIO_CHECK_EQUAL(crv->getControlPoint(2), OCIO::GradingControlPoint(1.f, 1.f)); + crv = gctLog->getValue()->getCurve(OCIO::HUE_LUM); + OCIO_CHECK_EQUAL(*gctLog->getValue()->getCurve(OCIO::HUE_SAT), *crv); + crv = gctLog->getValue()->getCurve(OCIO::LUM_LUM); + OCIO_CHECK_EQUAL(*gctLog->getValue()->getCurve(OCIO::SAT_SAT), *crv); + OCIO_CHECK_NO_THROW(gctLog->validate()); + + auto gctVid = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_VIDEO); + OCIO_CHECK_EQUAL(gctVid->getStyle(), OCIO::GRADING_VIDEO); + OCIO_CHECK_EQUAL(gctVid->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); + OCIO_CHECK_ASSERT(!gctVid->getBypassLinToLog()); + OCIO_CHECK_ASSERT(!gctVid->isDynamic()); + crv = gctVid->getValue()->getCurve(OCIO::LUM_SAT); + OCIO_CHECK_EQUAL(crv->getNumControlPoints(), 3); + OCIO_CHECK_EQUAL(crv->getControlPoint(0), OCIO::GradingControlPoint(0.f, 1.f)); + OCIO_CHECK_EQUAL(crv->getControlPoint(1), OCIO::GradingControlPoint(0.5f, 1.f)); + OCIO_CHECK_EQUAL(crv->getControlPoint(2), OCIO::GradingControlPoint(1.f, 1.f)); + crv = gctLog->getValue()->getCurve(OCIO::HUE_LUM); + OCIO_CHECK_EQUAL(*gctLog->getValue()->getCurve(OCIO::HUE_SAT), *crv); + crv = gctLog->getValue()->getCurve(OCIO::LUM_LUM); + OCIO_CHECK_EQUAL(*gctLog->getValue()->getCurve(OCIO::SAT_SAT), *crv); + OCIO_CHECK_NO_THROW(gctVid->validate()); + + // Change values. + auto t = gctVid->createEditableCopy(); + auto gct = OCIO_DYNAMIC_POINTER_CAST(t); + gct->setStyle(OCIO::GRADING_LIN); + OCIO_CHECK_EQUAL(gct->getStyle(), OCIO::GRADING_LIN); + gct->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + OCIO_CHECK_EQUAL(gct->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); + gct->setBypassLinToLog(true); + OCIO_CHECK_ASSERT(gct->getBypassLinToLog()); + gct->makeDynamic(); + OCIO_CHECK_ASSERT(gct->isDynamic()); + gct->setValue(gctLin->getValue()); + crv = gct->getValue()->getCurve(OCIO::LUM_LUM); + OCIO_CHECK_EQUAL(crv->getControlPoint(0), OCIO::GradingControlPoint(-7.f, -7.f)); + OCIO_CHECK_NO_THROW(gct->validate()); + + // Access out of range point. + OCIO_CHECK_THROW_WHAT(crv->getControlPoint(4), OCIO::Exception, + "There are '3' control points. '4' is out of bounds."); + + // X-coordinate has to be increasing. + OCIO::GradingHueCurveTransformRcPtr hct = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_VIDEO); + OCIO::GradingHueCurveRcPtr hueCurve = hct->getValue()->createEditableCopy(); + OCIO::GradingBSplineCurveRcPtr lumsat = hueCurve->getCurve(OCIO::LUM_SAT); + OCIO::GradingControlPoint & cp = lumsat->getControlPoint(0); + cp = OCIO::GradingControlPoint(0.7f, 1.f); + OCIO_CHECK_THROW_WHAT(hct->setValue(hueCurve), OCIO::Exception, + "has a x coordinate '0.5' that is less than previous control " + "point x cooordinate '0.7'."); + + // Check slopes. + gct->setSlope(OCIO::LUM_LUM, 2, 0.9f); + OCIO_CHECK_NO_THROW(gct->validate()); + OCIO_CHECK_EQUAL(gct->getSlope(OCIO::LUM_LUM, 2), 0.9f); + OCIO_CHECK_THROW_WHAT(gct->setSlope(OCIO::LUM_LUM, 4, 2.f), OCIO::Exception, + "There are '3' control points. '4' is out of bounds."); + OCIO_CHECK_ASSERT(gct->slopesAreDefault(OCIO::LUM_SAT)); + OCIO_CHECK_ASSERT(!gct->slopesAreDefault(OCIO::LUM_LUM)); } OCIO_ADD_TEST(GradingHueCurveTransform, processor_several_transforms) { - //OCIO::ConfigRcPtr config = OCIO::Config::Create(); - //const float srcPixel[3] = { 0.2f, 0.3f, 0.4f }; -// - //auto c1 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.2f, 0.2f }, - // { 0.5f, 0.7f }, { 1.0f, 1.0f } }); - //auto c2 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.5f }, { 0.3f, 0.7f }, - // { 0.5f, 1.1f }, { 1.0f, 1.5f } }); - //auto c3 = OCIO::GradingBSplineCurve::Create({ { 0.0f, -0.5f }, { 0.2f, -0.4f }, - // { 0.3f, 0.1f }, { 0.5f, 0.4f }, - // { 0.7f, 0.9f }, { 1.0f, 1.1f } }); - //auto c4 = OCIO::GradingBSplineCurve::Create({ { -1.0f, 0.0f }, { 0.2f, 0.2f }, - // { 0.8f, 0.8f }, { 2.0f, 1.0f } }); - //auto c5 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 1.0f, 1.0f } }); - - - //auto rgbCurveA = OCIO::GradingRGBCurve::Create(c1, c2, c3, c5); - - //auto gcta = OCIO::GradingHueCurveTransform::Create(); - //OCIO_CHECK_ASSERT(gcta.get()); - //OCIO_CHECK_NO_THROW(gcta->validate()); - //gcta->setValue(rgbCurveA); -// - //// Will hold results for rgbCurveA. - //float pixel_a[3] = { srcPixel[0], srcPixel[1], srcPixel[2] }; - //// Will hold results for rgbCurveA applied twice. - //float pixel_aa[3]; - //{ - // OCIO::ConstProcessorRcPtr processor = config->getProcessor(gcta); - // OCIO::ConstCPUProcessorRcPtr cpuProcessor = processor->getDefaultCPUProcessor(); - // cpuProcessor->applyRGB(pixel_a); -// - // pixel_aa[0] = pixel_a[0]; - // pixel_aa[1] = pixel_a[1]; - // pixel_aa[2] = pixel_a[2]; - // cpuProcessor->applyRGB(pixel_aa); - //} -// - //auto rgbCurveB = OCIO::GradingRGBCurve::Create(c4, c1, c2, c5); - //auto gctb = OCIO::GradingRGBCurveTransform::Create(OCIO::GRADING_LOG); - //gctb->setValue(rgbCurveB); -// - //// Will hold results for rgbCurveB. - //float pixel_b[3] = { srcPixel[0], srcPixel[1], srcPixel[2] }; - //// Will hold results for rgbCurveB applied twice. - //float pixel_bb[3]; - //// Will hold results for rgbCurveA applied then rgbCurveB applied. - //float pixel_ab[3] = { pixel_a[0], pixel_a[1], pixel_a[2] }; - //{ - // OCIO::ConstProcessorRcPtr processor = config->getProcessor(gctb); - // OCIO::ConstCPUProcessorRcPtr cpuProcessor = processor->getDefaultCPUProcessor(); - // cpuProcessor->applyRGB(pixel_b); -// - // pixel_bb[0] = pixel_b[0]; - // pixel_bb[1] = pixel_b[1]; - // pixel_bb[2] = pixel_b[2]; -// - // cpuProcessor->applyRGB(pixel_bb); - // cpuProcessor->applyRGB(pixel_ab); - //} -// - //// Make second transform dynamic. - //gctb->makeDynamic(); - //const float error = 1e-6f; -// - //// - //// Test with two grading rgb curve transforms where only the second one is dynamic. - //// - //OCIO::GroupTransformRcPtr grp1 = OCIO::GroupTransform::Create(); - //gctb->setValue(rgbCurveA); - //grp1->appendTransform(gcta); // gpta values are rgbCurveA - //grp1->appendTransform(gctb); // gptb values are rgbCurveA -// - //{ - // OCIO::ConstProcessorRcPtr processor = config->getProcessor(grp1); - // OCIO::ConstCPUProcessorRcPtr cpuProcessor = processor->getDefaultCPUProcessor(); -// - // // Second transform is dynamic. Value is still rgbCurveA. - // OCIO::DynamicPropertyRcPtr dp; - // OCIO_CHECK_NO_THROW(dp = cpuProcessor->getDynamicProperty(OCIO::DYNAMIC_PROPERTY_GRADING_RGBCURVE)); - // auto dpVal = OCIO::DynamicPropertyValue::AsGradingRGBCurve(dp); - // OCIO_REQUIRE_ASSERT(dpVal); -// - // float pixel[3] = { srcPixel[0], srcPixel[1], srcPixel[2] }; -// - // // Apply rgbCurveA then rgbCurveA. - // cpuProcessor->applyRGB(pixel); -// - // OCIO_CHECK_CLOSE(pixel[0], pixel_aa[0], error); - // OCIO_CHECK_CLOSE(pixel[1], pixel_aa[1], error); - // OCIO_CHECK_CLOSE(pixel[2], pixel_aa[2], error); -// - // // Change the 2nd values. - // dpVal->setValue(rgbCurveB); - // pixel[0] = srcPixel[0]; - // pixel[1] = srcPixel[1]; - // pixel[2] = srcPixel[2]; -// - // // Apply rgbCurveA then rgbCurveB. - // cpuProcessor->applyRGB(pixel); -// - // OCIO_CHECK_CLOSE(pixel[0], pixel_ab[0], error); - // OCIO_CHECK_CLOSE(pixel[1], pixel_ab[1], error); - // OCIO_CHECK_CLOSE(pixel[2], pixel_ab[2], error); - //} -// - //// - //// Test two grading rgb curve transforms can't be both dynamic. - //// -// - //// Make first dynamic (second already is). - //gcta->makeDynamic(); -// - //OCIO::GroupTransformRcPtr grp2 = OCIO::GroupTransform::Create(); - //grp2->appendTransform(gcta); - //grp2->appendTransform(gctb); -// - //{ - // OCIO::LogGuard log; - // OCIO::SetLoggingLevel(OCIO::LOGGING_LEVEL_WARNING); - // OCIO_CHECK_NO_THROW(config->getProcessor(grp2)); - // OCIO_CHECK_EQUAL(log.output(), "[OpenColorIO Warning]: Grading RGB curve dynamic property " - // "can only be there once.\n"); - //} + OCIO::GradingHueCurveTransformRcPtr gcta = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); + OCIO::GradingHueCurveRcPtr hueCurveA = gcta->getValue()->createEditableCopy(); + OCIO::GradingBSplineCurveRcPtr huefx = hueCurveA->getCurve(OCIO::HUE_FX); + // Shift all hues up by 0.1. + huefx->setNumControlPoints(2); + huefx->getControlPoint(0) = OCIO::GradingControlPoint(0.f, 0.1f); + huefx->getControlPoint(1) = OCIO::GradingControlPoint(0.9f, 0.1f); + gcta->setValue(hueCurveA); + + OCIO_CHECK_NO_THROW(gcta->validate()); + OCIO_CHECK_ASSERT(!hueCurveA->isIdentity()); + + OCIO::ConfigRcPtr config = OCIO::Config::Create(); + const float srcPixel[3] = { 0.2f, 0.3f, 0.4f }; + + // Will hold results for hueCurveA. + float pixel_a[3] = { srcPixel[0], srcPixel[1], srcPixel[2] }; + // Will hold results for hueCurveA applied twice. + float pixel_aa[3]; + { + OCIO::ConstProcessorRcPtr processor = config->getProcessor(gcta); + OCIO::ConstCPUProcessorRcPtr cpuProcessor = processor->getDefaultCPUProcessor(); + cpuProcessor->applyRGB(pixel_a); + + pixel_aa[0] = pixel_a[0]; + pixel_aa[1] = pixel_a[1]; + pixel_aa[2] = pixel_a[2]; + cpuProcessor->applyRGB(pixel_aa); + } + + // NB: This must be GRADING_LOG like above because the test will be changing the curves + // as dynamic parameters but that does not change the base style. + OCIO::GradingHueCurveTransformRcPtr gctb = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); + OCIO::GradingHueCurveRcPtr hueCurveB = gctb->getValue()->createEditableCopy(); + OCIO::GradingBSplineCurveRcPtr lumsat = hueCurveB->getCurve(OCIO::LUM_SAT); + // Increase sat at all luminances by 1.5. + lumsat->setNumControlPoints(2); + lumsat->getControlPoint(0) = OCIO::GradingControlPoint(0.f, 1.5f); + lumsat->getControlPoint(1) = OCIO::GradingControlPoint(1.f, 1.5f); + gctb->setValue(hueCurveB); + + OCIO_CHECK_ASSERT(!hueCurveB->isIdentity()); + + // Will hold results for hueCurveA applied then hueCurveB applied. + float pixel_ab[3] = { pixel_a[0], pixel_a[1], pixel_a[2] }; + { + OCIO::ConstProcessorRcPtr processor = config->getProcessor(gctb); + OCIO::ConstCPUProcessorRcPtr cpuProcessor = processor->getDefaultCPUProcessor(); + cpuProcessor->applyRGB(pixel_ab); + } + + // Make second transform dynamic. + gctb->makeDynamic(); + const float error = 1e-6f; + + // + // Test with two grading hue curve transforms where only the second one is dynamic. + // + + OCIO::GroupTransformRcPtr grp1 = OCIO::GroupTransform::Create(); + gctb->setValue(hueCurveA); + grp1->appendTransform(gcta); // gpta values are hueCurveA + grp1->appendTransform(gctb); // gptb values are hueCurveA + + { + OCIO::ConstProcessorRcPtr processor = config->getProcessor(grp1); + OCIO::ConstCPUProcessorRcPtr cpuProcessor = processor->getDefaultCPUProcessor(); + + // Second transform is dynamic. Value is still hueCurveA. + OCIO::DynamicPropertyRcPtr dp; + OCIO_CHECK_NO_THROW(dp = cpuProcessor->getDynamicProperty(OCIO::DYNAMIC_PROPERTY_GRADING_HUECURVE)); + auto dpVal = OCIO::DynamicPropertyValue::AsGradingHueCurve(dp); + OCIO_REQUIRE_ASSERT(dpVal); + + float pixel[3] = { srcPixel[0], srcPixel[1], srcPixel[2] }; + + // Apply hueCurveA then hueCurveA. + cpuProcessor->applyRGB(pixel); + + OCIO_CHECK_CLOSE(pixel[0], pixel_aa[0], error); + OCIO_CHECK_CLOSE(pixel[1], pixel_aa[1], error); + OCIO_CHECK_CLOSE(pixel[2], pixel_aa[2], error); + + // Change the 2nd values. + dpVal->setValue(hueCurveB); + + pixel[0] = srcPixel[0]; + pixel[1] = srcPixel[1]; + pixel[2] = srcPixel[2]; + + // Apply hueCurveA then hueCurveB. + cpuProcessor->applyRGB(pixel); + + OCIO_CHECK_CLOSE(pixel[0], pixel_ab[0], error); + OCIO_CHECK_CLOSE(pixel[1], pixel_ab[1], error); + OCIO_CHECK_CLOSE(pixel[2], pixel_ab[2], error); + } + + // + // Test two grading hue curve transforms can't be both dynamic. + // + + // Make first dynamic (second already is). + gcta->makeDynamic(); + + OCIO::GroupTransformRcPtr grp2 = OCIO::GroupTransform::Create(); + grp2->appendTransform(gcta); + grp2->appendTransform(gctb); + + { + OCIO::LogGuard log; + OCIO::SetLoggingLevel(OCIO::LOGGING_LEVEL_WARNING); + OCIO_CHECK_NO_THROW(config->getProcessor(grp2)); + OCIO_CHECK_EQUAL(log.output(), "[OpenColorIO Warning]: Grading hue curve dynamic property " + "can only be there once.\n"); + } } OCIO_ADD_TEST(GradingHueCurveTransform, serialization) { // Test the serialization of the transform. - //auto c1 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.2f, 0.2f }, - // { 0.5f, 0.7f }, { 1.0f, 1.0f } }); - //auto c2 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.5f }, { 0.3f, 0.7f }, - // { 0.5f, 1.1f }, { 1.0f, 1.5f } }); - //auto c3 = OCIO::GradingBSplineCurve::Create({ { 0.0f, -0.5f }, { 0.2f, -0.4f }, - // { 0.3f, 0.1f }, { 0.5f, 0.4f }, - // { 0.7f, 0.9f }, { 1.0f, 1.1f } }); - //auto c4 = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 1.0f, 1.0f } }); -// -// - //auto data = OCIO::GradingRGBCurve::Create(c1, c2, c3, c4); - - //auto curve = OCIO::GradingHueCurveTransform::Create(); - //OCIO_CHECK_ASSERT(curve.get()); - //OCIO_CHECK_NO_THROW(curve->validate()); - - //curve->setValue(data); - - //static constexpr char CURVE_STR[] - // = "]>, " - // "green=]>, " - // "blue=]>, " - // "master=]>>>"; -// - //{ - // std::ostringstream oss; - // oss << *curve; -// - // OCIO_CHECK_EQUAL(oss.str(), CURVE_STR); - //} -// - //OCIO::GroupTransformRcPtr grp = OCIO::GroupTransform::Create(); - //grp->appendTransform(OCIO::DynamicPtrCast(curve)); -// - //{ - // std::ostringstream oss; - // oss << *grp; -// - // std::string GROUP_STR("validate()); + + curve->setValue(data); + + static constexpr char CURVE_STR[] + = "" + "]>, hue_sat=" + "]>, hue_lum=]>, lum_sat=" + "]>, sat_sat=" + "]>, lum_lum=]>, sat_lum=" + "]>, hue_fx=" + "]>>>"; + + { + std::ostringstream oss; + oss << *curve; + OCIO_CHECK_EQUAL(oss.str(), CURVE_STR); + } + + OCIO::GroupTransformRcPtr grp = OCIO::GroupTransform::Create(); + grp->appendTransform(OCIO::DynamicPtrCast(curve)); + + { + std::ostringstream oss; + oss << *grp; + + std::string GROUP_STR("createEditableCopy(); - OCIO_CHECK_ASSERT(transformBypass.get()); +// OCIO_CHECK_ASSERT(transform.get()); +// +// OCIO::TransformRcPtr transformBypass = transform->createEditableCopy(); +// OCIO_CHECK_ASSERT(transformBypass.get()); OCIO_CHECK_NO_THROW(transform->validate()); - //OCIO::ConstConfigRcPtr config = OCIO::Config::CreateRaw(); -// - //OCIO::ConstProcessorRcPtr proc = config->getProcessor(transform); - //OCIO::ConstGPUProcessorRcPtr gpu = proc->getOptimizedGPUProcessor(OCIO::OPTIMIZATION_NONE); -// - //OCIO::GpuShaderDescRcPtr shaderDesc = OCIO::GpuShaderDesc::CreateShaderDesc(); -// - //OCIO_CHECK_NO_THROW(gpu->extractGpuShaderInfo(shaderDesc)); -// - //static const std::string gpuStr("\n" - // "// Declaration of the OCIO shader function\n" - // "\n" - // "vec4 OCIOMain(vec4 inPixel)\n" - // "{\n" - // " vec4 outColor = inPixel;\n" - // "\n" - // " return outColor;\n" - // "}\n"); -// - //OCIO_CHECK_EQUAL(gpuStr, std::string(shaderDesc->getShaderText())); + OCIO::ConstConfigRcPtr config = OCIO::Config::CreateRaw(); + + OCIO::ConstProcessorRcPtr proc = config->getProcessor(transform); + OCIO::ConstGPUProcessorRcPtr gpu = proc->getOptimizedGPUProcessor(OCIO::OPTIMIZATION_NONE); + + OCIO::GpuShaderDescRcPtr shaderDesc = OCIO::GpuShaderDesc::CreateShaderDesc(); + + OCIO_CHECK_NO_THROW(gpu->extractGpuShaderInfo(shaderDesc)); + + static const std::string gpuStr("\n" + "// Declaration of the OCIO shader function\n" + "\n" + "vec4 OCIOMain(vec4 inPixel)\n" + "{\n" + " vec4 outColor = inPixel;\n" + "\n" + " return outColor;\n" + "}\n"); + + OCIO_CHECK_EQUAL(gpuStr, std::string(shaderDesc->getShaderText())); } diff --git a/tests/cpu/transforms/GradingRGBCurveTransform_tests.cpp b/tests/cpu/transforms/GradingRGBCurveTransform_tests.cpp index 3e1f1ec742..5ee6c61e8b 100644 --- a/tests/cpu/transforms/GradingRGBCurveTransform_tests.cpp +++ b/tests/cpu/transforms/GradingRGBCurveTransform_tests.cpp @@ -77,14 +77,14 @@ OCIO_ADD_TEST(GradingRGBCurveTransform, basic) // Access out of range point. OCIO_CHECK_THROW_WHAT(red->getControlPoint(4), OCIO::Exception, - "There are '3' control points. '4' is invalid."); + "There are '3' control points. '4' is out of bounds."); // X has to be increasing. auto invalidCurve = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.5f, 0.2f }, { 0.2f, 0.7f }, { 1.0f, 1.0f } }); auto newCurve = OCIO::GradingRGBCurve::Create(red, red, invalidCurve, red); OCIO_CHECK_THROW_WHAT(gct->setValue(newCurve), OCIO::Exception, - "has a x coordinate '0.2' that is less from previous control " + "has a x coordinate '0.2' that is less than previous control " "point x cooordinate '0.5'."); // Check slopes. @@ -92,7 +92,7 @@ OCIO_ADD_TEST(GradingRGBCurveTransform, basic) OCIO_CHECK_NO_THROW(gct->validate()); OCIO_CHECK_EQUAL(gct->getSlope(OCIO::RGB_BLUE, 2), 0.9f); OCIO_CHECK_THROW_WHAT(gct->setSlope(OCIO::RGB_BLUE, 4, 2.f), OCIO::Exception, - "There are '3' control points. '4' is invalid."); + "There are '3' control points. '4' is out of bounds."); OCIO_CHECK_ASSERT(gct->slopesAreDefault(OCIO::RGB_GREEN)); OCIO_CHECK_ASSERT(!gct->slopesAreDefault(OCIO::RGB_BLUE)); } diff --git a/tests/data/files/grading_hue_curve.ctf b/tests/data/files/grading_hue_curve.ctf new file mode 100644 index 0000000000..ee73caf69b --- /dev/null +++ b/tests/data/files/grading_hue_curve.ctf @@ -0,0 +1,47 @@ + + + + + + +0.05, 0.15, 0.2, 0.3, 0.35, 0.4, 0.45, 0.45, 0.6, 0.7, 0.8, 0.85 + + + + +-0.1, 1.2, 0.2, 0.7, 0.4, 1.5, 0.5, 0.5, 0.6, 1.4, 0.8, 0.7 + + + + +0.1, 1.5, 0.2, 0.7, 0.4, 1.4, 0.5, 0.8, 0.8, 0.5 + + + + +0.05, 1.5, 0.5, 0.9, 1.1, 1.4 + + + + +0., 0.1, 0.5, 0.45, 1., 1.1 + + + + +-0.02, -0.04, 0.2, 0.1, 0.8, 0.95, 1.1, 1.2 + + + + +0., 1.2, 0.6, 0.8, 0.9, 1.1 + + + + +0.2, 0.05, .4, -0.09, .6, -0.2, .8, 0.05, 0.99, -0.02 + + + + + diff --git a/tests/gpu/FixedFunctionOp_test.cpp b/tests/gpu/FixedFunctionOp_test.cpp index c053b4dbaf..b6a30c11ae 100644 --- a/tests/gpu/FixedFunctionOp_test.cpp +++ b/tests/gpu/FixedFunctionOp_test.cpp @@ -1250,12 +1250,19 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_fwd) test.setProcessor(func); - test.setErrorThreshold(1e-6f); + OCIOGPUTest::CustomValues values; + values.m_inputValues = { + -0.075290f, 0.078996f, -0.108397f, 0.f, + 0.3f, 0.4f, 0.5f, 0.f, + 0.05f, 0.03f, 0.04f, 0.f, + 0.01f, 0.01f, -0.05f, 1.f, + 0.05f, -0.005f, -0.05f, 1.f, + -0.048f, 0.01f, 0.05f, 0.f, + 0.3f, -0.4f, 0.5f, 0.f, + -0.055f, 0.01f, 0.05f, 0.f }; + test.setCustomValues(values); -#ifdef __APPLE__ - test.setTestNaN(false); - test.setTestInfinity(false); -#endif + test.setErrorThreshold(1e-6f); } OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_inv) @@ -1266,20 +1273,19 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LIN_inv) test.setProcessor(func); - test.setErrorThreshold(1e-6f); - OCIOGPUTest::CustomValues values; values.m_inputValues = { - 4.70554752e-01f, 9.12594033f, 3.26650218e-02f, 0.f, - 0.75f, 0.22196741f, 0.38596f, 1.f, - 0.08333333f, 0.12976444f, 0.034974f, 0.f, - 0.96296296f, 9.7034f, -0.1862f, 1.f }; + 0.470554752f, 9.12594033f, 0.0326650218f, 0.f, // hsy alpha == 1 + 0.75f, 0.22196741f, 0.38596f, 0.f, + 0.08333333f, 0.12976444f, 0.034974f, 0.f, + 0.333333333333f, 0.606036032f, 0.0056680f, 1.f, // hsy mid alpha + 0.241666666667f, 0.8372990325f, 0.0034440f, 1.f, + 0.734693877551f, 0.752099600f, 0.0005572f, 0.f, // hsy alpha == 0 + 0.96296296f, 9.7034f, -0.1862f, 0.f, + 0.730158730159f, 0.811517000f, -0.0009310f, 0.f }; test.setCustomValues(values); -#ifdef __APPLE__ - test.setTestNaN(false); - test.setTestInfinity(false); -#endif + test.setErrorThreshold(1e-6f); } OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_fwd) @@ -1290,12 +1296,19 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_fwd) test.setProcessor(func); - test.setErrorThreshold(1e-6f); + OCIOGPUTest::CustomValues values; + values.m_inputValues = { + -0.075290f, 0.078996f, -0.108397f, 0.f, + 0.3f, 0.4f, 0.5f, 0.f, + 0.05f, 0.03f, 0.04f, 0.f, + 0.01f, 0.01f, -0.05f, 1.f, + 0.05f, -0.005f, -0.05f, 1.f, + -0.048f, 0.01f, 0.05f, 0.f, + 0.3f, -0.4f, 0.5f, 0.f, + -0.055f, 0.01f, 0.05f, 0.f }; + test.setCustomValues(values); -#ifdef __APPLE__ - test.setTestNaN(false); - test.setTestInfinity(false); -#endif + test.setErrorThreshold(1e-6f); } OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_inv) @@ -1306,12 +1319,19 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_LOG_inv) test.setProcessor(func); - test.setErrorThreshold(1e-6f); + OCIOGPUTest::CustomValues values; + values.m_inputValues = { + 0.470554752f, 9.12594033f, 0.0326650218f, 0.f, // hsy alpha == 1 + 0.75f, 0.22196741f, 0.38596f, 0.f, + 0.08333333f, 0.12976444f, 0.034974f, 0.f, + 0.333333333333f, 0.606036032f, 0.0056680f, 1.f, // hsy mid alpha + 0.241666666667f, 0.8372990325f, 0.0034440f, 1.f, + 0.734693877551f, 0.752099600f, 0.0005572f, 0.f, // hsy alpha == 0 + 0.96296296f, 9.7034f, -0.1862f, 0.f, + 0.730158730159f, 0.811517000f, -0.0009310f, 0.f }; + test.setCustomValues(values); -#ifdef __APPLE__ - test.setTestNaN(false); - test.setTestInfinity(false); -#endif + test.setErrorThreshold(1e-6f); } OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_fwd) @@ -1322,12 +1342,19 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_fwd) test.setProcessor(func); - test.setErrorThreshold(1e-6f); + OCIOGPUTest::CustomValues values; + values.m_inputValues = { + -0.075290f, 0.078996f, -0.108397f, 0.f, + 0.3f, 0.4f, 0.5f, 0.f, + 0.05f, 0.03f, 0.04f, 0.f, + 0.01f, 0.01f, -0.05f, 1.f, + 0.05f, -0.005f, -0.05f, 1.f, + -0.048f, 0.01f, 0.05f, 0.f, + 0.3f, -0.4f, 0.5f, 0.f, + -0.055f, 0.01f, 0.05f, 0.f }; + test.setCustomValues(values); -#ifdef __APPLE__ - test.setTestNaN(false); - test.setTestInfinity(false); -#endif + test.setErrorThreshold(1e-6f); } OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_inv) @@ -1338,12 +1365,19 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_RGB_TO_HSY_VID_inv) test.setProcessor(func); - test.setErrorThreshold(1e-6f); + OCIOGPUTest::CustomValues values; + values.m_inputValues = { + 0.470554752f, 9.12594033f, 0.0326650218f, 0.f, // hsy alpha == 1 + 0.75f, 0.22196741f, 0.38596f, 0.f, + 0.08333333f, 0.12976444f, 0.034974f, 0.f, + 0.333333333333f, 0.606036032f, 0.0056680f, 1.f, // hsy mid alpha + 0.241666666667f, 0.8372990325f, 0.0034440f, 1.f, + 0.734693877551f, 0.752099600f, 0.0005572f, 0.f, // hsy alpha == 0 + 0.96296296f, 9.7034f, -0.1862f, 0.f, + 0.730158730159f, 0.811517000f, -0.0009310f, 0.f }; + test.setCustomValues(values); -#ifdef __APPLE__ - test.setTestNaN(false); - test.setTestInfinity(false); -#endif + test.setErrorThreshold(1e-6f); } OCIO_ADD_GPU_TEST(FixedFunction, style_XYZ_TO_xyY_fwd) diff --git a/tests/gpu/GradingHueCurveOp_test.cpp b/tests/gpu/GradingHueCurveOp_test.cpp index e4576ba26c..28793d1b81 100644 --- a/tests/gpu/GradingHueCurveOp_test.cpp +++ b/tests/gpu/GradingHueCurveOp_test.cpp @@ -7,35 +7,71 @@ namespace OCIO = OCIO_NAMESPACE; -void GradingHueCurveLog(OCIOGPUTest & /*test*/, OCIO::TransformDirection /*dir*/, bool /*dynamic*/) +namespace { - // TODO: Put back the GradingHueCurve test once the CPU implementation is available.' - // GPU test compares the result of the GPU processor to the CPU processor. - - //auto c = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.785f, 0.231f }, - // { 0.809f, 0.631f },{ 0.948f, 0.704f }, - // { 1.0f, 1.0f } }); - //auto curve = OCIO::GradingHueCurve::Create(c); - //auto hc = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); - //if(!hc.get()) - //{ - // throw OCIO::Exception("Cannot create GradingHueCurveTransform."); - //} - //hc->setValue(curve); - //hc->setDirection(dir); - //if (dynamic) - //{ - // hc->makeDynamic(); - //} - - //test.setProcessor(hc); - - //test.setErrorThreshold(2e-5f); - //test.setExpectedMinimalValue(1.0f); - //test.setRelativeComparison(true); - //test.setTestWideRange(true); - //test.setTestInfinity(true); - //test.setTestNaN(true); + +void GenerateIdentityLut3D(OCIOGPUTest::CustomValues & values, int edgeLen, float min, float max) +{ + const int numChannels = 4; + int num_samples = edgeLen * edgeLen * edgeLen; + std::vector img(num_samples * numChannels, 0.f); + + const float scale = max - min; + float c = 1.0f / ((float)edgeLen - 1.0f); + for (int i = 0; i < edgeLen*edgeLen*edgeLen; i++) + { + img[numChannels*i + 0] = scale * (float)(i%edgeLen) * c + min; + img[numChannels*i + 1] = scale * (float)((i / edgeLen) % edgeLen) * c + min; + img[numChannels*i + 2] = scale * (float)((i / edgeLen / edgeLen) % edgeLen) * c + min; + } + values.m_inputValues = img; +} + +} // anon. + +void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +{ + // All curves are non-identities. + auto hh = OCIO::GradingBSplineCurve::Create({ {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, + {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create({ {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, + {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create({ {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, + {0.5f, 0.8f}, {0.8f, 0.5f} }, OCIO::HUE_LUM); + auto ls = OCIO::GradingBSplineCurve::Create({ {0.05f, 1.5f}, {0.5f, 0.9f}, {1.1f, 1.4f}, + }, OCIO::LUM_SAT); + auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, + OCIO::SAT_SAT); + auto ll = OCIO::GradingBSplineCurve::Create({ {-0.02f, -0.04f}, {0.2f, 0.1f}, {0.8f, 0.95f}, {1.1f, 1.2f} }, + OCIO::LUM_LUM); + auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, + OCIO::SAT_LUM); + auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, + { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); + + auto curve = OCIO::GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); + auto hc = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); + if(!hc.get()) + { + throw OCIO::Exception("Cannot create GradingHueCurveTransform."); + } + hc->setValue(curve); + hc->setDirection(dir); + if (dynamic) + { + hc->makeDynamic(); + } + + test.setProcessor(hc); + + // Set up a grid of RGBA custom values. + const int lut_size = 17; + OCIOGPUTest::CustomValues values; + GenerateIdentityLut3D(values, lut_size, -0.1f, 1.5f); + test.setCustomValues(values); + + test.setErrorThreshold(2e-5f); + test.setExpectedMinimalValue(1.0f); } OCIO_ADD_GPU_TEST(GradingHueCurve, style_log_fwd) @@ -58,35 +94,51 @@ OCIO_ADD_GPU_TEST(GradingHueCurve, style_log_rev_dynamic) GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, true); } -void HueCurveLin(OCIOGPUTest & /*test*/, OCIO::TransformDirection /*dir*/, bool /*dynamic*/) +void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) { - // TODO: Put back the GradingHueCurve test once the CPU implementation is available.' - // GPU test compares the result of the GPU processor to the CPU processor. - - //auto c = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.785f, 0.231f }, - // { 0.809f, 0.631f },{ 0.948f, 0.704f }, - // { 1.0f, 1.0f } }); - //auto curve = OCIO::GradingHueCurve::Create(c); - //auto hc = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LIN); - //if(!hc.get()) - //{ - // throw OCIO::Exception("Cannot create GradingHueCurveTransform."); - //} - //hc->setValue(curve); - //hc->setDirection(dir); - //if (dynamic) - //{ - // hc->makeDynamic(); - //} - - //test.setProcessor(hc); - - //test.setErrorThreshold(2e-5f); - //test.setExpectedMinimalValue(1.0f); - //test.setRelativeComparison(true); - //test.setTestWideRange(true); - //test.setTestInfinity(true); - //test.setTestNaN(true); + // All curves are non-identities. + auto hh = OCIO::GradingBSplineCurve::Create({ {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, + {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create({ {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, + {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create({ {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, + {0.5f, 0.8f}, {0.8f, 0.5f} }, OCIO::HUE_LUM); + auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, + OCIO::SAT_SAT); + auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, + OCIO::SAT_LUM); + auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, + { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); + // Adjust these two, relative to previous test, to work in f-stops. + auto ls = OCIO::GradingBSplineCurve::Create({ {-6.f, 0.9f}, {-3.f, 0.95f}, {0.f, 1.2f}, + {2.f, 1.f}, {4.f, 0.6f}, {6.f, 0.55f} }, OCIO::LUM_SAT); + auto ll = OCIO::GradingBSplineCurve::Create({ {-8.f, -7.f}, {-2.f, -3.f}, {2.f, 3.5f}, {8.f, 7.f} }, + OCIO::LUM_LUM); + + auto curve = OCIO::GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); + auto hc = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LIN); + if(!hc.get()) + { + throw OCIO::Exception("Cannot create GradingHueCurveTransform."); + } + hc->setValue(curve); + hc->setDirection(dir); + if (dynamic) + { + hc->makeDynamic(); + } + + test.setProcessor(hc); + + // Set up a grid of RGBA custom values. + const int lut_size = 17; + OCIOGPUTest::CustomValues values; + GenerateIdentityLut3D(values, lut_size, -0.1f, 1.5f); + test.setCustomValues(values); + + test.setErrorThreshold(2e-4f); + test.setExpectedMinimalValue(1.0f); + test.setRelativeComparison(true); } OCIO_ADD_GPU_TEST(GradingHueCurve, style_lin_fwd) @@ -108,68 +160,3 @@ OCIO_ADD_GPU_TEST(GradingHueCurve, style_lin_rev_dynamic) { HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, true); } - -void HueSCurve(OCIOGPUTest & /*test*/, OCIO::TransformDirection /*dir*/, bool /*dynamic*/) -{ - // TODO: Implement this GradingHueCurve test once the CPU implementation is available.' - // GPU test compares the result of the GPU processor to the CPU processor. - - // Create an S-curve with 0 slope at each end. - //auto curve = OCIO::GradingBSplineCurve::Create({ - // {-5.26017743f, -4.f}, - // {-3.75502745f, -3.57868829f}, - // {-2.24987747f, -1.82131329f}, - // {-0.74472749f, 0.68124124f}, - // { 1.06145248f, 2.87457742f}, - // { 2.86763245f, 3.83406206f}, - // { 4.67381243f, 4.f} - // }); - //float slopes[] = { 0.f, 0.55982688f, 1.77532247f, 1.55f, 0.8787017f, 0.18374463f, 0.f }; - //for (size_t i = 0; i < 7; ++i) - //{ - // curve->setSlope( i, slopes[i] ); - //} - - //OCIO::ConstGradingBSplineCurveRcPtr m = curve; - //// Adjust scaling to ensure the test vector for the inverse hits the flat areas. - //auto scaling = OCIO::GradingBSplineCurve::Create({ { -5.f, 0.f }, { 5.f, 1.f } }); - //OCIO::ConstGradingBSplineCurveRcPtr z = scaling; - //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(m, m, m, z); - - //auto gc = OCIO::GradingHueCurveTransform::Create(); - //gc->setValue(curves); - //gc->setDirection(dir); - //if (dynamic) - //{ - // gc->makeDynamic(); - //} - - //test.setProcessor(gc); - - //test.setErrorThreshold(1.5e-4f); - //test.setExpectedMinimalValue(1.0f); - //test.setRelativeComparison(true); - //test.setTestWideRange(true); - //test.setTestInfinity(false); - //test.setTestNaN(true); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_fwd) -{ - HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, false); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_fwd_dynamic) -{ - HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, true); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_rev) -{ - HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, false); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_rev_dynamic) -{ - HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, true); -} diff --git a/tests/osl/GradingHueCurveOp_test.cpp b/tests/osl/GradingHueCurveOp_test.cpp deleted file mode 100644 index 803dc7a904..0000000000 --- a/tests/osl/GradingHueCurveOp_test.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright Contributors to the OpenColorIO Project. - -#include - -#include "GPUUnitTest.h" - -namespace OCIO = OCIO_NAMESPACE; - -void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) -{ - //auto r = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f }, { 0.785f, 0.231f }, - // { 0.809f, 0.631f },{ 0.948f, 0.704f }, - // { 1.0f, 1.0f } }); - //auto g = OCIO::GradingBSplineCurve::Create({ { 0.1f, 0.15f }, { 0.55f, 0.35f },{ 0.9f, 1.1f } }); - //auto b = OCIO::GradingBSplineCurve::Create({ { -6.f, -8.f }, { -2.f, -5.f }, - // { 2.f, 4.f }, { 5.f, 6.f } }); - //auto m = OCIO::GradingBSplineCurve::Create({ { -0.1f, 0.1f },{ 1.1f, 1.3f } }); - //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(r, g, b, m); - - //auto gc = OCIO::GradingHueCurveTransform::Create(); - //gc->setValue(curves); - //if(!gc.get()) - //{ - // throw OCIO::Exception("Cannot create GradingHueCurveTransform."); - //} - - //gc->setDirection(dir); - //if (dynamic) - //{ - // gc->makeDynamic(); - //} - - //test.setProcessor(gc); -// - //test.setErrorThreshold(2e-5f); - //test.setExpectedMinimalValue(1.0f); - //test.setRelativeComparison(true); - //test.setTestWideRange(true); - //test.setTestInfinity(true); - //test.setTestNaN(true); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_log_fwd) -{ - GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_FORWARD, false); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_log_fwd_dynamic) -{ - GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_FORWARD, true); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_log_rev) -{ - GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, false); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_log_rev_dynamic) -{ - GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, true); -} - -void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) -{ - //auto r = OCIO::GradingBSplineCurve::Create({ { 0.0f, 0.0f },{ 0.785f, 0.231f }, - // { 0.809f, 0.631f },{ 0.948f, 0.704f }, - // { 1.0f, 1.0f } }); - //auto g = OCIO::GradingBSplineCurve::Create({ { 0.1f, 0.15f },{ 0.55f, 0.35f },{ 0.9f, 0.8f } }); - //auto b = OCIO::GradingBSplineCurve::Create({ { -6.f, -4.f },{ -2.f, -1.f }, - // { 2.f, 2.f },{ 5.f, 4.f } }); - //auto m = OCIO::GradingBSplineCurve::Create({ { -0.1f, 0.1f },{ 1.1f, 0.9f } }); - //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(r, g, b, m); - - //auto gc = OCIO::GradingHueCurveTransform::Create(); - //gc->setValue(curves); - //gc->setDirection(dir); - //if (dynamic) - //{ - // gc->makeDynamic(); - //} - - //test.setProcessor(gc); -// - //test.setErrorThreshold(1.5e-4f); - //test.setExpectedMinimalValue(1.0f); - //test.setRelativeComparison(true); - //test.setTestWideRange(true); - //test.setTestInfinity(false); - //test.setTestNaN(true); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_lin_fwd) -{ - HueCurveLin(test, OCIO::TRANSFORM_DIR_FORWARD, false); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_lin_fwd_dynamic) -{ - HueCurveLin(test, OCIO::TRANSFORM_DIR_FORWARD, true); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_lin_rev) -{ - HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, false); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, style_lin_rev_dynamic) -{ - HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, true); -} - -void HueSCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) -{ - // Create an S-curve with 0 slope at each end. - //auto curve = OCIO::GradingBSplineCurve::Create({ - // {-5.26017743f, -4.f}, - // {-3.75502745f, -3.57868829f}, - // {-2.24987747f, -1.82131329f}, - // {-0.74472749f, 0.68124124f}, - // { 1.06145248f, 2.87457742f}, - // { 2.86763245f, 3.83406206f}, - // { 4.67381243f, 4.f} - // }); - //float slopes[] = { 0.f, 0.55982688f, 1.77532247f, 1.55f, 0.8787017f, 0.18374463f, 0.f }; - //for (size_t i = 0; i < 7; ++i) - //{ - // curve->setSlope( i, slopes[i] ); - //} -// - //OCIO::ConstGradingBSplineCurveRcPtr m = curve; - //// Adjust scaling to ensure the test vector for the inverse hits the flat areas. - //auto scaling = OCIO::GradingBSplineCurve::Create({ { -5.f, 0.f }, { 5.f, 1.f } }); - //OCIO::ConstGradingBSplineCurveRcPtr z = scaling; - //OCIO::ConstGradingRGBCurveRcPtr curves = OCIO::GradingRGBCurve::Create(m, m, m, z); - - //auto gc = OCIO::GradingHueCurveTransform::Create(); - //if(!gc.get()) - //{ - // throw OCIO::Exception("Cannot create GradingHueCurveTransform."); - //} - //gc->setValue(curves); - //gc->setDirection(dir); - //if (dynamic) - //{ - // gc->makeDynamic(); - //} - - //test.setProcessor(gc); -// - //test.setErrorThreshold(1.5e-4f); - //test.setExpectedMinimalValue(1.0f); - //test.setRelativeComparison(true); - //test.setTestWideRange(true); - //test.setTestInfinity(false); - //test.setTestNaN(true); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_fwd) -{ - HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, false); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_fwd_dynamic) -{ - HueSCurve(test, OCIO::TRANSFORM_DIR_FORWARD, true); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_rev) -{ - HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, false); -} - -OCIO_ADD_GPU_TEST(GradingHueCurveTransform, scurve_rev_dynamic) -{ - HueSCurve(test, OCIO::TRANSFORM_DIR_INVERSE, true); -} diff --git a/tests/python/GradingDataTest.py b/tests/python/GradingDataTest.py index b015e53702..9f928d64d4 100644 --- a/tests/python/GradingDataTest.py +++ b/tests/python/GradingDataTest.py @@ -6,7 +6,9 @@ import sys import PyOpenColorIO as OCIO -from UnitTestUtils import assertEqualRGBM, assertEqualPrimary, assertEqualRGBMSW, assertEqualTone, assertEqualBSpline, assertEqualRGBCurve +from UnitTestUtils import assertEqualRGBM, assertEqualPrimary, assertEqualRGBMSW, \ + assertEqualTone, assertEqualBSpline, assertEqualRGBCurve, assertEqualHueCurve, \ + assertAlmostEqualVector class GradingDataTest(unittest.TestCase): @@ -148,6 +150,8 @@ def test_bspline(self): cpts[3] = OCIO.GradingControlPoint(0.6, 0.7) cpts[4] = OCIO.GradingControlPoint(1, 1) self.assertIsNone(bs.validate()) + self.assertEqual(bs.getSplineType(), OCIO.B_SPLINE) + self.assertEqual(bs.slopesAreDefault(), True) # Move point 4 before point 3 on the x axis so that the control points are not anymore # monotonic. Then, it must throw an exception. @@ -158,13 +162,32 @@ def test_bspline(self): # Restore valid data. cpts[4] = OCIO.GradingControlPoint(1, 1) + # Test constructing via splineType or curveType. + spl1 = OCIO.GradingBSplineCurve(5, OCIO.DIAGONAL_B_SPLINE) + self.assertEqual(spl1.getSplineType(), OCIO.DIAGONAL_B_SPLINE) + spl2 = OCIO.GradingBSplineCurve(5, OCIO.HUE_HUE) + self.assertEqual(spl2.getSplineType(), OCIO.HUE_HUE_B_SPLINE) + spl2.setSplineType(OCIO.PERIODIC_1_B_SPLINE) + self.assertEqual(spl2.getSplineType(), OCIO.PERIODIC_1_B_SPLINE) + # Create a similar bspline curve with alternate constructor. bs2 = OCIO.GradingBSplineCurve([0, 0, 0.1, 0.5, 0.4, 0.6, 0.6, 0.7, 1, 1]) cpts2 = bs2.getControlPoints() + assertEqualBSpline(self, bs, bs2) + self.assertEqual(bs2.getSplineType(), OCIO.B_SPLINE) + self.assertEqual(bs, bs2) + bs2 = OCIO.GradingBSplineCurve([0, 0, 0.1, 0.5, 0.4, 0.6, 0.6, 0.7, 1, 1], + OCIO.LUM_SAT) + cpts2 = bs2.getControlPoints() + # NB: This comparison function is only on the control points. assertEqualBSpline(self, bs, bs2) + self.assertEqual(bs2.getSplineType(), OCIO.HORIZONTAL1_B_SPLINE) + self.assertEqual(bs2.slopesAreDefault(), True) + # The class op== detects that the curve type is different. + self.assertNotEqual(bs, bs2) - # Curve with less control points. + # Curve with fewer control points. bs3 = OCIO.GradingBSplineCurve(4) cpts3 = bs3.getControlPoints() cpts3[1] = OCIO.GradingControlPoint(0.1, 0.5) @@ -186,7 +209,7 @@ def test_bspline(self): with self.assertRaises(AssertionError): assertEqualBSpline(self, bs, bs4) - # Curve with the same number of control points but point at index 2 differ. + # Curve with the same number of control points but point at index 2 differs. bs5 = OCIO.GradingBSplineCurve(5) cpts5 = bs5.getControlPoints() cpts5[1] = OCIO.GradingControlPoint(0.1, 0.5) @@ -215,11 +238,24 @@ def test_bspline(self): bs1.getControlPoints()[2] = OCIO.GradingControlPoint(0.1, 0.4) self.assertNotEqual(cpts1, cpts2) + # Slopes. + s = [0.9, 0.8, 0.7, 0.6, 0.5] + bs1.setSlopes(s) + s1 = bs1.getSlopes() + assertAlmostEqualVector(self, s, s1, delta=1e-6) + with self.assertRaises(OCIO.Exception): + # Length of slopes must match control points. + bs1.setSlopes(s[2:]) + # The comparison now fails since the slopes are not equal. + self.assertNotEqual(bs1, bs2) + def test_rgbcurve(self): """ Test the GradingRGBCurve, creation, default value, modification. """ + # Check default values. + rgbLin = OCIO.GradingRGBCurve(OCIO.GRADING_LIN) defLin = OCIO.GradingBSplineCurve(3) @@ -246,15 +282,82 @@ def test_rgbcurve(self): with self.assertRaises(AssertionError): assertEqualBSpline(self, rgbLog.master, defLin) - rgbVideo = OCIO.GradingRGBCurve(OCIO.GRADING_LOG) + rgbVideo = OCIO.GradingRGBCurve(OCIO.GRADING_VIDEO) assertEqualRGBCurve(self, rgbLog, rgbVideo) # Check comparison operators rgbc1 = OCIO.GradingRGBCurve(OCIO.GRADING_LIN) rgbc2 = OCIO.GradingRGBCurve(OCIO.GRADING_LIN) self.assertEqual(rgbc1, rgbc2) - rgbc1.red.getControlPoints()[1] = OCIO.GradingControlPoint(0.4, 0.4) + self.assertEqual(rgbc1.isIdentity(), True) + rgbc1.red.getControlPoints()[1] = OCIO.GradingControlPoint(0.4, 0.5) self.assertNotEqual(rgbc1, rgbc2) + self.assertEqual(rgbc1.isIdentity(), False) + rgbc1.validate() + + # Check full constructor. + bs1 = OCIO.GradingBSplineCurve([0, 0, 0.1, 0.5, 0.4, 0.6, 0.6, 0.7, 1, 1]) + bs2 = OCIO.GradingBSplineCurve([0.1, 0.5, 0.4, 0.6, 0.6, 0.7, 1, 1.1]) + rgbc1 = OCIO.GradingRGBCurve(bs1, bs2, bs1, bs2) + rgbc1.validate() + self.assertEqual(rgbc1.isIdentity(), False) + assertEqualBSpline(self, rgbc1.green, bs2) + self.assertEqual(rgbc1.green, bs2) + + def test_huecurve(self): + """ + Test the GradingHueCurve, creation, default value, modification. + """ + + # Check default values. + + hueLin = OCIO.GradingHueCurve(OCIO.GRADING_LIN) + + defLin = OCIO.GradingBSplineCurve(3) + cpts = defLin.getControlPoints() + cpts[0] = OCIO.GradingControlPoint(-7, -7) + cpts[1] = OCIO.GradingControlPoint(0, 0) + cpts[2] = OCIO.GradingControlPoint(7, 7) + assertEqualBSpline(self, hueLin.lum_lum, defLin) + + hueLog = OCIO.GradingHueCurve(OCIO.GRADING_LOG) + + defLog = OCIO.GradingBSplineCurve(3) + cpts = defLog.getControlPoints() + cpts[0] = OCIO.GradingControlPoint(0, 0) + cpts[1] = OCIO.GradingControlPoint(0.5, 0.5) + cpts[2] = OCIO.GradingControlPoint(1, 1) + assertEqualBSpline(self, hueLog.lum_lum, defLog) + with self.assertRaises(AssertionError): + assertEqualBSpline(self, hueLog.lum_lum, defLin) + + hueVideo = OCIO.GradingHueCurve(OCIO.GRADING_VIDEO) + assertEqualHueCurve(self, hueLog, hueVideo) + + # Check comparison operators + huec1 = OCIO.GradingHueCurve(OCIO.GRADING_LIN) + huec2 = OCIO.GradingHueCurve(OCIO.GRADING_LIN) + self.assertEqual(huec1, huec2) + self.assertEqual(huec1.isIdentity(), True) + huec1.sat_lum.getControlPoints()[1] = OCIO.GradingControlPoint(0.4, 0.8) + self.assertNotEqual(huec1, huec2) + self.assertEqual(huec1.isIdentity(), False) + huec1.validate() + + # Check full constructor. + hh = OCIO.GradingBSplineCurve(5, OCIO.HUE_HUE) + hs = OCIO.GradingBSplineCurve(5, OCIO.HUE_SAT) + hl = OCIO.GradingBSplineCurve(5, OCIO.HUE_LUM) + ls = OCIO.GradingBSplineCurve([0.1, 0.8, 0.9, 0.7], OCIO.LUM_SAT) + ss = OCIO.GradingBSplineCurve(5, OCIO.SAT_SAT) + ll = OCIO.GradingBSplineCurve(5, OCIO.LUM_LUM) + sl = OCIO.GradingBSplineCurve(5, OCIO.SAT_LUM) + hfx = OCIO.GradingBSplineCurve(5, OCIO.HUE_FX) + hcrv = OCIO.GradingHueCurve(hh, hs, hl, ls, ss, ll, sl, hfx) + hcrv.validate() + self.assertEqual(hcrv.isIdentity(), False) + assertEqualBSpline(self, hcrv.lum_sat, ls) + self.assertEqual(hcrv.lum_sat, ls) def test_rgbmsw(self): """ diff --git a/tests/python/GradingHueCurveTransformTest.py b/tests/python/GradingHueCurveTransformTest.py new file mode 100644 index 0000000000..541ebd88ab --- /dev/null +++ b/tests/python/GradingHueCurveTransformTest.py @@ -0,0 +1,171 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright Contributors to the OpenColorIO Project. + +import unittest +import os +import sys + +import PyOpenColorIO as OCIO +from UnitTestUtils import assertEqualHueCurve + +class GradingHueCurveTransformTest(unittest.TestCase): + + valDefaultLin = OCIO.GradingHueCurve(OCIO.GRADING_LIN) + valDefaultLog = OCIO.GradingHueCurve(OCIO.GRADING_LOG) + + def test_transform_type(self): + """ + Test the getTransformType() method. + """ + gct = OCIO.GradingHueCurveTransform() + self.assertEqual(gct.getTransformType(), OCIO.TRANSFORM_TYPE_GRADING_HUE_CURVE) + + def test_contructor(self): + """ + Test GradingHueCurveTransform constructor without and with keywords. + """ + + gct = OCIO.GradingHueCurveTransform() + self.assertEqual(gct.getStyle(), OCIO.GRADING_LOG) + assertEqualHueCurve(self, gct.getValue(), self.valDefaultLog) + self.assertEqual(gct.isDynamic(), False) + self.assertEqual(gct.getBypassLinToLog(), False) + self.assertEqual(gct.getDrawCurveOnly(), False) + self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_FORWARD) + + gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LIN) + self.assertEqual(gct.getStyle(), OCIO.GRADING_LIN) + assertEqualHueCurve(self, gct.getValue(), self.valDefaultLin) + self.assertEqual(gct.isDynamic(), False) + self.assertEqual(gct.getBypassLinToLog(), False) + self.assertEqual(gct.getDrawCurveOnly(), False) + self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_FORWARD) + + vals = OCIO.GradingHueCurve(OCIO.GRADING_LOG) + vals.sat_lum = OCIO.GradingBSplineCurve(4, OCIO.SAT_LUM) + cpts = vals.sat_lum.getControlPoints() + cpts[0] = OCIO.GradingControlPoint(0.0, 0.1) + cpts[1] = OCIO.GradingControlPoint(0.1, 0.5) + cpts[2] = OCIO.GradingControlPoint(0.4, 0.6) + cpts[3] = OCIO.GradingControlPoint(0.6, 0.7) + gct = OCIO.GradingHueCurveTransform(style=OCIO.GRADING_VIDEO, values=vals, + dynamic=True, dir=OCIO.TRANSFORM_DIR_INVERSE) + self.assertEqual(gct.getStyle(), OCIO.GRADING_VIDEO) + self.assertEqual(gct.isDynamic(), True) + self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_INVERSE) + assertEqualHueCurve(self, gct.getValue(), vals) + + def test_style(self): + """ + Test setStyle() and getStyle(). + """ + + gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LOG) + for style in OCIO.GradingStyle.__members__.values(): + gct.setStyle(style) + self.assertEqual(gct.getStyle(), style) + + def test_misc(self): + """ + Test miscellaneous getters/setters. + """ + + gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LOG) + self.assertEqual(gct.getBypassLinToLog(), False) + gct.setBypassLinToLog(True) + self.assertEqual(gct.getBypassLinToLog(), True) + self.assertEqual(gct.getDrawCurveOnly(), False) + gct.setDrawCurveOnly(True) + self.assertEqual(gct.getDrawCurveOnly(), True) + self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_FORWARD) + gct.setDirection(OCIO.TRANSFORM_DIR_INVERSE) + self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_INVERSE) + + def test_values(self): + """ + Test setValue() and getValue(). + """ + + gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LOG) + gct.setValue(self.valDefaultLin) + assertEqualHueCurve(self, gct.getValue(), self.valDefaultLin) + self.assertEqual(gct.getValue(), self.valDefaultLin) + + def test_slopes(self): + """ + Test slopes. + """ + + gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LOG) + self.assertEqual(gct.slopesAreDefault(OCIO.HUE_HUE), True) + + gct.setSlope(OCIO.HUE_HUE, 3, 1.1) + self.assertEqual(gct.slopesAreDefault(OCIO.HUE_HUE), False) + self.assertEqual(gct.slopesAreDefault(OCIO.HUE_SAT), True) + + s1 = gct.getSlope(OCIO.HUE_HUE, 3) + self.assertAlmostEqual(s1, 1.1, delta=1e-6) + with self.assertRaises(OCIO.Exception): + # Length of slopes must match control points. + gct.setSlope(OCIO.HUE_HUE, 6, 1.1) + + def test_dynamic(self): + """ + Test isDynamic() and makeDynamic(). + """ + + gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LOG) + self.assertEqual(gct.isDynamic(), False) + gct.makeDynamic() + self.assertEqual(gct.isDynamic(), True) + + def test_validation(self): + """ + Test validate() and setValue(). + """ + + gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LOG) + gct.validate() + + # 3rd control point x is lower than 2nd control point x. + vals = OCIO.GradingHueCurve(OCIO.GRADING_LOG) + vals.sat_lum = OCIO.GradingBSplineCurve([0, 0, 0.5, 0.2, 0.2, 0.5, 1, 1], OCIO.SAT_LUM) + + with self.assertRaises(OCIO.Exception): + gct.setValue(vals); + + def test_apply_inverse(self): + """ + Test applying transform with inversion. + """ + + gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LOG) + vals = OCIO.GradingHueCurve(OCIO.GRADING_LOG) + vals.hue_lum = OCIO.GradingBSplineCurve([0, 2., 0.9, 2.], OCIO.HUE_LUM) + gct.setValue(vals) + vals2 = gct.getValue() + self.assertEqual(vals, vals2) + + cfg = OCIO.Config().CreateRaw() + proc = cfg.getProcessor(gct) + cpu = proc.getDefaultCPUProcessor() + + # Apply the transform and keep the result. + pixel = [0.7, 0.5, 0.1] + rgb1 = cpu.applyRGB(pixel) + + # The processing did something. + self.assertAlmostEqual( 0.8, rgb1[0], delta=1e-5) + self.assertAlmostEqual( 0.6, rgb1[1], delta=1e-5) + self.assertAlmostEqual( 0.2, rgb1[2], delta=1e-5) + + # Invert. + gct.setDirection(OCIO.TRANSFORM_DIR_INVERSE) + proc = cfg.getProcessor(gct) + cpu = proc.getDefaultCPUProcessor() + pixel2 = cpu.applyRGB(rgb1) + + # Invert back to original value. + self.assertAlmostEqual(pixel[0], pixel2[0], delta=1e-5) + self.assertAlmostEqual(pixel[1], pixel2[1], delta=1e-5) + self.assertAlmostEqual(pixel[2], pixel2[2], delta=1e-5) diff --git a/tests/python/OpenColorIOTestSuite.py b/tests/python/OpenColorIOTestSuite.py index 89bd110087..545197fe60 100755 --- a/tests/python/OpenColorIOTestSuite.py +++ b/tests/python/OpenColorIOTestSuite.py @@ -69,6 +69,7 @@ import GpuShaderDescTest import GradingDataTest import GradingPrimaryTransformTest +import GradingHueCurveTransformTest import GradingRGBCurveTransformTest import GradingToneTransformTest import GroupTransformTest @@ -102,52 +103,53 @@ def suite(): suite = unittest.TestSuite() loader = unittest.TestLoader() - suite.addTest(loader.loadTestsFromModule(AllocationTransformTest)) - suite.addTest(loader.loadTestsFromModule(BakerTest)) - suite.addTest(loader.loadTestsFromModule(BuiltinConfigRegistryTest)) - suite.addTest(loader.loadTestsFromModule(BuiltinTransformRegistryTest)) - suite.addTest(loader.loadTestsFromModule(BuiltinTransformTest)) - suite.addTest(loader.loadTestsFromModule(CDLTransformTest)) - suite.addTest(loader.loadTestsFromModule(ColorSpaceHelpersTest)) - suite.addTest(loader.loadTestsFromModule(ColorSpaceTest)) - suite.addTest(loader.loadTestsFromModule(ColorSpaceTransformTest)) - suite.addTest(loader.loadTestsFromModule(ConfigTest)) - suite.addTest(loader.loadTestsFromModule(ConstantsTest)) - suite.addTest(loader.loadTestsFromModule(ContextTest)) - suite.addTest(loader.loadTestsFromModule(CPUProcessorTest)) - suite.addTest(loader.loadTestsFromModule(DisplayViewHelpersTest)) - suite.addTest(loader.loadTestsFromModule(DisplayViewTransformTest)) - suite.addTest(loader.loadTestsFromModule(ExponentTransformTest)) - suite.addTest(loader.loadTestsFromModule(ExponentWithLinearTransformTest)) - suite.addTest(loader.loadTestsFromModule(ExposureContrastTransformTest)) - suite.addTest(loader.loadTestsFromModule(FileTransformTest)) - suite.addTest(loader.loadTestsFromModule(FileRulesTest)) - suite.addTest(loader.loadTestsFromModule(FixedFunctionTransformTest)) - suite.addTest(loader.loadTestsFromModule(FormatMetadataTest)) - suite.addTest(loader.loadTestsFromModule(GpuShaderDescTest)) +# suite.addTest(loader.loadTestsFromModule(AllocationTransformTest)) +# suite.addTest(loader.loadTestsFromModule(BakerTest)) +# suite.addTest(loader.loadTestsFromModule(BuiltinConfigRegistryTest)) +# suite.addTest(loader.loadTestsFromModule(BuiltinTransformRegistryTest)) +# suite.addTest(loader.loadTestsFromModule(BuiltinTransformTest)) +# suite.addTest(loader.loadTestsFromModule(CDLTransformTest)) +# suite.addTest(loader.loadTestsFromModule(ColorSpaceHelpersTest)) +# suite.addTest(loader.loadTestsFromModule(ColorSpaceTest)) +# suite.addTest(loader.loadTestsFromModule(ColorSpaceTransformTest)) +# suite.addTest(loader.loadTestsFromModule(ConfigTest)) +# suite.addTest(loader.loadTestsFromModule(ConstantsTest)) +# suite.addTest(loader.loadTestsFromModule(ContextTest)) +# suite.addTest(loader.loadTestsFromModule(CPUProcessorTest)) +# suite.addTest(loader.loadTestsFromModule(DisplayViewHelpersTest)) +# suite.addTest(loader.loadTestsFromModule(DisplayViewTransformTest)) +# suite.addTest(loader.loadTestsFromModule(ExponentTransformTest)) +# suite.addTest(loader.loadTestsFromModule(ExponentWithLinearTransformTest)) +# suite.addTest(loader.loadTestsFromModule(ExposureContrastTransformTest)) +# suite.addTest(loader.loadTestsFromModule(FileTransformTest)) +# suite.addTest(loader.loadTestsFromModule(FileRulesTest)) +# suite.addTest(loader.loadTestsFromModule(FixedFunctionTransformTest)) +# suite.addTest(loader.loadTestsFromModule(FormatMetadataTest)) +# suite.addTest(loader.loadTestsFromModule(GpuShaderDescTest)) suite.addTest(loader.loadTestsFromModule(GradingDataTest)) - suite.addTest(loader.loadTestsFromModule(GradingPrimaryTransformTest)) - suite.addTest(loader.loadTestsFromModule(GradingRGBCurveTransformTest)) - suite.addTest(loader.loadTestsFromModule(GradingToneTransformTest)) - suite.addTest(loader.loadTestsFromModule(GroupTransformTest)) - suite.addTest(loader.loadTestsFromModule(LegacyViewingPipelineTest)) - suite.addTest(loader.loadTestsFromModule(LogCameraTransformTest)) - suite.addTest(loader.loadTestsFromModule(LogTransformTest)) - suite.addTest(loader.loadTestsFromModule(LookTest)) - suite.addTest(loader.loadTestsFromModule(LookTransformTest)) - suite.addTest(loader.loadTestsFromModule(Lut1DTransformTest)) - suite.addTest(loader.loadTestsFromModule(Lut3DTransformTest)) - suite.addTest(loader.loadTestsFromModule(MatrixTransformTest)) - suite.addTest(loader.loadTestsFromModule(MixingHelpersTest)) - suite.addTest(loader.loadTestsFromModule(NamedTransformTest)) - suite.addTest(loader.loadTestsFromModule(OCIOZArchiveTest)) - suite.addTest(loader.loadTestsFromModule(OpenColorIOTest)) - suite.addTest(loader.loadTestsFromModule(ProcessorCacheTest)) - suite.addTest(loader.loadTestsFromModule(ProcessorTest)) - suite.addTest(loader.loadTestsFromModule(RangeTransformTest)) - suite.addTest(loader.loadTestsFromModule(TransformsTest)) - suite.addTest(loader.loadTestsFromModule(ViewingRulesTest)) - suite.addTest(loader.loadTestsFromModule(ViewTransformTest)) +# suite.addTest(loader.loadTestsFromModule(GradingPrimaryTransformTest)) + suite.addTest(loader.loadTestsFromModule(GradingHueCurveTransformTest)) +# suite.addTest(loader.loadTestsFromModule(GradingRGBCurveTransformTest)) +# suite.addTest(loader.loadTestsFromModule(GradingToneTransformTest)) +# suite.addTest(loader.loadTestsFromModule(GroupTransformTest)) +# suite.addTest(loader.loadTestsFromModule(LegacyViewingPipelineTest)) +# suite.addTest(loader.loadTestsFromModule(LogCameraTransformTest)) +# suite.addTest(loader.loadTestsFromModule(LogTransformTest)) +# suite.addTest(loader.loadTestsFromModule(LookTest)) +# suite.addTest(loader.loadTestsFromModule(LookTransformTest)) +# suite.addTest(loader.loadTestsFromModule(Lut1DTransformTest)) +# suite.addTest(loader.loadTestsFromModule(Lut3DTransformTest)) +# suite.addTest(loader.loadTestsFromModule(MatrixTransformTest)) +# suite.addTest(loader.loadTestsFromModule(MixingHelpersTest)) +# suite.addTest(loader.loadTestsFromModule(NamedTransformTest)) +# suite.addTest(loader.loadTestsFromModule(OCIOZArchiveTest)) +# suite.addTest(loader.loadTestsFromModule(OpenColorIOTest)) +# suite.addTest(loader.loadTestsFromModule(ProcessorCacheTest)) +# suite.addTest(loader.loadTestsFromModule(ProcessorTest)) +# suite.addTest(loader.loadTestsFromModule(RangeTransformTest)) +# suite.addTest(loader.loadTestsFromModule(TransformsTest)) +# suite.addTest(loader.loadTestsFromModule(ViewingRulesTest)) +# suite.addTest(loader.loadTestsFromModule(ViewTransformTest)) return suite diff --git a/tests/python/UnitTestUtils.py b/tests/python/UnitTestUtils.py index dd41586547..07c5b0aff7 100644 --- a/tests/python/UnitTestUtils.py +++ b/tests/python/UnitTestUtils.py @@ -61,12 +61,42 @@ def assertEqualBSpline(testCase, first, second): else: raise AssertionError("Different number of control points") +def assertAlmostEqualVector(testCase, first, second, delta=1e-6): + if len(first) != len(second): + raise AssertionError("Different number of elements") + for pt1, pt2 in zip(first, second): + testCase.assertAlmostEqual(pt1, pt2, delta=delta) + +# +# for pt1 in first: +# try: +# pt2 = next(second) +# testCase.assertAlmostEqual(testCase, pt1, pt2, delta=delta) +# except StopIteration: +# raise AssertionError("Different number of elements") +# try: +# pt2 = next(second) +# except StopIteration: +# pass +# else: +# raise AssertionError("Different number of elements") + def assertEqualRGBCurve(testCase, first, second): assertEqualBSpline(testCase, first.red, second.red) assertEqualBSpline(testCase, first.green, second.green) assertEqualBSpline(testCase, first.blue, second.blue) assertEqualBSpline(testCase, first.master, second.master) +def assertEqualHueCurve(testCase, first, second): + assertEqualBSpline(testCase, first.hue_hue, second.hue_hue) + assertEqualBSpline(testCase, first.hue_sat, second.hue_sat) + assertEqualBSpline(testCase, first.hue_lum, second.hue_lum) + assertEqualBSpline(testCase, first.lum_sat, second.lum_sat) + assertEqualBSpline(testCase, first.sat_sat, second.sat_sat) + assertEqualBSpline(testCase, first.lum_lum, second.lum_lum) + assertEqualBSpline(testCase, first.sat_lum, second.sat_lum) + assertEqualBSpline(testCase, first.hue_fx, second.hue_fx) + class MuteLogging: def __init__(self): self.previous_function = None From 43df6836552f9ec6dd21300631165d3e44ba5988 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Mon, 28 Jul 2025 00:56:47 -0400 Subject: [PATCH 23/30] Improve interface, add tests Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTransforms.h | 89 +++++--- src/OpenColorIO/DynamicProperty.cpp | 5 +- src/OpenColorIO/OCIOYaml.cpp | 15 +- .../fileformats/ctf/CTFReaderHelper.cpp | 8 +- .../fileformats/ctf/CTFReaderUtils.h | 1 + .../fileformats/ctf/CTFTransform.cpp | 4 +- .../ops/gradinghuecurve/GradingHueCurve.cpp | 37 ++- .../ops/gradinghuecurve/GradingHueCurve.h | 3 + .../gradinghuecurve/GradingHueCurveOpCPU.cpp | 99 ++++++-- .../gradinghuecurve/GradingHueCurveOpData.cpp | 12 +- .../gradinghuecurve/GradingHueCurveOpData.h | 10 +- .../gradinghuecurve/GradingHueCurveOpGPU.cpp | 214 ++++++++++-------- .../gradingrgbcurve/GradingBSplineCurve.cpp | 99 ++++---- .../ops/gradingrgbcurve/GradingBSplineCurve.h | 16 +- .../gradingrgbcurve/GradingRGBCurveOpCPU.cpp | 12 +- .../gradingrgbcurve/GradingRGBCurveOpGPU.cpp | 5 +- .../transforms/GradingHueCurveTransform.cpp | 22 +- .../transforms/GradingHueCurveTransform.h | 7 +- src/bindings/python/PyGradingData.cpp | 4 + .../transforms/PyGradingHueCurveTransform.cpp | 12 +- tests/cpu/Config_tests.cpp | 1 + tests/cpu/DynamicProperty_tests.cpp | 38 +++- tests/cpu/fileformats/FileFormatCTF_tests.cpp | 6 +- .../GradingHueCurveOpCPU_tests.cpp | 90 ++++++++ .../GradingHueCurveOpData_tests.cpp | 6 +- .../gradinghuecurve/GradingHueCurve_tests.cpp | 11 +- .../GradingHueCurveTransform_tests.cpp | 17 +- tests/gpu/GPUUnitTest.cpp | 29 ++- tests/gpu/GPUUnitTest.h | 9 +- tests/gpu/GradingHueCurveOp_test.cpp | 117 +++++++++- tests/python/GradingDataTest.py | 9 + tests/python/GradingHueCurveTransformTest.py | 15 +- 32 files changed, 702 insertions(+), 320 deletions(-) diff --git a/include/OpenColorIO/OpenColorTransforms.h b/include/OpenColorIO/OpenColorTransforms.h index 77f6d24bd2..88da1c6f56 100644 --- a/include/OpenColorIO/OpenColorTransforms.h +++ b/include/OpenColorIO/OpenColorTransforms.h @@ -606,6 +606,18 @@ class OCIOEXPORT GradingHueCurve virtual GradingHueCurveRcPtr createEditableCopy() const = 0; virtual void validate() const = 0; virtual bool isIdentity() const = 0; + /** + * Enable drawCurveOnly mode to return the output value of a spline curve without any of the + * other associated processing of the RGB values. This is useful when the curves need to be + * graphed independently in a user interface. To use this, set the curve parameters on the + * Hue-Sat curve. The R, G, and B values will be sent through that curve with the interpretation + * that they are the input axis to the curve (which would be hue, sat, or luma) rather than RGB. + * This mode does not apply the RGB-to-HSY or Lin-to-Log, so for scene-linear curves the luma + * values are interpreted as already being in the logarithmic (f-stop) space. The forward curve + * evaluation is done regardless of the transform direction. + */ + virtual bool getDrawCurveOnly() const = 0; + virtual void setDrawCurveOnly(bool drawCurveOnly) = 0; virtual ConstGradingBSplineCurveRcPtr getCurve(HueCurveType c) const = 0; virtual GradingBSplineCurveRcPtr getCurve(HueCurveType c) = 0; @@ -1268,26 +1280,43 @@ class OCIOEXPORT GradingPrimaryTransform : public Transform extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingPrimaryTransform &) noexcept; /** - * Hue color correction controls. - * - * RGB curve color correction controls. - * - - -UPDATE - - * This transform allows for modifying tone reproduction via B-spline curves. - * - * There is an R, G, and B curve along with a Master curve (that applies to R, G, and B). Each - * curve is specified via the x and y coordinates of its control points. A monotonic spline is - * fit to the control points. The x coordinates must be non-decreasing. When the grading style - * is linear, the units for the control points are photographic stops relative to 0.18. + * Hue curve color correction controls. * + * This transform provides eight spline curves to make the following adjustments: + * + * - Hue-Hue: Map input hue to output hue (where a diagonal line is the identity). + * - Hue-Sat: Adjust saturation as a function of hue (a value of 1.0 is the identity). + * - Hue-Lum: Adjust luma as a function of hue (a value of 1.0 is the identity). + * - Lum-Sat: Adjust saturation as a function of luma (a value of 1.0 is the identity). + * - Sat-Sat: Adjust saturation as a function of saturation (a diagonal is the identity). + * - Lum-Lum: Adjust luma as a function of luma, maintaining hue & sat (diagonal is identity). + * - Sat-Lum: Adjust luma as a function of saturation (a value of 1.0 is the identity). + * - Hue-FX : Map input hue to delta output hue (a value of 0.0 is the identity). + * + * The algorithm is different for scene-linear, logarithmic, and video color spaces, so + * initialize the style argument appropriately before setting the curves. + * + * An RGB-to-HSY FixedFunction is used to convert RGB into a hue, saturation, luma color + * space. However, there is an option to bypass that conversion to use an outboard transform. + * + * Like the GradingRGBCurveTransform, the curves are defined by the x and y coordinates of a + * set of control points. A spline will be fit to the control points. Monotonicity is preserved + * for curves where the diagonal is the identity. For curves that take luma as input, if the + * style is scene-linear, the units are in photographic stops relative to 0.18. For log and + * video, the luma is scaled the same as the input RGB. + * + * The hue variable is [0,1] and is periodic. For example, -0.2, 0.8, and 1.8 are equivalent. + * The domain of the curves is [0,1] and control points outside that domain are mapped into it. + * A hue of 0 or 1 corresponds to a magenta hue. + * + * The transform is invertible as long as the curves allow it. For example, if saturation is + * mapped to zero, obviously that cannot be resaturated. Care should be taken with the Hue-FX + * curve because it is possible to fold hues over on themselves, which also cannot be inverted. + * In most cases the Hue-FX curve is not necessary since the Hue-Hue curve provides similar + * functionality with the added benefit of being strictly invertible. + * * The control points are dynamic, so they may be adjusted even after the Transform is included * in a Processor. - - - */ class OCIOEXPORT GradingHueCurveTransform : public Transform { @@ -1312,7 +1341,7 @@ class OCIOEXPORT GradingHueCurveTransform : public Transform virtual const ConstGradingHueCurveRcPtr getValue() const = 0; /// Throws if value is not valid. - virtual void setValue(const ConstGradingHueCurveRcPtr & values) = 0; + virtual void setValue(const ConstGradingHueCurveRcPtr & value) = 0; /** * It is possible to provide a desired slope value for each control point. The number of slopes is @@ -1327,24 +1356,12 @@ class OCIOEXPORT GradingHueCurveTransform : public Transform virtual bool slopesAreDefault(HueCurveType c) const = 0; /** - * The scene-linear grading style applies a lin-to-log transform to the pixel - * values before going through the curve. However, in some cases (e.g. drawing curves in a UI) - * it may be useful to bypass the lin-to-log. Default value is false. - */ - virtual bool getBypassLinToLog() const = 0; - virtual void setBypassLinToLog(bool bypass) = 0; - - /** - * Enable drawCurveOnly mode to return the output value of a spline curve without any of the - * other associated processing of the RGB values. This is useful when the curves need to be - * graphed independently in a user interface. To use this, set the curve parameters on the - * Hue-Sat curve. The R, G, and B values will be sent through that curve but with the interpretation - * that they are the input axis to the curve (which would be hue, sat, or luma) rather than RGB. - * This mode ignores the setting of BypassLinToLog, so for scene-linear curves the luma values are - * interpreted as already being in the logarithmic (f-stop) space. + * By default, the input is transformed into HSY space to apply the hue curves and then the result is + * transformed back to RGB. However, this may be bypassed to use other hue/sat/luma type transforms + * applied separately before and after this transform. */ - virtual bool getDrawCurveOnly() const = 0; - virtual void setDrawCurveOnly( bool drawCurveOnly ) = 0; + virtual bool getBypassRGBToHSY() const = 0; + virtual void setBypassRGBToHSY(bool bypass) = 0; ///** // * Parameters can be made dynamic so the values can be changed through the CPU or GPU processor, @@ -1371,7 +1388,7 @@ extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingHueCurv * * This transform allows for modifying tone reproduction via B-spline curves. * - * There is an R, G, and B curve along with a Master curve (that applies to R, G, and B). Each + * There is an R, G, and B curve followed by a Master curve (that applies to R, G, and B). Each * curve is specified via the x and y coordinates of its control points. A monotonic spline is * fit to the control points. The x coordinates must be non-decreasing. When the grading style * is linear, the units for the control points are photographic stops relative to 0.18. diff --git a/src/OpenColorIO/DynamicProperty.cpp b/src/OpenColorIO/DynamicProperty.cpp index 3fe51f7562..6c8bee5489 100644 --- a/src/OpenColorIO/DynamicProperty.cpp +++ b/src/OpenColorIO/DynamicProperty.cpp @@ -276,7 +276,7 @@ void DynamicPropertyGradingRGBCurveImpl::precompute() { ConstGradingBSplineCurveRcPtr curve = m_gradingRGBCurve->getCurve(c); auto curveImpl = dynamic_cast(curve.get()); - curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c)); + curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c), false); } if (m_knotsCoefs.m_numKnots <= 0) m_knotsCoefs.m_localBypass = true; } @@ -370,7 +370,8 @@ void DynamicPropertyGradingHueCurveImpl::precompute() { ConstGradingBSplineCurveRcPtr curve = m_gradingHueCurve->getCurve(c); auto curveImpl = dynamic_cast(curve.get()); - curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c)); + curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c), + m_gradingHueCurve->getDrawCurveOnly()); } if (m_knotsCoefs.m_numKnots == 0) m_knotsCoefs.m_localBypass = true; } diff --git a/src/OpenColorIO/OCIOYaml.cpp b/src/OpenColorIO/OCIOYaml.cpp index f57c36a7e3..1da3d78ca8 100644 --- a/src/OpenColorIO/OCIOYaml.cpp +++ b/src/OpenColorIO/OCIOYaml.cpp @@ -2094,6 +2094,12 @@ inline void load(const YAML::Node & node, GradingHueCurveTransformRcPtr & t) load(iter->second, val); t->setDirection(val); } + else if (key == "rgbtohsy_bypass") + { + bool bypass = true; + load(iter->second, bypass); + t->setBypassRGBToHSY(bypass); + } else if (key == "hue_hue") { hh = GradingBSplineCurve::Create(0, HUE_HUE); @@ -2146,9 +2152,6 @@ inline void load(const YAML::Node & node, GradingHueCurveTransformRcPtr & t) } } -// auto & defCurve = t->getStyle() == GRADING_LIN ? GradingHueCurveImpl::DefaultCurvesLin : -// GradingHueCurveImpl::DefaultCurves; -// if (!hh) hh = GradingHueCurveImpl::DefaultHueHue.createEditableCopy(); if (!hs) hs = GradingHueCurveImpl::DefaultHueSat.createEditableCopy(); if (!hl) hl = GradingHueCurveImpl::DefaultHueLum.createEditableCopy(); @@ -2193,6 +2196,12 @@ inline void save(YAML::Emitter & out, ConstGradingHueCurveTransformRcPtr t) out << YAML::Key << "style"; out << YAML::Value << YAML::Flow << GradingStyleToString(style); + if (t->getBypassRGBToHSY()) + { + out << YAML::Key << "rgbtohsy_bypass"; + out << YAML::Value << YAML::Flow << true; + } + static const std::vector curveNames = { "hue_hue", "hue_sat", "hue_lum", "lum_sat", "sat_sat", "lum_lum", "sat_lum", "hue_fx" }; for (int c = 0; c < HUE_NUM_CURVES; ++c) diff --git a/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp b/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp index 7dee4ba41c..ba139739ac 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp @@ -2665,7 +2665,7 @@ bool CTFReaderGradingHueCurveElt::isOpParameterValid(const char * att) const noe { return CTFReaderOpElt::isOpParameterValid(att) || 0 == Platform::Strcasecmp(ATTR_STYLE, att) || - 0 == Platform::Strcasecmp(ATTR_BYPASS_LIN_TO_LOG, att); + 0 == Platform::Strcasecmp(ATTR_BYPASS_RGB_TO_HSY, att); } void CTFReaderGradingHueCurveElt::start(const char ** atts) @@ -2696,17 +2696,17 @@ void CTFReaderGradingHueCurveElt::start(const char ** atts) } isStyleFound = true; } - else if (0 == Platform::Strcasecmp(ATTR_BYPASS_LIN_TO_LOG, atts[i])) + else if (0 == Platform::Strcasecmp(ATTR_BYPASS_RGB_TO_HSY, atts[i])) { if (0 != Platform::Strcasecmp("true", atts[i + 1])) { std::ostringstream oss; - oss << "Unknown bypassLinToLog value: '" << atts[i + 1]; + oss << "Unknown bypassRGBToHSY value: '" << atts[i + 1]; oss << "' while parsing HueCurve."; throwMessage(oss.str()); } - m_gradingHueCurve->setBypassLinToLog(true); + m_gradingHueCurve->setBypassRGBToHSY(true); } i += 2; diff --git a/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h b/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h index efb45e0ff2..68cf80f702 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h +++ b/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h @@ -98,6 +98,7 @@ static constexpr char ATTR_BITDEPTH_IN[] = "inBitDepth"; static constexpr char ATTR_BITDEPTH_OUT[] = "outBitDepth"; static constexpr char ATTR_BYPASS[] = "bypass"; static constexpr char ATTR_BYPASS_LIN_TO_LOG[] = "bypassLinToLog"; +static constexpr char ATTR_BYPASS_RGB_TO_HSY[] = "bypassRGBToHSY"; static constexpr char ATTR_CENTER[] = "center"; static constexpr char ATTR_CHAN[] = "channel"; static constexpr char ATTR_COMP_CLF_VERSION[] = "compCLFversion"; diff --git a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp index df7f698ef0..c018d7ef6f 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp @@ -1682,9 +1682,9 @@ void GradingHueCurveWriter::getAttributes(XmlFormatter::Attributes& attributes) const auto styleStr = ConvertGradingStyleAndDirToString(style, dir); attributes.push_back(XmlFormatter::Attribute(ATTR_STYLE, styleStr)); - if (m_curves->getBypassLinToLog()) + if (m_curves->getBypassRGBToHSY()) { - attributes.push_back(XmlFormatter::Attribute(ATTR_BYPASS_LIN_TO_LOG, "true")); + attributes.push_back(XmlFormatter::Attribute(ATTR_BYPASS_RGB_TO_HSY, "true")); } } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp index 3e29aaf411..c59ee944f9 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp @@ -120,6 +120,7 @@ GradingHueCurveImpl::GradingHueCurveImpl(const ConstGradingHueCurveRcPtr & rhs) { m_curves[c] = impl->m_curves[c]->createEditableCopy(); } + m_drawCurveOnly = rhs->getDrawCurveOnly(); } } @@ -130,7 +131,8 @@ GradingHueCurveRcPtr GradingHueCurveImpl::createEditableCopy() const { newCurve->m_curves[c] = m_curves[c]->createEditableCopy(); } - + newCurve->setDrawCurveOnly(m_drawCurveOnly); + GradingHueCurveRcPtr res = newCurve; return res; } @@ -182,14 +184,19 @@ void GradingHueCurveImpl::validate() const throw Exception(oss.str().c_str()); } - const BSplineType splineType = m_curves[c]->getSplineType(); - const HueCurveType hueType = static_cast(c); - if (splineType != GetBSplineTypeForHueCurveType(hueType)) + // Unless drawCurveOnly is enabled, check that the spline type is correct for + // the given hue curve type. + if (!getDrawCurveOnly()) { - std::ostringstream oss; - oss << "GradingHueCurve validation failed: '" << CurveTypeName(c) << "' curve " - << "is of the wrong BSplineType."; - throw Exception(oss.str().c_str()); + const BSplineType splineType = m_curves[c]->getSplineType(); + const HueCurveType hueType = static_cast(c); + if (splineType != GetBSplineTypeForHueCurveType(hueType)) + { + std::ostringstream oss; + oss << "GradingHueCurve validation failed: '" << CurveTypeName(c) << "' curve " + << "is of the wrong BSplineType."; + throw Exception(oss.str().c_str()); + } } } } @@ -206,6 +213,16 @@ bool GradingHueCurveImpl::isIdentity() const return true; } +bool GradingHueCurveImpl::getDrawCurveOnly() const +{ + return m_drawCurveOnly; +} + +void GradingHueCurveImpl::setDrawCurveOnly(bool drawCurveOnly) +{ + m_drawCurveOnly = drawCurveOnly; +} + bool GradingHueCurveImpl::isHueCurveTypeValid(HueCurveType c) const { return ( c >= HUE_HUE && @@ -308,6 +325,10 @@ bool operator==(const GradingHueCurve & lhs, const GradingHueCurve & rhs) return false; } } + if (lhs.getDrawCurveOnly() != rhs.getDrawCurveOnly()) + { + return false; + } return true; } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h index 2c2f456236..6d9bd35522 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.h @@ -36,6 +36,8 @@ class GradingHueCurveImpl : public GradingHueCurve void validate() const override; bool isIdentity() const override; + bool getDrawCurveOnly() const override; + void setDrawCurveOnly(bool drawCurveOnly) override; ConstGradingBSplineCurveRcPtr getCurve(HueCurveType) const override; GradingBSplineCurveRcPtr getCurve(HueCurveType) override; @@ -56,6 +58,7 @@ class GradingHueCurveImpl : public GradingHueCurve private: bool isHueCurveTypeValid(HueCurveType c) const; + bool m_drawCurveOnly = false; std::array(HUE_NUM_CURVES)> m_curves; }; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp index 5054d1183c..d10e1037fd 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp @@ -12,6 +12,8 @@ #include "ops/gradinghuecurve/GradingHueCurveOpCPU.h" #include "ops/fixedfunction/FixedFunctionOpCPU.h" #include "ops/fixedfunction/FixedFunctionOpData.h" +#include "ops/matrix/MatrixOpCPU.h" +#include "ops/matrix/MatrixOpData.h" namespace OCIO_NAMESPACE { @@ -85,7 +87,7 @@ GradingHueCurveOpCPU::GradingHueCurveOpCPU(ConstGradingHueCurveOpDataRcPtr & gcD const GradingStyle style = gcData->getStyle(); FixedFunctionOpData::Style fwdStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; - FixedFunctionOpData::Style invStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + FixedFunctionOpData::Style invStyle = FixedFunctionOpData::HSY_LIN_TO_RGB; switch(style) { case GRADING_LIN: @@ -103,12 +105,22 @@ GradingHueCurveOpCPU::GradingHueCurveOpCPU(ConstGradingHueCurveOpDataRcPtr & gcD break; } - ConstFixedFunctionOpDataRcPtr fwdOpData = std::make_shared(fwdStyle); - m_rgbToHsyOp = GetFixedFunctionCPURenderer(fwdOpData, false /* fastLogExpPow */); - ConstFixedFunctionOpDataRcPtr invOpData = std::make_shared(invStyle); - m_hsyToRgbOp = GetFixedFunctionCPURenderer(invOpData, false /* fastLogExpPow */); + if (gcData->getBypassRGBToHSY()) + { + // TODO: Could template the apply function to avoid the need for a matrix op. + ConstMatrixOpDataRcPtr fwdOpData = std::make_shared(TRANSFORM_DIR_FORWARD); + m_rgbToHsyOp = GetMatrixRenderer(fwdOpData); + m_hsyToRgbOp = GetMatrixRenderer(fwdOpData); + } + else + { + ConstFixedFunctionOpDataRcPtr fwdOpData = std::make_shared(fwdStyle); + m_rgbToHsyOp = GetFixedFunctionCPURenderer(fwdOpData, false /* fastLogExpPow */); + ConstFixedFunctionOpDataRcPtr invOpData = std::make_shared(invStyle); + m_hsyToRgbOp = GetFixedFunctionCPURenderer(invOpData, false /* fastLogExpPow */); + } - if (style == GRADING_LIN && !gcData->getBypassLinToLog()) + if (style == GRADING_LIN) { m_applyLinLog = LinLog; m_applyLogLin = LogLin; @@ -147,6 +159,44 @@ DynamicPropertyRcPtr GradingHueCurveOpCPU::getDynamicProperty(DynamicPropertyTyp throw Exception("GradingHueCurve property is not dynamic."); } +class GradingHueCurveDrawOpCPU : public GradingHueCurveOpCPU +{ +public: + GradingHueCurveDrawOpCPU(const GradingHueCurveOpCPU &) = delete; + GradingHueCurveDrawOpCPU() = delete; + + explicit GradingHueCurveDrawOpCPU(ConstGradingHueCurveOpDataRcPtr & ghuec); + void apply(const void * inImg, void * outImg, long numPixels) const override; +}; + +GradingHueCurveDrawOpCPU::GradingHueCurveDrawOpCPU(ConstGradingHueCurveOpDataRcPtr & ghuec) + : GradingHueCurveOpCPU(ghuec) +{ +} + +void GradingHueCurveDrawOpCPU::apply(const void * inImg, void * outImg, long numPixels) const +{ + // NB: LocalBypass does not matter, need to evaluate even if it's an identity. + + const GradingBSplineCurveImpl::KnotsCoefs & knotsCoefs = m_ghuecurve->getKnotsCoefs(); + + const float * in = (float *)inImg; + float * out = (float *)outImg; + + for (long idx = 0; idx < numPixels; ++idx) + { + // In drawCurveOnly mode, only evaluate the HueSat curve, with no RGB-to-HSY or LogLin. + // (In practice it may be any of the curves, but only eval the one stored in that slot.) + out[0] = knotsCoefs.evalCurve(static_cast(HUE_SAT), in[0], 1.f); + out[1] = knotsCoefs.evalCurve(static_cast(HUE_SAT), in[1], 1.f); + out[2] = knotsCoefs.evalCurve(static_cast(HUE_SAT), in[2], 1.f); + out[3] = in[3]; + + in += 4; + out += 4; + } +} + class GradingHueCurveFwdOpCPU : public GradingHueCurveOpCPU { public: @@ -187,24 +237,24 @@ void GradingHueCurveFwdOpCPU::apply(const void * inImg, void * outImg, long numP m_applyLinLog(out); // HUE-SAT - const float hueSatGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(HUE_SAT), out[0])); + const float hueSatGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(HUE_SAT), out[0], 1.f)); // HUE-LUM - float hueLumGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(HUE_LUM), out[0])); + float hueLumGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(HUE_LUM), out[0], 1.f)); // HUE-HUE - out[0] = knotsCoefs.evalCurve(static_cast(HUE_HUE), out[0]); + out[0] = knotsCoefs.evalCurve(static_cast(HUE_HUE), out[0], out[0]); // SAT-SAT - out[1] = std::max(0.f, knotsCoefs.evalCurve(static_cast(SAT_SAT), out[1])); + out[1] = std::max(0.f, knotsCoefs.evalCurve(static_cast(SAT_SAT), out[1], out[1])); // LUM-SAT - const float lumSatGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(LUM_SAT), out[2])); + const float lumSatGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(LUM_SAT), out[2], 1.f)); // Apply sat gain. const float satGain = lumSatGain * hueSatGain; out[1] *= satGain; // SAT-LUM - const float satLumGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(SAT_LUM), out[1])); + const float satLumGain = std::max(0.f, knotsCoefs.evalCurve(static_cast(SAT_LUM), out[1], 1.f)); // LUM-LUM - out[2] = knotsCoefs.evalCurve(static_cast(LUM_LUM), out[2]); + out[2] = knotsCoefs.evalCurve(static_cast(LUM_LUM), out[2], out[2]); m_applyLogLin(out); @@ -218,7 +268,7 @@ void GradingHueCurveFwdOpCPU::apply(const void * inImg, void * outImg, long numP // HUE-FX out[0] = out[0] - std::floor(out[0]); // wrap to [0,1) - out[0] = out[0] + knotsCoefs.evalCurve(static_cast(HUE_FX), out[0]); + out[0] = out[0] + knotsCoefs.evalCurve(static_cast(HUE_FX), out[0], 0.f); m_hsyToRgbOp->apply(out, out, 1L); @@ -263,19 +313,19 @@ void GradingHueCurveRevOpCPU::apply(const void * inImg, void * outImg, long numP m_rgbToHsyOp->apply(in, out, 1L); // Invert HUE-FX. - out[0] = knotsCoefs.evalCurveRevHue(static_cast(HUE_FX), out[0], true); + out[0] = knotsCoefs.evalCurveRevHue(static_cast(HUE_FX), out[0]); // Invert HUE-HUE. - out[0] = knotsCoefs.evalCurveRevHue(static_cast(HUE_HUE), out[0], false); + out[0] = knotsCoefs.evalCurveRevHue(static_cast(HUE_HUE), out[0]); // Use the inverted hue to calculate the HUE-SAT & HUE-LUM gains. out[0] = out[0] - std::floor(out[0]); // wrap to [0,1) - const float hue_sat_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(HUE_SAT), out[0]) ); - float hue_lum_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(HUE_LUM), out[0]) ); + const float hue_sat_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(HUE_SAT), out[0], 1.f) ); + float hue_lum_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(HUE_LUM), out[0], 1.f) ); // Use the output sat to calculate the SAT-LUM gain. - out[1] = std::max(0.f, out[1]); // guard against negative saturation - const float sat_lum_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(SAT_LUM), out[1]) ); + out[1] = std::max(0.f, out[1]); // guard against negative saturation + const float sat_lum_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(SAT_LUM), out[1], 1.f) ); hue_lum_gain = 1.f - (1.f - hue_lum_gain) * std::min(out[1], 1.f); @@ -290,7 +340,7 @@ void GradingHueCurveRevOpCPU::apply(const void * inImg, void * outImg, long numP out[2] = knotsCoefs.evalCurveRev(static_cast(LUM_LUM), out[2]); // Use it to calc the LUM-SAT gain. - const float lum_sat_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(LUM_SAT), out[2]) ); + const float lum_sat_gain = std::max( 0.f, knotsCoefs.evalCurve(static_cast(LUM_SAT), out[2], 1.f) ); m_applyLogLin(out); @@ -315,6 +365,13 @@ void GradingHueCurveRevOpCPU::apply(const void * inImg, void * outImg, long numP ConstOpCPURcPtr GetGradingHueCurveCPURenderer(ConstGradingHueCurveOpDataRcPtr & prim) { + // DrawCurveOnly mode ignores the direction, it's always the forward transform. + auto dynProp = prim->getDynamicPropertyInternal(); + if (dynProp->getValue()->getDrawCurveOnly()) + { + return std::make_shared(prim); + } + switch (prim->getDirection()) { case TRANSFORM_DIR_FORWARD: diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp index 27c7096ad3..0ab1c053c1 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp @@ -67,8 +67,7 @@ GradingHueCurveOpData & GradingHueCurveOpData::operator=(const GradingHueCurveOp m_direction = rhs.m_direction; m_style = rhs.m_style; - m_bypassLinToLog = rhs.m_bypassLinToLog; - m_drawCurveOnly = rhs.m_drawCurveOnly; + m_bypassRGBToHSY = rhs.m_bypassRGBToHSY; // Copy dynamic properties. Sharing happens when needed, with CPUOp for instance. m_value->setValue(rhs.m_value->getValue()); @@ -117,7 +116,7 @@ bool GradingHueCurveOpData::isInverse(ConstGradingHueCurveOpDataRcPtr & r) const } if (m_style == r->m_style && - (m_style != GRADING_LIN || m_bypassLinToLog == r->m_bypassLinToLog) && + (m_style != GRADING_LIN || m_bypassRGBToHSY == r->m_bypassRGBToHSY) && m_value->equals(*r->m_value)) { if (CombineTransformDirections(getDirection(), r->getDirection()) == TRANSFORM_DIR_INVERSE) @@ -149,9 +148,9 @@ std::string GradingHueCurveOpData::getCacheID() const cacheIDStream << GradingStyleToString(getStyle()) << " "; cacheIDStream << TransformDirectionToString(getDirection()) << " "; - if (m_bypassLinToLog) + if (m_bypassRGBToHSY) { - cacheIDStream << " bypassLinToLog"; + cacheIDStream << " bypassRGBToHSY"; } if (!isDynamic()) { @@ -229,8 +228,7 @@ bool GradingHueCurveOpData::equals(const OpData & other) const if (m_direction != rop->m_direction || m_style != rop->m_style || - m_bypassLinToLog != rop->m_bypassLinToLog || - m_drawCurveOnly != rop->m_drawCurveOnly || + m_bypassRGBToHSY != rop->m_bypassRGBToHSY || !m_value->equals( *(rop->m_value) )) { return false; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h index 5a5d3795b6..059b679250 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h @@ -63,11 +63,8 @@ class GradingHueCurveOpData : public OpData void setSlope(HueCurveType c, size_t index, float slope); bool slopesAreDefault(HueCurveType c) const; - bool getBypassLinToLog() const noexcept { return m_bypassLinToLog; } - void setBypassLinToLog(bool bypass) noexcept { m_bypassLinToLog = bypass; } - - bool getDrawCurveOnly() const noexcept { return m_drawCurveOnly; } - void setDrawCurveOnly(bool drawCurveOnly) noexcept { m_drawCurveOnly = drawCurveOnly; } + bool getBypassRGBToHSY() const noexcept { return m_bypassRGBToHSY; } + void setBypassRGBToHSY(bool bypass) noexcept { m_bypassRGBToHSY = bypass; } TransformDirection getDirection() const noexcept; void setDirection(TransformDirection dir) noexcept; @@ -87,8 +84,7 @@ class GradingHueCurveOpData : public OpData private: GradingStyle m_style; DynamicPropertyGradingHueCurveImplRcPtr m_value; - bool m_bypassLinToLog{ false }; - bool m_drawCurveOnly{ false }; + bool m_bypassRGBToHSY{ false }; TransformDirection m_direction{ TRANSFORM_DIR_FORWARD }; }; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp index b20f07cbb2..53640cf529 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp @@ -215,18 +215,33 @@ void AddGCPropertiesUniforms(GpuShaderCreatorRcPtr & shaderCreator, AddUniform(shaderCreator, getLB, propNames.m_localBypass); } -void AddCurveFunctionName(GpuShaderCreatorRcPtr & shaderCreator, - GpuShaderText & st, - const std::string & funcName) +void AddCurveFunctionName(GpuShaderCreatorRcPtr & shaderCreator, + GpuShaderText & st, + const std::string & funcName, + bool isFwd) { st.newLine() << ""; if (shaderCreator->getLanguage() == LANGUAGE_OSL_1 || shaderCreator->getLanguage() == GPU_LANGUAGE_MSL_2_0) { - st.newLine() << st.floatKeyword() << " " << funcName << "(int curveIdx, float x, float identity_x)"; + if (isFwd) + { + st.newLine() << st.floatKeyword() << " " << funcName << "(int curveIdx, float x, float identity_x)"; + } + else + { + st.newLine() << st.floatKeyword() << " " << funcName << "(int curveIdx, float x)"; + } } else { - st.newLine() << st.floatKeyword() << " " << funcName << "(in int curveIdx, in float x, in float identity_x)"; + if (isFwd) + { + st.newLine() << st.floatKeyword() << " " << funcName << "(in int curveIdx, in float x, in float identity_x)"; + } + else + { + st.newLine() << st.floatKeyword() << " " << funcName << "(in int curveIdx, in float x)"; + } } } @@ -242,18 +257,19 @@ void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator if (!dyn) { auto propGC = gcData->getDynamicPropertyInternal(); + const int numOffsets = propGC->GetNumOffsetValues(); // 2 ints for each curve. st.newLine() << ""; - st.declareIntArrayConst(props.m_knotsOffsets, 8 * 2, propGC->getKnotsOffsetsArray()); // TODO: Avoid magic numbers (8 Curves * 2 values) + st.declareIntArrayConst(props.m_knotsOffsets, numOffsets, propGC->getKnotsOffsetsArray()); st.declareFloatArrayConst(props.m_knots, propGC->getNumKnots(), propGC->getKnotsArray()); - st.declareIntArrayConst(props.m_coefsOffsets, 8 * 2, propGC->getCoefsOffsetsArray()); + st.declareIntArrayConst(props.m_coefsOffsets, numOffsets, propGC->getCoefsOffsetsArray()); st.declareFloatArrayConst(props.m_coefs, propGC->getNumCoefs(), propGC->getCoefsArray()); } // Both the forward and inverse hue curve eval need the forward spline eval, so always add that. - AddCurveFunctionName(shaderCreator, st, props.m_eval); + AddCurveFunctionName(shaderCreator, st, props.m_eval, true); st.newLine() << "{"; st.indent(); @@ -265,7 +281,7 @@ void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator if (gcData->getDirection() == TRANSFORM_DIR_INVERSE) { // Add inverse curve eval. - AddCurveFunctionName(shaderCreator, st, props.m_evalRev); + AddCurveFunctionName(shaderCreator, st, props.m_evalRev, false); st.newLine() << "{"; st.indent(); @@ -275,7 +291,7 @@ void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator st.newLine() << "}"; // Add inverse hue curve eval. - AddCurveFunctionName(shaderCreator, st, props.m_evalRevHue); + AddCurveFunctionName(shaderCreator, st, props.m_evalRevHue, false); st.newLine() << "{"; st.indent(); @@ -294,11 +310,12 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, const GCProperties & props, bool dyn, bool doLinToLog, + bool doRGBToHSY, bool drawCurveOnly, GradingStyle style) { const std::string pix(shaderCreator->getPixelName()); - if(drawCurveOnly) + if (drawCurveOnly) { // Note that this is not within the localBypass if-statement since outColor // needs to get processed even if isDefault is true for all curves since @@ -317,27 +334,28 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, } // Add the conversion from RGB to HSY. + if (doRGBToHSY) { - FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; - switch(style) - { + FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + switch (style) + { case GRADING_LIN: - hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; - break; + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + break; case GRADING_LOG: - hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LOG; - break; + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LOG; + break; case GRADING_VIDEO: - hsyStyle = FixedFunctionOpData::RGB_TO_HSY_VID; - break; - } - - st.newLine() << "{"; // establish scope so local variable names won't conflict - st.indent(); - ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); - GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); - st.dedent(); - st.newLine() << "}"; + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_VID; + break; + } + + st.newLine() << "{"; // establish scope so local variable names won't conflict + st.indent(); + ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); + GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); + st.dedent(); + st.newLine() << "}"; } // Lin to Log (on Luma only). @@ -399,26 +417,27 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, st.newLine() << pix << ".r = " << pix << ".r + " << props.m_eval << "(7, " << pix << ".r, 0.);"; // Add the conversion from HSY to RGB. + if (doRGBToHSY) { - FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; - switch(style) - { + FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + switch (style) + { case GRADING_LIN: - hsyStyle = FixedFunctionOpData::HSY_LIN_TO_RGB; - break; + hsyStyle = FixedFunctionOpData::HSY_LIN_TO_RGB; + break; case GRADING_LOG: - hsyStyle = FixedFunctionOpData::HSY_LOG_TO_RGB; - break; + hsyStyle = FixedFunctionOpData::HSY_LOG_TO_RGB; + break; case GRADING_VIDEO: - hsyStyle = FixedFunctionOpData::HSY_VID_TO_RGB; - break; - } - st.newLine() << "{"; - st.indent(); - ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); - GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); - st.dedent(); - st.newLine() << "}"; + hsyStyle = FixedFunctionOpData::HSY_VID_TO_RGB; + break; + } + st.newLine() << "{"; + st.indent(); + ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); + GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); + st.dedent(); + st.newLine() << "}"; } if (dyn) @@ -433,12 +452,13 @@ void AddGCInverseShader(GpuShaderCreatorRcPtr & shaderCreator, const GCProperties & props, bool dyn, bool doLinToLog, + bool doRGBToHSY, bool drawCurveOnly, GradingStyle style) { const std::string pix(shaderCreator->getPixelName()); - if(drawCurveOnly) + if (drawCurveOnly) { // Note that this is not within the localBypass if-statement since outColor // needs to get processed even if isDefault is true for all curves since @@ -449,44 +469,45 @@ void AddGCInverseShader(GpuShaderCreatorRcPtr & shaderCreator, return; } - if (dyn) - { - st.newLine() << "if (!" << props.m_localBypass << ")"; - st.newLine() << "{"; - st.indent(); - } + if (dyn) + { + st.newLine() << "if (!" << props.m_localBypass << ")"; + st.newLine() << "{"; + st.indent(); + } // Add the conversion from RGB to HSY. + if (doRGBToHSY) { - FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; - switch(style) - { + FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + switch (style) + { case GRADING_LIN: - hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; - break; + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + break; case GRADING_LOG: - hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LOG; - break; + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LOG; + break; case GRADING_VIDEO: - hsyStyle = FixedFunctionOpData::RGB_TO_HSY_VID; - break; - } - - st.newLine() << "{"; // establish scope so local variable names won't conflict - st.indent(); - ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); - GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); - st.dedent(); - st.newLine() << "}"; + hsyStyle = FixedFunctionOpData::RGB_TO_HSY_VID; + break; + } + + st.newLine() << "{"; // establish scope so local variable names won't conflict + st.indent(); + ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); + GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); + st.dedent(); + st.newLine() << "}"; } // Apply the hue curves inverse. // Invert HUE-FX. - st.newLine() << pix << ".r = " << props.m_evalRevHue << "(7, " << pix << ".r, 0.);"; + st.newLine() << pix << ".r = " << props.m_evalRevHue << "(7, " << pix << ".r);"; // Invert HUE-HUE. - st.newLine() << pix << ".r = " << props.m_evalRevHue << "(0, " << pix << ".r, " << pix << ".r);"; + st.newLine() << pix << ".r = " << props.m_evalRevHue << "(0, " << pix << ".r);"; st.newLine() << ""; // Use the inverted hue to calculate the HUE-SAT & HUE-LUM gains. @@ -522,7 +543,7 @@ void AddGCInverseShader(GpuShaderCreatorRcPtr & shaderCreator, } // Invert LUM-LUM. - st.newLine() << pix << ".b = " << props.m_evalRev << "(5, " << pix << ".b, " << pix << ".b);"; + st.newLine() << pix << ".b = " << props.m_evalRev << "(5, " << pix << ".b);"; st.newLine() << ""; // Use it to calc the LUM-SAT gain. @@ -540,29 +561,30 @@ void AddGCInverseShader(GpuShaderCreatorRcPtr & shaderCreator, st.newLine() << "" << pix << ".g = " << pix << ".g / satGain;"; // Invert SAT-SAT. - st.newLine() << "" << pix << ".g = max(0., " << props.m_evalRev << "(4, " << pix << ".g, " << pix << ".g));"; + st.newLine() << "" << pix << ".g = max(0., " << props.m_evalRev << "(4, " << pix << ".g));"; // Add the conversion from HSY to RGB. + if (doRGBToHSY) { - FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; - switch(style) - { + FixedFunctionOpData::Style hsyStyle = FixedFunctionOpData::RGB_TO_HSY_LIN; + switch (style) + { case GRADING_LIN: - hsyStyle = FixedFunctionOpData::HSY_LIN_TO_RGB; - break; + hsyStyle = FixedFunctionOpData::HSY_LIN_TO_RGB; + break; case GRADING_LOG: - hsyStyle = FixedFunctionOpData::HSY_LOG_TO_RGB; - break; + hsyStyle = FixedFunctionOpData::HSY_LOG_TO_RGB; + break; case GRADING_VIDEO: - hsyStyle = FixedFunctionOpData::HSY_VID_TO_RGB; - break; - } - st.newLine() << "{"; - st.indent(); - ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); - GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); - st.dedent(); - st.newLine() << "}"; + hsyStyle = FixedFunctionOpData::HSY_VID_TO_RGB; + break; + } + st.newLine() << "{"; + st.indent(); + ConstFixedFunctionOpDataRcPtr funcOpData = std::make_shared(hsyStyle); + GetFixedFunctionGPUProcessingText(shaderCreator, st, funcOpData); + st.dedent(); + st.newLine() << "}"; } if (dyn) @@ -604,7 +626,7 @@ void GetGradingHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, st.indent(); st.newLine() << ""; - st.newLine() << "// Add GradingHueCurve '" + st.newLine() << "// Add GradingHueCurve " << TransformDirectionToString(dir) << " processing"; st.newLine() << ""; st.newLine() << "{"; @@ -613,13 +635,14 @@ void GetGradingHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, GCProperties properties; SetGCProperties(shaderCreator, dyn, properties); + auto dynProp = gcData->getDynamicPropertyInternal(); + if (dyn) { // Add the dynamic property to the shader creator. - auto prop = gcData->getDynamicPropertyInternal(); // Property is decoupled. - auto shaderProp = prop->createEditableCopy(); + auto shaderProp = dynProp->createEditableCopy(); DynamicPropertyRcPtr newProp = shaderProp; shaderCreator->addDynamicProperty(newProp); @@ -635,14 +658,17 @@ void GetGradingHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, AddCurveEvalMethodTextToShaderProgram(shaderCreator, gcData, properties, dyn); } - const bool doLinToLog = (style == GRADING_LIN) && !gcData->getBypassLinToLog(); + const bool doLinToLog = style == GRADING_LIN; + const bool doRGBToHSY = !gcData->getBypassRGBToHSY(); switch (dir) { case TRANSFORM_DIR_FORWARD: - AddGCForwardShader(shaderCreator, st, properties, dyn, doLinToLog, gcData->getDrawCurveOnly(), style); + AddGCForwardShader(shaderCreator, st, properties, dyn, doLinToLog, doRGBToHSY, + dynProp->getValue()->getDrawCurveOnly(), style); break; case TRANSFORM_DIR_INVERSE: - AddGCInverseShader(shaderCreator, st, properties, dyn, doLinToLog, gcData->getDrawCurveOnly(), style); + AddGCInverseShader(shaderCreator, st, properties, dyn, doLinToLog, doRGBToHSY, + dynProp->getValue()->getDrawCurveOnly(), style); break; } diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index f7cb46c580..b8350278fb 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -801,46 +801,71 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForRGBCurve(KnotsCoefs & knots //------------------------------------------------------------------------------------------------ // -void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knotsCoefs, int curveIdx) const +void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knotsCoefs, + int curveIdx, + bool drawCurveOnly) const { // Return 0 knots and coefficients when the curve is identity. if (m_controlPoints.size() < 2 || isIdentity()) { - // Set knots and coefficients that represent identity curve. - const int numKnots = static_cast(knotsCoefs.m_numKnots); - const int numCoefs = static_cast(knotsCoefs.m_numCoefs); + if (!drawCurveOnly) + { + // In this case, do not add any knots or coefs. This will allow localBypass + // to be true if all the curves are identities. + + // Identity curve: offset is -1 and count is 0. + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = -1; + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = 0; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = -1; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = 0; + return; + } + else + { + // DrawCurveOnly is set when drawing the splines for a UI. In this mode, the + // spline is always set on the HueSat curve and the HueCurve eval only computes + // that one curve. But the curve/spline type are not known, so the polynomial + // must be set so that it returns the correct values, even if it is an identity. + // Note that the value returned for an identity varies among the spline types. - const int N_IDENTITY_KNOTS = 2; - const int N_IDENTITY_COEFS = 3; + // Set knots and coefficients that represent an identity curve. - knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = numKnots; - knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = N_IDENTITY_KNOTS; - knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = knotsCoefs.m_numCoefs; - knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = N_IDENTITY_COEFS; + const int numKnots = static_cast(knotsCoefs.m_numKnots); + const int numCoefs = static_cast(knotsCoefs.m_numCoefs); - knotsCoefs.m_knotsArray.begin()[numKnots] = 0.f; - knotsCoefs.m_knotsArray.begin()[numKnots + 1] = 1.f; + const int N_IDENTITY_KNOTS = 2; + const int N_IDENTITY_COEFS = 3; - // Identity curve are linear or constant, so set the quadratic coefficient to zero. - knotsCoefs.m_coefsArray.begin()[numCoefs] = 0.f; + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2] = numKnots; + knotsCoefs.m_knotsOffsetsArray[curveIdx * 2 + 1] = N_IDENTITY_KNOTS; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2] = knotsCoefs.m_numCoefs; + knotsCoefs.m_coefsOffsetsArray[curveIdx * 2 + 1] = N_IDENTITY_COEFS; - // Set the linear coefficient to match the slope of the identity curve. - const float linearCoef = m_splineType == BSplineType::DIAGONAL_B_SPLINE || - m_splineType == BSplineType::HUE_HUE_B_SPLINE ? - 1.0f : 0.f; + knotsCoefs.m_knotsArray.begin()[numKnots] = 0.f; + knotsCoefs.m_knotsArray.begin()[numKnots + 1] = 1.f; - knotsCoefs.m_coefsArray.begin()[numCoefs + 1] = linearCoef; + // Identity curve are linear or constant, so set the quadratic coefficient to zero. + knotsCoefs.m_coefsArray.begin()[numCoefs] = 0.f; - // Set the constant coefficient for an identity curve. - const float constantCoef = m_splineType == BSplineType::PERIODIC_1_B_SPLINE || - m_splineType == BSplineType::HORIZONTAL1_B_SPLINE ? - 1.0f : 0.f; + // Set the linear coefficient to match the slope of the identity curve. + const float linearCoef = m_splineType == BSplineType::DIAGONAL_B_SPLINE || + m_splineType == BSplineType::HUE_HUE_B_SPLINE ? + 1.0f : 0.f; - knotsCoefs.m_coefsArray.begin()[numCoefs + 2] = constantCoef; + knotsCoefs.m_coefsArray.begin()[numCoefs + 1] = linearCoef; - knotsCoefs.m_numKnots += N_IDENTITY_KNOTS; - knotsCoefs.m_numCoefs += N_IDENTITY_COEFS; - return; + // Set the constant coefficient for an identity curve. + const float constantCoef = m_splineType == BSplineType::PERIODIC_1_B_SPLINE || + m_splineType == BSplineType::HORIZONTAL1_B_SPLINE ? + 1.0f : 0.f; + + knotsCoefs.m_coefsArray.begin()[numCoefs + 2] = constantCoef; + + knotsCoefs.m_numKnots += N_IDENTITY_KNOTS; + knotsCoefs.m_numCoefs += N_IDENTITY_COEFS; + + return; + } } bool isPeriodic = false; @@ -920,7 +945,7 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots } -void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int curveIdx) const +void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int curveIdx, bool drawCurveOnly) const { if(m_splineType == BSplineType::B_SPLINE) { @@ -928,7 +953,7 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int } else { - computeKnotsAndCoefsForHueCurve(knotsCoefs, curveIdx); + computeKnotsAndCoefsForHueCurve(knotsCoefs, curveIdx, drawCurveOnly); } } @@ -1005,7 +1030,6 @@ void GradingBSplineCurveImpl::AddShaderEvalRev(GpuShaderText & st, // The input arguments are: // curveIdx -- The index of the curve being evaluated. // x -- The input value. - // identity_x -- The desired output if there is no curve to evaluate. st.newLine() << "int knotsOffs = " << knotsOffsets << "[curveIdx * 2];"; st.newLine() << "int knotsCnt = " << knotsOffsets << "[curveIdx * 2 + 1];"; @@ -1015,7 +1039,7 @@ void GradingBSplineCurveImpl::AddShaderEvalRev(GpuShaderText & st, st.newLine() << "if (coefsSets == 0)"; st.newLine() << "{"; - st.newLine() << " return identity_x;"; + st.newLine() << " return x;"; st.newLine() << "}"; st.newLine() << "float knStart = " << knots << "[knotsOffs];"; @@ -1079,7 +1103,6 @@ void GradingBSplineCurveImpl::AddShaderEvalRevHue(GpuShaderText & st, // The input arguments are: // curveIdx -- The index of the curve being evaluated. // x -- The input value. - // identity_x -- The desired output if there is no curve to evaluate. st.newLine() << "int knotsOffs = " << knotsOffsets << "[curveIdx * 2];"; st.newLine() << "int knotsCnt = " << knotsOffsets << "[curveIdx * 2 + 1];"; @@ -1089,7 +1112,7 @@ void GradingBSplineCurveImpl::AddShaderEvalRevHue(GpuShaderText & st, st.newLine() << "if (coefsSets == 0)"; st.newLine() << "{"; - st.newLine() << " return identity_x;"; + st.newLine() << " return x;"; st.newLine() << "}"; st.newLine() << "float knStart = " << knots << "[knotsOffs];"; @@ -1151,8 +1174,7 @@ GradingBSplineCurveImpl::KnotsCoefs::KnotsCoefs(size_t numCurves) m_knotsArray.resize(DynamicPropertyGradingRGBCurveImpl::GetMaxKnots()); } -// TODO: Update to return hue curve identity value. -float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x) const +float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x, float identity_x) const { // NB: When evaluating hue curves, x should be wrapped to [0,1) by the caller // so there is no extrapolation. @@ -1160,7 +1182,7 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x) const const int coefsSets = m_coefsOffsetsArray[2 * c + 1] / 3; if (coefsSets == 0) { - return x; + return identity_x; } const int coefsOffs = m_coefsOffsetsArray[2 * c]; const int knotsCnt = m_knotsOffsetsArray[2 * c + 1]; @@ -1203,7 +1225,6 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x) const } } -// TODO: Update to return hue curve identity value. float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const { // Note: This is only intended to invert the monotonic curve types. @@ -1270,7 +1291,7 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const } } -float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRevHue(int c, float y, bool isHfx) const +float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRevHue(int c, float y) const { // This function is specifically to invert the HueFX and Hue-Hue curve types. // @@ -1284,6 +1305,7 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRevHue(int c, float y, bool { return y; } + const int coefsOffs = m_coefsOffsetsArray[2 * c]; const int knotsCnt = m_knotsOffsetsArray[2 * c + 1]; const int knotsOffs = m_knotsOffsetsArray[2 * c]; @@ -1291,6 +1313,7 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRevHue(int c, float y, bool const float knStart = m_knotsArray[knotsOffs]; const float knEnd = m_knotsArray[knotsOffs + knotsCnt - 1]; float knStartY = m_coefsArray[coefsOffs + coefsSets * 2]; + const bool isHfx = c == 7; knStartY = isHfx ? knStartY + knStart : knStartY; float knEndY; { diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h index edd36392be..f990f67886 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.h @@ -111,27 +111,33 @@ class GradingBSplineCurveImpl : public GradingBSplineCurve int m_numCoefs = 0; int m_numKnots = 0; - float evalCurve(int curveIdx, float x) const; - float evalCurveRev(int curveIdx, float x) const; - float evalCurveRevHue(int c, float y, bool isHfx) const; + // Forward evaluation of any spline type. + float evalCurve(int curveIdx, float x, float identity_x) const; + // Reverse evaluation of B_SPLINE or DIAGONAL_B_SPLINE. + float evalCurveRev(int curveIdx, float y) const; + // Reverse evaluation of HUE_HUE_B_SPLINE or HUE_FX curves using PERIODIC_0_B_SPLINE. + float evalCurveRevHue(int c, float y) const; }; // Compute knots and coefs for a curve and add result to knotsCoefs. It has to be called for // each curve using a given curve order. - void computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int curveIdx) const; + void computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int curveIdx, bool drawCurveOnly) const; + // Forward evaluation of any spline type. static void AddShaderEvalFwd(GpuShaderText & st, const std::string & knotsOffsets, const std::string & coefsOffsets, const std::string & knots, const std::string & coefs); + // Reverse evaluation of B_SPLINE or DIAGONAL_B_SPLINE. static void AddShaderEvalRev(GpuShaderText & st, const std::string & knotsOffsets, const std::string & coefsOffsets, const std::string & knots, const std::string & coefs); + // Reverse evaluation of HUE_HUE_B_SPLINE or HUE_FX curves using PERIODIC_0_B_SPLINE. static void AddShaderEvalRevHue(GpuShaderText & st, const std::string & knotsOffsets, const std::string & coefsOffsets, const std::string & knots, const std::string & coefs); private: void computeKnotsAndCoefsForRGBCurve(KnotsCoefs & knotsCoefs, int curveIdx) const; - void computeKnotsAndCoefsForHueCurve(KnotsCoefs & knotsCoefs, int curveIdx) const; + void computeKnotsAndCoefsForHueCurve(KnotsCoefs & knotsCoefs, int curveIdx, bool drawCurveOnly) const; void validateIndex(size_t index) const; diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpCPU.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpCPU.cpp index bf8c73a4b9..914a9881a0 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpCPU.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpCPU.cpp @@ -33,13 +33,13 @@ class GradingRGBCurveOpCPU : public OpCPU void eval(const GradingBSplineCurveImpl::KnotsCoefs & knotsCoefs, float * out, const float * in) const { - out[0] = knotsCoefs.evalCurve(static_cast(RGB_RED), in[0]); - out[1] = knotsCoefs.evalCurve(static_cast(RGB_GREEN), in[1]); - out[2] = knotsCoefs.evalCurve(static_cast(RGB_BLUE), in[2]); + out[0] = knotsCoefs.evalCurve(static_cast(RGB_RED), in[0], in[0]); + out[1] = knotsCoefs.evalCurve(static_cast(RGB_GREEN), in[1], in[1]); + out[2] = knotsCoefs.evalCurve(static_cast(RGB_BLUE), in[2], in[2]); // TODO: Add vectorized version for master curve. - out[0] = knotsCoefs.evalCurve(static_cast(RGB_MASTER), out[0]); - out[1] = knotsCoefs.evalCurve(static_cast(RGB_MASTER), out[1]); - out[2] = knotsCoefs.evalCurve(static_cast(RGB_MASTER), out[2]); + out[0] = knotsCoefs.evalCurve(static_cast(RGB_MASTER), out[0], out[0]); + out[1] = knotsCoefs.evalCurve(static_cast(RGB_MASTER), out[1], out[1]); + out[2] = knotsCoefs.evalCurve(static_cast(RGB_MASTER), out[2], out[2]); } void evalRev(const GradingBSplineCurveImpl::KnotsCoefs & knotsCoefs, float * out, const float * in) const diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp index c7f382fd7c..a28996d87e 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp @@ -217,12 +217,13 @@ void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator if (!dyn) { auto propGC = gcData->getDynamicPropertyInternal(); + const int numOffsets = propGC->GetNumOffsetValues(); // 2 ints for each curve. st.newLine() << ""; - st.declareIntArrayConst(props.m_knotsOffsets, 4 * 2, propGC->getKnotsOffsetsArray()); + st.declareIntArrayConst(props.m_knotsOffsets, numOffsets, propGC->getKnotsOffsetsArray()); st.declareFloatArrayConst(props.m_knots, propGC->getNumKnots(), propGC->getKnotsArray()); - st.declareIntArrayConst(props.m_coefsOffsets, 4 * 2, propGC->getCoefsOffsetsArray()); + st.declareIntArrayConst(props.m_coefsOffsets, numOffsets, propGC->getCoefsOffsetsArray()); st.declareFloatArrayConst(props.m_coefs, propGC->getNumCoefs(), propGC->getCoefsArray()); } diff --git a/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp b/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp index 435b56b436..cbc46051bf 100644 --- a/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp +++ b/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp @@ -111,24 +111,14 @@ bool GradingHueCurveTransformImpl::slopesAreDefault(HueCurveType c) const return data().slopesAreDefault(c); } -bool GradingHueCurveTransformImpl::getBypassLinToLog() const noexcept +bool GradingHueCurveTransformImpl::getBypassRGBToHSY() const noexcept { - return data().getBypassLinToLog(); + return data().getBypassRGBToHSY(); } -void GradingHueCurveTransformImpl::setBypassLinToLog(bool bypass) noexcept +void GradingHueCurveTransformImpl::setBypassRGBToHSY(bool bypass) noexcept { - data().setBypassLinToLog(bypass); -} - -bool GradingHueCurveTransformImpl::getDrawCurveOnly() const noexcept -{ - return data().getDrawCurveOnly(); -} - -void GradingHueCurveTransformImpl::setDrawCurveOnly( bool drawCurveOnly ) noexcept -{ - data().setDrawCurveOnly( drawCurveOnly ); + data().setBypassRGBToHSY(bypass); } bool GradingHueCurveTransformImpl::isDynamic() const noexcept @@ -152,6 +142,10 @@ std::ostream& operator<< (std::ostream & os, const GradingHueCurveTransform & t) os << "direction=" << TransformDirectionToString(t.getDirection()); os << ", style=" << GradingStyleToString(t.getStyle()); os << ", values=" << *t.getValue(); + if (t.getBypassRGBToHSY()) + { + os << ", bypassRGBToHSY=true"; + } if (t.isDynamic()) { os << ", dynamic"; diff --git a/src/OpenColorIO/transforms/GradingHueCurveTransform.h b/src/OpenColorIO/transforms/GradingHueCurveTransform.h index ee3bf7c465..d499bb38d7 100644 --- a/src/OpenColorIO/transforms/GradingHueCurveTransform.h +++ b/src/OpenColorIO/transforms/GradingHueCurveTransform.h @@ -46,12 +46,9 @@ class GradingHueCurveTransformImpl : public GradingHueCurveTransform void setSlope(HueCurveType c, size_t index, float slope) override; bool slopesAreDefault(HueCurveType c) const override; - bool getBypassLinToLog() const noexcept override; - void setBypassLinToLog(bool bypass) noexcept override; + bool getBypassRGBToHSY() const noexcept override; + void setBypassRGBToHSY(bool bypass) noexcept override; - bool getDrawCurveOnly() const noexcept override; - void setDrawCurveOnly(bool drawCurveOnly) noexcept override; - bool isDynamic() const noexcept override; void makeDynamic() noexcept override; void makeNonDynamic() noexcept override; diff --git a/src/bindings/python/PyGradingData.cpp b/src/bindings/python/PyGradingData.cpp index 690733a422..9e9e0c5beb 100644 --- a/src/bindings/python/PyGradingData.cpp +++ b/src/bindings/python/PyGradingData.cpp @@ -502,6 +502,10 @@ void bindPyGradingData(py::module & m) DOC(GradingHueCurve, validate)) .def("isIdentity", &GradingHueCurve::isIdentity, DOC(GradingHueCurve, isIdentity)) + .def("getDrawCurveOnly", &GradingHueCurve::getDrawCurveOnly, + DOC(GradingHueCurve, getDrawCurveOnly)) + .def("setDrawCurveOnly", &GradingHueCurve::setDrawCurveOnly, "drawcurveonly"_a, + DOC(GradingHueCurve, setDrawCurveOnly)) .def_property("hue_hue", [](const GradingHueCurveRcPtr & hueCurve) diff --git a/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp b/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp index 547c82a760..09adb77701 100644 --- a/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp +++ b/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp @@ -98,14 +98,10 @@ void bindPyGradingHueCurveTransform(py::module & m) // } // }, // DOC(GradingHueCurveTransform, getSlopes)); - .def("getBypassLinToLog", &GradingHueCurveTransform::getBypassLinToLog, - DOC(GradingHueCurveTransform, getBypassLinToLog)) - .def("setBypassLinToLog", &GradingHueCurveTransform::setBypassLinToLog, "bypass"_a, - DOC(GradingHueCurveTransform, setBypassLinToLog)) - .def("getDrawCurveOnly", &GradingHueCurveTransform::getDrawCurveOnly, - DOC(GradingHueCurveTransform, getDrawCurveOnly)) - .def("setDrawCurveOnly", &GradingHueCurveTransform::setDrawCurveOnly, "drawcurveonly"_a, - DOC(GradingHueCurveTransform, setDrawCurveOnly)) + .def("getBypassRGBToHSY", &GradingHueCurveTransform::getBypassRGBToHSY, + DOC(GradingHueCurveTransform, getBypassRGBToHSY)) + .def("setBypassRGBToHSY", &GradingHueCurveTransform::setBypassRGBToHSY, "bypass"_a, + DOC(GradingHueCurveTransform, setBypassRGBToHSY)) .def("isDynamic", &GradingHueCurveTransform::isDynamic, DOC(GradingHueCurveTransform, isDynamic)) .def("makeDynamic", &GradingHueCurveTransform::makeDynamic, diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index fe40f187a2..c1e36579ae 100644 --- a/tests/cpu/Config_tests.cpp +++ b/tests/cpu/Config_tests.cpp @@ -4658,6 +4658,7 @@ OCIO_ADD_TEST(Config, grading_huecurve_serialization) " direction: inverse\n" " - !\n" " style: linear\n" + " rgbtohsy_bypass: true\n" " sat_sat: {control_points: [0, 0, 0.1, 0.2, 0.5, 0.5, 0.7, 0.6, 1, 1.5]}\n" " lum_lum: {control_points: [-1, -1, 0, 0.1, 0.5, 0.6, 1, 1.1]}\n" " - !\n" diff --git a/tests/cpu/DynamicProperty_tests.cpp b/tests/cpu/DynamicProperty_tests.cpp index c603eb7e2b..fc7b3f3687 100644 --- a/tests/cpu/DynamicProperty_tests.cpp +++ b/tests/cpu/DynamicProperty_tests.cpp @@ -452,28 +452,50 @@ OCIO_ADD_TEST(DynamicPropertyImpl, grading_hue_curve_knots_coefs) auto curves = OCIO::GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); - // Fit the polynomials. + { + // Fit the polynomials. + OCIO::DynamicPropertyGradingHueCurveImplRcPtr dp = + std::make_shared(curves, false); + + OCIO_CHECK_EQUAL(46, dp->getNumKnots()); + OCIO_CHECK_EQUAL(129, dp->getNumCoefs()); + + const int * coefsOffsets = dp->getCoefsOffsetsArray(); + const int * knotsOffsets = dp->getKnotsOffsetsArray(); + + // These are offset0, count0, offset1, count1, offset2, count2, ... + const int true_knotsOffsets[] = {0, 15, 15, 19, 34, 12, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0}; + const int true_coefsOffsets[] = {0, 42, 42, 54, 96, 33, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0}; + OCIO_CHECK_EQUAL(16, dp->GetNumOffsetValues()); + for (int i=0; i < dp->GetNumOffsetValues(); i++) + { + OCIO_CHECK_EQUAL(knotsOffsets[i], true_knotsOffsets[i]); + OCIO_CHECK_EQUAL(coefsOffsets[i], true_coefsOffsets[i]); + } + } + + // Repeat the test in DrawCurveOnly mode. This will yield identity knots and coefs for the + // curves that are identities. + + curves->setDrawCurveOnly(true); + OCIO::DynamicPropertyGradingHueCurveImplRcPtr dp = std::make_shared(curves, false); - const int numOffsets = dp->GetNumOffsetValues(); - OCIO_CHECK_EQUAL(16, numOffsets); + OCIO_CHECK_EQUAL(56, dp->getNumKnots()); + OCIO_CHECK_EQUAL(144, dp->getNumCoefs()); const int * coefsOffsets = dp->getCoefsOffsetsArray(); const int * knotsOffsets = dp->getKnotsOffsetsArray(); - // These are offset0, count0, offset1, count1, offset2, count2, ... const int true_knotsOffsets[] = {0, 15, 15, 19, 34, 12, 46, 2, 48, 2, 50, 2, 52, 2, 54, 2}; const int true_coefsOffsets[] = {0, 42, 42, 54, 96, 33, 129, 3, 132, 3, 135, 3, 138, 3, 141, 3}; - for (int i=0; i < numOffsets; i++) + for (int i=0; i < dp->GetNumOffsetValues(); i++) { OCIO_CHECK_EQUAL(knotsOffsets[i], true_knotsOffsets[i]); OCIO_CHECK_EQUAL(coefsOffsets[i], true_coefsOffsets[i]); } - OCIO_CHECK_EQUAL(56, dp->getNumKnots()); - OCIO_CHECK_EQUAL(144, dp->getNumCoefs()); - { // Hue-Hue diff --git a/tests/cpu/fileformats/FileFormatCTF_tests.cpp b/tests/cpu/fileformats/FileFormatCTF_tests.cpp index d104b02b81..538bc6d8b9 100644 --- a/tests/cpu/fileformats/FileFormatCTF_tests.cpp +++ b/tests/cpu/fileformats/FileFormatCTF_tests.cpp @@ -4645,7 +4645,7 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_huecurves_log) OCIO_REQUIRE_ASSERT(gradingCurves0); OCIO_CHECK_EQUAL(gradingCurves0->getStyle(), OCIO::GRADING_LOG); OCIO_CHECK_EQUAL(gradingCurves0->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); - OCIO_CHECK_ASSERT(!gradingCurves0->getBypassLinToLog()); + OCIO_CHECK_ASSERT(!gradingCurves0->getBypassRGBToHSY()); OCIO_CHECK_ASSERT(gradingCurves0->isDynamic()); auto curves = gradingCurves0->getValue(); auto hh = curves->getCurve(OCIO::HUE_HUE); @@ -7335,7 +7335,7 @@ OCIO_ADD_TEST(CTFTransform, grading_huecurve_lin_ctf) // All curves are default curves, no curve is saved. curves = OCIO::GradingHueCurve::Create(OCIO::GRADING_LIN); gradingCurves->setValue(curves); - gradingCurves->setBypassLinToLog(true); + gradingCurves->setBypassRGBToHSY(true); // Make it dynamic so it is not identity. gradingCurves->makeDynamic(); { @@ -7348,7 +7348,7 @@ OCIO_ADD_TEST(CTFTransform, grading_huecurve_lin_ctf) const std::string expected{ R"( - + diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp index 63c3c4a08c..34e212db48 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp @@ -316,3 +316,93 @@ OCIO_ADD_TEST(GradingHueCurveOpCPU, lin_all_curves) OCIO_CHECK_NO_THROW(op->apply(res, res, num_samples)); ValidateImage(input_32f, res, num_samples, __LINE__); } + +OCIO_ADD_TEST(GradingHueCurveOpCPU, draw_curve_only) +{ + auto gc = std::make_shared(OCIO::GRADING_LOG); + + auto val = gc->getValue()->createEditableCopy(); + auto spline = val->getCurve(OCIO::HUE_SAT); + spline->getControlPoint(1) = OCIO::GradingControlPoint(0.15f, 1.4f); + OCIO_CHECK_ASSERT(!val->isIdentity()); + + // Enable drawCurveOnly mode. This should only evaluate the HUE-SAT spline for + // use in a user interface. + val->setDrawCurveOnly(true); + gc->setValue(val); + + OCIO::ConstGradingHueCurveOpDataRcPtr gcc = gc; + OCIO::ConstOpCPURcPtr op; + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + + constexpr long num_samples = 2; + float res[4 * num_samples]{ 0.f }; + + constexpr float input_32f[] = { + -0.2f, 0.15f, 0.15f, 0.0f, + 0.15f, 1.0f, 2.0f, 0.5f }; + + constexpr float expected_32f[] = { + 1.0f, 1.4f, 1.4f, 0.0f, + 1.4f, 1.0f, 1.0f, 0.5f }; + + // Test in forward direction. + + OCIO_CHECK_NO_THROW(op->apply(input_32f, res, num_samples)); + ValidateImage(expected_32f, res, num_samples, __LINE__); + + // Test in inverse direction, which should be the same as the forward direction + // since the direction is ignored for DrawCurveOnly mode. + + gc->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + OCIO_CHECK_NO_THROW(op->apply(input_32f, res, num_samples)); + ValidateImage(expected_32f, res, num_samples, __LINE__); +} + +OCIO_ADD_TEST(GradingHueCurveOpCPU, bypass_rgbtohsy) +{ + auto gc = std::make_shared(OCIO::GRADING_LOG); + + gc->setBypassRGBToHSY(true); + + auto val = gc->getValue()->createEditableCopy(); + auto spline = val->getCurve(OCIO::SAT_SAT); + spline->getControlPoint(1) = OCIO::GradingControlPoint(0.4f, 0.8f); + OCIO_CHECK_ASSERT(!val->isIdentity()); + gc->setValue(val); + + OCIO::ConstGradingHueCurveOpDataRcPtr gcc = gc; + OCIO::ConstOpCPURcPtr op; + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + + constexpr long num_samples = 2; + float res[4 * num_samples]{ 0.f }; + + constexpr float input_32f[] = { + 0.2f, 0.2f, -0.1f, 0.0f, + 0.1f, 0.4f, 2.0f, 0.5f }; + + // Only the green channel gets processed, using the sat-sat curve. + constexpr float expected_32f[] = { + 0.2f, 0.4475418f, -0.1f, 0.0f, + 0.1f, 0.8000000f, 2.0f, 0.5f }; + + // Test in forward direction. + + OCIO_CHECK_NO_THROW(op->apply(input_32f, res, num_samples)); + ValidateImage(expected_32f, res, num_samples, __LINE__); + + // Test in inverse direction. + + gc->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + OCIO_CHECK_NO_THROW(op = OCIO::GetGradingHueCurveCPURenderer(gcc)); + OCIO_CHECK_ASSERT(op); + OCIO_CHECK_NO_THROW(op->apply(res, res, num_samples)); + ValidateImage(input_32f, res, num_samples, __LINE__); +} diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp index ad00ade2ff..41bf26fe76 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp @@ -32,12 +32,12 @@ OCIO_ADD_TEST(GradingHueCurveOpData, accessors) OCIO_CHECK_ASSERT(gc.isIdentity()); OCIO_CHECK_ASSERT(gc.isNoOp()); OCIO_CHECK_ASSERT(gc.hasChannelCrosstalk()); - OCIO_CHECK_ASSERT(!gc.getBypassLinToLog()); + OCIO_CHECK_ASSERT(!gc.getBypassRGBToHSY()); gc.setStyle(OCIO::GRADING_LIN); OCIO_CHECK_EQUAL(gc.getStyle(), OCIO::GRADING_LIN); - gc.setBypassLinToLog(true); - OCIO_CHECK_ASSERT(gc.getBypassLinToLog()); + gc.setBypassRGBToHSY(true); + OCIO_CHECK_ASSERT(gc.getBypassRGBToHSY()); // Get dynamic property as a generic dynamic property and as a typed one and verify they are // the same and can be made dynamic. diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurve_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurve_tests.cpp index 713c63afa9..621846cf6c 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurve_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurve_tests.cpp @@ -60,6 +60,9 @@ OCIO_ADD_TEST(GradingHueCurve, basic) copiedCurve->setSplineType(OCIO::DIAGONAL_B_SPLINE); OCIO_CHECK_THROW_WHAT(hueCurve->validate(), OCIO::Exception, "GradingHueCurve validation failed: 'hue_hue' curve is of the wrong BSplineType."); + // Turn on drawCurveOnly mode and verify that any spline type is now allowed. + hueCurve->setDrawCurveOnly(true); + OCIO_CHECK_NO_THROW(hueCurve->validate()); // Test default curves. auto hueCurveLin = OCIO::GradingHueCurve::Create(OCIO::GRADING_LIN); @@ -101,6 +104,10 @@ OCIO_ADD_TEST(GradingHueCurve, basic) OCIO_CHECK_EQUAL(7.f, hueCurveLin->getCurve(OCIO::LUM_LUM)->getControlPoint(2).m_x); OCIO_CHECK_EQUAL(7.f, hueCurveLin->getCurve(OCIO::LUM_LUM)->getControlPoint(2).m_y); + OCIO_CHECK_ASSERT(!hueCurveLin->getDrawCurveOnly()); + hueCurveLin->setDrawCurveOnly(true); + OCIO_CHECK_ASSERT(hueCurveLin->getDrawCurveOnly()); + // Validate that the other Create function made copies of its arguments. auto hueCurveLinCopy = OCIO::GradingHueCurve::Create(hueCurveLin); OCIO_REQUIRE_ASSERT(hueCurveLinCopy); @@ -108,6 +115,8 @@ OCIO_ADD_TEST(GradingHueCurve, basic) // Use overloaded op== to compare the contents of the curves. OCIO_CHECK_ASSERT(*hueCurveLin == *hueCurveLinCopy); + OCIO_CHECK_ASSERT(hueCurveLinCopy->getDrawCurveOnly()); + // Test createEditableCopy. hueCurveLinCopy = hueCurveLin->createEditableCopy(); OCIO_REQUIRE_ASSERT(hueCurveLinCopy); @@ -163,7 +172,7 @@ OCIO_ADD_TEST(GradingHueCurve, max_ctrl_pnts) { // Use non const curve accessor to modify the curves. OCIO::GradingBSplineCurveRcPtr spline = hueCurve->getCurve(static_cast(c)); - spline->setNumControlPoints(26); + spline->setNumControlPoints(28); } OCIO::DynamicPropertyGradingHueCurveImplRcPtr res; diff --git a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp index 2168798eb5..a089a42c57 100644 --- a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp +++ b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp @@ -19,7 +19,7 @@ OCIO_ADD_TEST(GradingHueCurveTransform, basic) OCIO_CHECK_EQUAL(gctLin->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); gctLin->setDirection(OCIO::TRANSFORM_DIR_INVERSE); OCIO_CHECK_EQUAL(gctLin->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); - OCIO_CHECK_ASSERT(!gctLin->getBypassLinToLog()); + OCIO_CHECK_ASSERT(!gctLin->getBypassRGBToHSY()); OCIO_CHECK_ASSERT(!gctLin->isDynamic()); auto crv = gctLin->getValue()->getCurve(OCIO::LUM_SAT); OCIO_CHECK_EQUAL(crv->getNumControlPoints(), 3); @@ -34,7 +34,7 @@ OCIO_ADD_TEST(GradingHueCurveTransform, basic) auto gctLog = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); OCIO_CHECK_EQUAL(gctLog->getStyle(), OCIO::GRADING_LOG); OCIO_CHECK_EQUAL(gctLog->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); - OCIO_CHECK_ASSERT(!gctLog->getBypassLinToLog()); + OCIO_CHECK_ASSERT(!gctLog->getBypassRGBToHSY()); OCIO_CHECK_ASSERT(!gctLog->isDynamic()); crv = gctLog->getValue()->getCurve(OCIO::LUM_SAT); OCIO_CHECK_EQUAL(crv->getNumControlPoints(), 3); @@ -50,7 +50,7 @@ OCIO_ADD_TEST(GradingHueCurveTransform, basic) auto gctVid = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_VIDEO); OCIO_CHECK_EQUAL(gctVid->getStyle(), OCIO::GRADING_VIDEO); OCIO_CHECK_EQUAL(gctVid->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); - OCIO_CHECK_ASSERT(!gctVid->getBypassLinToLog()); + OCIO_CHECK_ASSERT(!gctVid->getBypassRGBToHSY()); OCIO_CHECK_ASSERT(!gctVid->isDynamic()); crv = gctVid->getValue()->getCurve(OCIO::LUM_SAT); OCIO_CHECK_EQUAL(crv->getNumControlPoints(), 3); @@ -70,8 +70,8 @@ OCIO_ADD_TEST(GradingHueCurveTransform, basic) OCIO_CHECK_EQUAL(gct->getStyle(), OCIO::GRADING_LIN); gct->setDirection(OCIO::TRANSFORM_DIR_INVERSE); OCIO_CHECK_EQUAL(gct->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); - gct->setBypassLinToLog(true); - OCIO_CHECK_ASSERT(gct->getBypassLinToLog()); + gct->setBypassRGBToHSY(true); + OCIO_CHECK_ASSERT(gct->getBypassRGBToHSY()); gct->makeDynamic(); OCIO_CHECK_ASSERT(gct->isDynamic()); gct->setValue(gctLin->getValue()); @@ -289,15 +289,10 @@ OCIO_ADD_TEST(GradingHueCurveTransform, serialization) OCIO_ADD_TEST(GradingHueCurveTransform, local_bypass) { - // Test that the GPU is empty for an identity transform. + // Test that the GPU shader is empty for an identity transform. OCIO::GradingHueCurveTransformRcPtr transform = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); - //std::cout << "local_bypass HOMEMADE\n"; -// OCIO_CHECK_ASSERT(transform.get()); -// -// OCIO::TransformRcPtr transformBypass = transform->createEditableCopy(); -// OCIO_CHECK_ASSERT(transformBypass.get()); OCIO_CHECK_NO_THROW(transform->validate()); diff --git a/tests/gpu/GPUUnitTest.cpp b/tests/gpu/GPUUnitTest.cpp index c85ff89af3..6508131218 100644 --- a/tests/gpu/GPUUnitTest.cpp +++ b/tests/gpu/GPUUnitTest.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -437,24 +436,34 @@ namespace size_t idxDiff = invalidIndex; size_t idxNan = invalidIndex; size_t idxInf = invalidIndex; + constexpr float huge = std::numeric_limits::max(); + float minVals[4] = {huge, huge, huge, huge}; + float maxVals[4] = {-huge, -huge, -huge, -huge}; const bool relativeTest = test->getRelativeComparison(); for(size_t idx=0; idx<(width*height); ++idx) { - DiffComponent(cpuImage, gpuImage, 4 * idx + 0, relativeTest, expectMinValue, - diff, idxDiff, idxInf, idxNan); - DiffComponent(cpuImage, gpuImage, 4 * idx + 1, relativeTest, expectMinValue, - diff, idxDiff, idxInf, idxNan); - DiffComponent(cpuImage, gpuImage, 4 * idx + 2, relativeTest, expectMinValue, - diff, idxDiff, idxInf, idxNan); - DiffComponent(cpuImage, gpuImage, 4 * idx + 3, relativeTest, expectMinValue, - diff, idxDiff, idxInf, idxNan); + for(size_t chan=0; chan<4; ++chan) + { + DiffComponent(cpuImage, gpuImage, 4 * idx + chan, relativeTest, expectMinValue, + diff, idxDiff, idxInf, idxNan); + minVals[chan] = std::min(minVals[chan], + std::isinf(gpuImage[4 * idx + chan]) ? huge: gpuImage[4 * idx + chan]); + maxVals[chan] = std::max(maxVals[chan], + std::isinf(gpuImage[4 * idx + chan]) ? -huge: gpuImage[4 * idx + chan]); + } } size_t componentIdx = idxDiff % 4; size_t pixelIdx = idxDiff / 4; - if (diff > epsilon || idxInf != invalidIndex || idxNan != invalidIndex) + if (diff > epsilon || idxInf != invalidIndex || idxNan != invalidIndex || test->isPrintMinMax()) { std::stringstream err; + err << std::setprecision(10); + err << "\n\nGPU max vals = {" + << maxVals[0] << ", " << maxVals[1] << ", " << maxVals[2] << ", " << maxVals[3] << "}\n" + << "GPU min vals = {" + << minVals[0] << ", " << minVals[1] << ", " << minVals[2] << ", " << minVals[3] << "}\n"; + err << std::setprecision(10) << "\nMaximum error: " << diff << " at pixel: " << pixelIdx << " on component " << componentIdx; diff --git a/tests/gpu/GPUUnitTest.h b/tests/gpu/GPUUnitTest.h index 967325c943..59d07183d2 100644 --- a/tests/gpu/GPUUnitTest.h +++ b/tests/gpu/GPUUnitTest.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "CPUInfoConfig.h" @@ -90,10 +91,15 @@ class OCIOGPUTest inline float getExpectedMinimalValue() const { return m_expectedMinimalValue; } inline void setExpectedMinimalValue(float minValue) { m_expectedMinimalValue = minValue; } - // Dump or not the gpu shader program + // Dump or not the gpu shader program. inline void setVerbose(bool verbose) { m_verbose = verbose; } inline bool isVerbose() const { return m_verbose; } + // Fail the test and print the min and max values of the GPU output, excluding infinities. + // This is useful during development for assessing the range of values being tested. + inline void setPrintMinMax(bool print) { m_printMinMax = print; } + inline bool isPrintMinMax() const { return m_printMinMax; } + inline void setLegacyShader(bool legacy) { m_legacyShader = legacy; } inline bool isLegacyShader() const { return m_legacyShader; } @@ -159,6 +165,7 @@ class OCIOGPUTest bool m_testInfinity{ true }; bool m_performRelativeComparison{ false }; bool m_verbose{ false }; + bool m_printMinMax{ false }; bool m_enabled{ true }; bool m_legacyShader{ false }; unsigned m_legacyShaderLutEdge{ 32 }; diff --git a/tests/gpu/GradingHueCurveOp_test.cpp b/tests/gpu/GradingHueCurveOp_test.cpp index 28793d1b81..8a4f9ea548 100644 --- a/tests/gpu/GradingHueCurveOp_test.cpp +++ b/tests/gpu/GradingHueCurveOp_test.cpp @@ -65,9 +65,10 @@ void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool d test.setProcessor(hc); // Set up a grid of RGBA custom values. - const int lut_size = 17; + const int lut_size = 21; OCIOGPUTest::CustomValues values; - GenerateIdentityLut3D(values, lut_size, -0.1f, 1.5f); + // Choose values so that there is a grid point at 0. + GenerateIdentityLut3D(values, lut_size, -0.075f, 1.425f); test.setCustomValues(values); test.setErrorThreshold(2e-5f); @@ -105,38 +106,42 @@ void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) {0.5f, 0.8f}, {0.8f, 0.5f} }, OCIO::HUE_LUM); auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, OCIO::SAT_SAT); - auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, - OCIO::SAT_LUM); auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); // Adjust these two, relative to previous test, to work in f-stops. - auto ls = OCIO::GradingBSplineCurve::Create({ {-6.f, 0.9f}, {-3.f, 0.95f}, {0.f, 1.2f}, - {2.f, 1.f}, {4.f, 0.6f}, {6.f, 0.55f} }, OCIO::LUM_SAT); auto ll = OCIO::GradingBSplineCurve::Create({ {-8.f, -7.f}, {-2.f, -3.f}, {2.f, 3.5f}, {8.f, 7.f} }, OCIO::LUM_LUM); + auto ls = OCIO::GradingBSplineCurve::Create({ {-6.f, 0.9f}, {-3.f, 0.95f}, {0.f, 1.1f}, + {2.f, 1.f}, {4.f, 0.6f}, {6.f, 0.55f} }, OCIO::LUM_SAT); + // Adjusted this one, relative to above, to avoid some artifacts due to high sat boost. + auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.05f}, {1.f, 1.1f} }, + OCIO::SAT_LUM); auto curve = OCIO::GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); auto hc = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LIN); if(!hc.get()) { - throw OCIO::Exception("Cannot create GradingHueCurveTransform."); + throw OCIO::Exception("Cannot create GradingHueCurveTransform."); } hc->setValue(curve); hc->setDirection(dir); if (dynamic) { - hc->makeDynamic(); + hc->makeDynamic(); } test.setProcessor(hc); // Set up a grid of RGBA custom values. - const int lut_size = 17; + const int lut_size = 21; OCIOGPUTest::CustomValues values; - GenerateIdentityLut3D(values, lut_size, -0.1f, 1.5f); + // Choose values so that there is a grid point at 0. + GenerateIdentityLut3D(values, lut_size, -0.075f, 1.425f); test.setCustomValues(values); - test.setErrorThreshold(2e-4f); + // This test produces some large linear values due to the sat boost, needs a large tolerance. + // Metal is worse than GLSL. + test.setErrorThreshold(4e-4f); test.setExpectedMinimalValue(1.0f); test.setRelativeComparison(true); } @@ -160,3 +165,93 @@ OCIO_ADD_GPU_TEST(GradingHueCurve, style_lin_rev_dynamic) { HueCurveLin(test, OCIO::TRANSFORM_DIR_INVERSE, true); } + +void DrawCurve(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +{ + OCIO::GradingHueCurveTransformRcPtr hc = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LIN); + OCIO::GradingHueCurveRcPtr hueCurve = hc->getValue()->createEditableCopy(); + OCIO::GradingBSplineCurveRcPtr huesat = hueCurve->getCurve(OCIO::HUE_SAT); + + // Enable drawCurveOnly mode. This should only evaluate the HUE-SAT spline for use + // in a user interface. + hueCurve->setDrawCurveOnly(true); + + huesat->setSplineType( OCIO::DIAGONAL_B_SPLINE ); + + huesat->setNumControlPoints(3); + huesat->getControlPoint(0) = OCIO::GradingControlPoint(0.0f, 0.0f); + huesat->getControlPoint(1) = OCIO::GradingControlPoint(0.5f, 0.7f); + huesat->getControlPoint(2) = OCIO::GradingControlPoint(1.0f, 1.0f); + + hc->setValue(hueCurve); + hc->setDirection(dir); + if (dynamic) + { + hc->makeDynamic(); + } + + test.setProcessor(hc); + + test.setErrorThreshold(1e-5f); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, draw_lin_fwd) +{ + DrawCurve(test, OCIO::TRANSFORM_DIR_FORWARD, false); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, draw_lin_fwd_dynamic) +{ + DrawCurve(test, OCIO::TRANSFORM_DIR_FORWARD, true); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, draw_lin_rev) +{ + DrawCurve(test, OCIO::TRANSFORM_DIR_INVERSE, false); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, draw_lin_rev_dynamic) +{ + DrawCurve(test, OCIO::TRANSFORM_DIR_INVERSE, true); +} + +void BypassRGBtoHSY(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) +{ + OCIO::GradingHueCurveTransformRcPtr hc = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LIN); + OCIO::GradingHueCurveRcPtr hueCurve = hc->getValue()->createEditableCopy(); + OCIO::GradingBSplineCurveRcPtr satsat = hueCurve->getCurve(OCIO::SAT_SAT); + satsat->getControlPoint(1) = OCIO::GradingControlPoint(0.4f, 0.6f); + + hc->setBypassRGBToHSY(true); + + hc->setValue(hueCurve); + hc->setDirection(dir); + if (dynamic) + { + hc->makeDynamic(); + } + + test.setProcessor(hc); + + test.setErrorThreshold(1e-5f); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, bypass_rgbtohsy_fwd) +{ + BypassRGBtoHSY(test, OCIO::TRANSFORM_DIR_FORWARD, false); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, bypass_rgbtohsy_fwd_dynamic) +{ + BypassRGBtoHSY(test, OCIO::TRANSFORM_DIR_FORWARD, true); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, bypass_rgbtohsy_rev) +{ + BypassRGBtoHSY(test, OCIO::TRANSFORM_DIR_INVERSE, false); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, bypass_rgbtohsy_rev_dynamic) +{ + BypassRGBtoHSY(test, OCIO::TRANSFORM_DIR_INVERSE, true); +} diff --git a/tests/python/GradingDataTest.py b/tests/python/GradingDataTest.py index 9f928d64d4..39cd8ba5cc 100644 --- a/tests/python/GradingDataTest.py +++ b/tests/python/GradingDataTest.py @@ -330,14 +330,23 @@ def test_huecurve(self): assertEqualBSpline(self, hueLog.lum_lum, defLog) with self.assertRaises(AssertionError): assertEqualBSpline(self, hueLog.lum_lum, defLin) + self.assertEqual(hueLog.getDrawCurveOnly(), False) hueVideo = OCIO.GradingHueCurve(OCIO.GRADING_VIDEO) assertEqualHueCurve(self, hueLog, hueVideo) # Check comparison operators + huec1 = OCIO.GradingHueCurve(OCIO.GRADING_LIN) huec2 = OCIO.GradingHueCurve(OCIO.GRADING_LIN) self.assertEqual(huec1, huec2) + + huec1.setDrawCurveOnly(True) + self.assertNotEqual(huec1, huec2) + self.assertEqual(huec1.isIdentity(), True) + huec1.setDrawCurveOnly(False) + self.assertEqual(huec1, huec2) + self.assertEqual(huec1.isIdentity(), True) huec1.sat_lum.getControlPoints()[1] = OCIO.GradingControlPoint(0.4, 0.8) self.assertNotEqual(huec1, huec2) diff --git a/tests/python/GradingHueCurveTransformTest.py b/tests/python/GradingHueCurveTransformTest.py index 541ebd88ab..cca643a21e 100644 --- a/tests/python/GradingHueCurveTransformTest.py +++ b/tests/python/GradingHueCurveTransformTest.py @@ -29,16 +29,14 @@ def test_contructor(self): self.assertEqual(gct.getStyle(), OCIO.GRADING_LOG) assertEqualHueCurve(self, gct.getValue(), self.valDefaultLog) self.assertEqual(gct.isDynamic(), False) - self.assertEqual(gct.getBypassLinToLog(), False) - self.assertEqual(gct.getDrawCurveOnly(), False) + self.assertEqual(gct.getBypassRGBToHSY(), False) self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_FORWARD) gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LIN) self.assertEqual(gct.getStyle(), OCIO.GRADING_LIN) assertEqualHueCurve(self, gct.getValue(), self.valDefaultLin) self.assertEqual(gct.isDynamic(), False) - self.assertEqual(gct.getBypassLinToLog(), False) - self.assertEqual(gct.getDrawCurveOnly(), False) + self.assertEqual(gct.getBypassRGBToHSY(), False) self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_FORWARD) vals = OCIO.GradingHueCurve(OCIO.GRADING_LOG) @@ -71,12 +69,9 @@ def test_misc(self): """ gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LOG) - self.assertEqual(gct.getBypassLinToLog(), False) - gct.setBypassLinToLog(True) - self.assertEqual(gct.getBypassLinToLog(), True) - self.assertEqual(gct.getDrawCurveOnly(), False) - gct.setDrawCurveOnly(True) - self.assertEqual(gct.getDrawCurveOnly(), True) + self.assertEqual(gct.getBypassRGBToHSY(), False) + gct.setBypassRGBToHSY(True) + self.assertEqual(gct.getBypassRGBToHSY(), True) self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_FORWARD) gct.setDirection(OCIO.TRANSFORM_DIR_INVERSE) self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_INVERSE) From a5bb65fb9f59f9c7c149331d2ffc21508a94dec1 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Tue, 29 Jul 2025 01:26:49 -0400 Subject: [PATCH 24/30] Minor clean-up Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTypes.h | 6 +- src/OpenColorIO/DynamicProperty.cpp | 2 +- src/OpenColorIO/DynamicProperty.h | 4 +- src/OpenColorIO/GpuShaderUtils.h | 1 - .../ops/fixedfunction/FixedFunctionOpCPU.cpp | 34 +++--- .../ops/fixedfunction/FixedFunctionOpData.h | 12 +- .../ops/fixedfunction/FixedFunctionOpGPU.cpp | 32 ++--- .../ops/gradinghuecurve/GradingHueCurve.cpp | 5 +- .../ops/gradinghuecurve/GradingHueCurveOp.cpp | 6 - .../gradinghuecurve/GradingHueCurveOpData.cpp | 3 +- .../gradinghuecurve/GradingHueCurveOpGPU.cpp | 3 +- .../gradinghuecurve/GradingHueCurveOpGPU.h | 2 - .../gradingrgbcurve/GradingBSplineCurve.cpp | 33 +++++- .../transforms/PyGradingHueCurveTransform.cpp | 2 +- tests/cpu/Config_tests.cpp | 2 - tests/cpu/DynamicProperty_tests.cpp | 33 ++++-- .../GradingHueCurveOpCPU_tests.cpp | 112 +++++++++++------- .../GradingHueCurveTransform_tests.cpp | 34 ++++-- tests/gpu/GradingHueCurveOp_test.cpp | 68 +++++++---- tests/osl/FixedFunctionOp_test.cpp | 74 +----------- tests/python/OpenColorIOTestSuite.py | 90 +++++++------- tests/python/UnitTestUtils.py | 14 --- 22 files changed, 281 insertions(+), 291 deletions(-) diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 046f997425..0557328031 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -510,9 +510,9 @@ enum FixedFunctionStyle FIXED_FUNCTION_ACES_RGB_TO_JMH_20, ///< ACES 2.0 RGB to JMh -- EXPERIMENTAL FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20, ///< ACES 2.0 Tonescale and chroma compression -- EXPERIMENTAL FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20, ///< ACES 2.0 Gamut compression -- EXPERIMENTAL - FIXED_FUNCTION_RGB_TO_HSY_LIN, ///< RGB to HSY (Hue, Saturation, Lightness) using linear - FIXED_FUNCTION_RGB_TO_HSY_LOG, ///< RGB to HSY (Hue, Saturation, Lightness) using log - FIXED_FUNCTION_RGB_TO_HSY_VID, ///< RGB to HSY (Hue, Saturation, Lightness) using video + FIXED_FUNCTION_RGB_TO_HSY_LIN, ///< RGB to HSY (Hue, Saturation, Luminance) for linear spaces + FIXED_FUNCTION_RGB_TO_HSY_LOG, ///< RGB to HSY (Hue, Saturation, Luma) for log spaces + FIXED_FUNCTION_RGB_TO_HSY_VID, ///< RGB to HSY (Hue, Saturation, Luma) for video spaces }; /// Enumeration of the :cpp:class:`ExposureContrastTransform` transform algorithms. diff --git a/src/OpenColorIO/DynamicProperty.cpp b/src/OpenColorIO/DynamicProperty.cpp index 6c8bee5489..70db06f015 100644 --- a/src/OpenColorIO/DynamicProperty.cpp +++ b/src/OpenColorIO/DynamicProperty.cpp @@ -373,7 +373,7 @@ void DynamicPropertyGradingHueCurveImpl::precompute() curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c), m_gradingHueCurve->getDrawCurveOnly()); } - if (m_knotsCoefs.m_numKnots == 0) m_knotsCoefs.m_localBypass = true; + if (m_knotsCoefs.m_numKnots <= 0) m_knotsCoefs.m_localBypass = true; } DynamicPropertyGradingHueCurveImplRcPtr DynamicPropertyGradingHueCurveImpl::createEditableCopy() const diff --git a/src/OpenColorIO/DynamicProperty.h b/src/OpenColorIO/DynamicProperty.h index b77cc085f0..8257116e2d 100644 --- a/src/OpenColorIO/DynamicProperty.h +++ b/src/OpenColorIO/DynamicProperty.h @@ -151,7 +151,7 @@ class DynamicPropertyGradingRGBCurveImpl : public DynamicPropertyImpl, bool getLocalBypass() const; int getNumKnots() const; int getNumCoefs() const; - static int GetNumOffsetValues() { return 8; } + static int GetNumOffsetValues() { return 8; } // offset and num vals for four curves const int * getKnotsOffsetsArray() const; const int * getCoefsOffsetsArray() const; const float * getKnotsArray() const; @@ -189,7 +189,7 @@ class DynamicPropertyGradingHueCurveImpl : public DynamicPropertyImpl, bool getLocalBypass() const; int getNumKnots() const; int getNumCoefs() const; - static int GetNumOffsetValues() { return 16; } + static int GetNumOffsetValues() { return 16; } // offset and num vals for eight curves const int * getKnotsOffsetsArray() const; const int * getCoefsOffsetsArray() const; const float * getKnotsArray() const; diff --git a/src/OpenColorIO/GpuShaderUtils.h b/src/OpenColorIO/GpuShaderUtils.h index dcf16e9e70..1100fc97cb 100644 --- a/src/OpenColorIO/GpuShaderUtils.h +++ b/src/OpenColorIO/GpuShaderUtils.h @@ -275,7 +275,6 @@ void AddLinToLogShaderChannelBlue(GpuShaderCreatorRcPtr & shaderCreator, GpuShad // Convert "grading log" values to scene-linear. void AddLogToLinShader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st); - void AddLogToLinShaderChannelBlue(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st); } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp index f2610ef21a..8140d6e472 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp @@ -240,7 +240,6 @@ class Renderer_HSV_TO_RGB : public OpCPU class Renderer_RGB_TO_HSY_LOG : public OpCPU { - const float MIN_0 = -0.1f; public: Renderer_RGB_TO_HSY_LOG() = delete; explicit Renderer_RGB_TO_HSY_LOG(ConstFixedFunctionOpDataRcPtr & data); @@ -250,7 +249,6 @@ class Renderer_RGB_TO_HSY_LOG : public OpCPU class Renderer_HSY_LOG_TO_RGB : public OpCPU { - const float MIN_0 = -0.1f;; public: Renderer_HSY_LOG_TO_RGB() = delete; explicit Renderer_HSY_LOG_TO_RGB(ConstFixedFunctionOpDataRcPtr & data); @@ -260,7 +258,6 @@ class Renderer_HSY_LOG_TO_RGB : public OpCPU class Renderer_RGB_TO_HSY_VID : public OpCPU { - const float MIN_0 = 0.0f; public: Renderer_RGB_TO_HSY_VID() = delete; explicit Renderer_RGB_TO_HSY_VID(ConstFixedFunctionOpDataRcPtr & data); @@ -270,7 +267,6 @@ class Renderer_RGB_TO_HSY_VID : public OpCPU class Renderer_HSY_VID_TO_RGB : public OpCPU { - const float MIN_0 = 0.0f; public: Renderer_HSY_VID_TO_RGB() = delete; explicit Renderer_HSY_VID_TO_RGB(ConstFixedFunctionOpDataRcPtr & data); @@ -280,7 +276,6 @@ class Renderer_HSY_VID_TO_RGB : public OpCPU class Renderer_RGB_TO_HSY_LIN : public OpCPU { - const float MIN_0 = 0.2f; public: Renderer_RGB_TO_HSY_LIN() = delete; explicit Renderer_RGB_TO_HSY_LIN(ConstFixedFunctionOpDataRcPtr & data); @@ -290,7 +285,6 @@ class Renderer_RGB_TO_HSY_LIN : public OpCPU class Renderer_HSY_LIN_TO_RGB : public OpCPU { - const float MIN_0 = 0.2f; public: Renderer_HSY_LIN_TO_RGB() = delete; explicit Renderer_HSY_LIN_TO_RGB(ConstFixedFunctionOpDataRcPtr & data); @@ -1594,7 +1588,7 @@ void Renderer_HSV_TO_RGB::apply(const void * inImg, void * outImg, long numPixel } } -void applyHSYToRGB(const void * inImg, void * outImg, long numPixels, float min0) +void applyHSYToRGB(const void * inImg, void * outImg, long numPixels, FixedFunctionOpData::Style funcStyle) { const float * in = (const float *)inImg; float * out = (float *)outImg; @@ -1623,7 +1617,7 @@ void applyHSYToRGB(const void * inImg, void * outImg, long numPixels, float min0 const float distRgb = std::fabs(red - luma) + std::fabs(grn - luma) + std::fabs(blu - luma); - if (min0 > 0.f) // linear + if (funcStyle == FixedFunctionOpData::HSY_LIN_TO_RGB) { const float sumRgb = red + grn + blu; @@ -1656,13 +1650,13 @@ void applyHSYToRGB(const void * inImg, void * outImg, long numPixels, float min0 gainS = gainS >= 0.f ? gainS : (2.f * c) / (denom + discrim * 2.f); } } - else if (min0 < 0.f) // log + else if (funcStyle == FixedFunctionOpData::HSY_LOG_TO_RGB) { const float satGain = 4.f; const float currSat = distRgb * satGain; gainS = sat / std::max(1e-10f, currSat); } - else // video + else // funcStyle == FixedFunctionOpData::HSY_VID_TO_RGB { const float satGain = 1.25f; const float currSat = distRgb * satGain; @@ -1680,7 +1674,7 @@ void applyHSYToRGB(const void * inImg, void * outImg, long numPixels, float min0 } -void applyRGBToHSY(const void * inImg, void * outImg, long numPixels, float min0) +void applyRGBToHSY(const void * inImg, void * outImg, long numPixels, FixedFunctionOpData::Style funcStyle) { const float * in = (const float *)inImg; float * out = (float *)outImg; @@ -1704,7 +1698,7 @@ void applyRGBToHSY(const void * inImg, void * outImg, long numPixels, float min0 float sat = 0.f; - if (min0 > 0.f) // linear + if (funcStyle == FixedFunctionOpData::RGB_TO_HSY_LIN) { const float sumRgb = red + grn + blu; const float k = 0.15f; @@ -1717,12 +1711,12 @@ void applyRGBToHSY(const void * inImg, void * outImg, long numPixels, float min0 sat = satLo + alpha * (satHi - satLo); sat *= 1.4f; } - else if (min0 < 0.f) // log + else if (funcStyle == FixedFunctionOpData::RGB_TO_HSY_LOG) { const float sat_gain = 4.f; sat = distRgb * sat_gain; } - else // video + else // FixedFunctionOpData::RGB_TO_HSY_VID { const float sat_gain = 1.25f; sat = distRgb * sat_gain; @@ -1769,7 +1763,7 @@ Renderer_RGB_TO_HSY_LOG::Renderer_RGB_TO_HSY_LOG(ConstFixedFunctionOpDataRcPtr & void Renderer_RGB_TO_HSY_LOG::apply(const void * inImg, void * outImg, long numPixels) const { - applyRGBToHSY(inImg, outImg, numPixels, MIN_0); + applyRGBToHSY(inImg, outImg, numPixels, FixedFunctionOpData::RGB_TO_HSY_LOG); } Renderer_HSY_LOG_TO_RGB::Renderer_HSY_LOG_TO_RGB(ConstFixedFunctionOpDataRcPtr & /*data*/) @@ -1779,7 +1773,7 @@ Renderer_HSY_LOG_TO_RGB::Renderer_HSY_LOG_TO_RGB(ConstFixedFunctionOpDataRcPtr & void Renderer_HSY_LOG_TO_RGB::apply(const void * inImg, void * outImg, long numPixels) const { - applyHSYToRGB(inImg, outImg, numPixels, MIN_0); + applyHSYToRGB(inImg, outImg, numPixels, FixedFunctionOpData::HSY_LOG_TO_RGB); } Renderer_RGB_TO_HSY_LIN::Renderer_RGB_TO_HSY_LIN(ConstFixedFunctionOpDataRcPtr & /*data*/) @@ -1789,7 +1783,7 @@ Renderer_RGB_TO_HSY_LIN::Renderer_RGB_TO_HSY_LIN(ConstFixedFunctionOpDataRcPtr & void Renderer_RGB_TO_HSY_LIN::apply(const void * inImg, void * outImg, long numPixels) const { - applyRGBToHSY(inImg, outImg, numPixels, MIN_0); + applyRGBToHSY(inImg, outImg, numPixels, FixedFunctionOpData::RGB_TO_HSY_LIN); } Renderer_HSY_LIN_TO_RGB::Renderer_HSY_LIN_TO_RGB(ConstFixedFunctionOpDataRcPtr & /*data*/) @@ -1799,7 +1793,7 @@ Renderer_HSY_LIN_TO_RGB::Renderer_HSY_LIN_TO_RGB(ConstFixedFunctionOpDataRcPtr & void Renderer_HSY_LIN_TO_RGB::apply(const void * inImg, void * outImg, long numPixels) const { - applyHSYToRGB(inImg, outImg, numPixels, MIN_0); + applyHSYToRGB(inImg, outImg, numPixels, FixedFunctionOpData::HSY_LIN_TO_RGB); } Renderer_RGB_TO_HSY_VID::Renderer_RGB_TO_HSY_VID(ConstFixedFunctionOpDataRcPtr & /*data*/) @@ -1809,7 +1803,7 @@ Renderer_RGB_TO_HSY_VID::Renderer_RGB_TO_HSY_VID(ConstFixedFunctionOpDataRcPtr & void Renderer_RGB_TO_HSY_VID::apply(const void * inImg, void * outImg, long numPixels) const { - applyRGBToHSY(inImg, outImg, numPixels, MIN_0); + applyRGBToHSY(inImg, outImg, numPixels, FixedFunctionOpData::RGB_TO_HSY_VID); } Renderer_HSY_VID_TO_RGB::Renderer_HSY_VID_TO_RGB(ConstFixedFunctionOpDataRcPtr & /*data*/) @@ -1819,7 +1813,7 @@ Renderer_HSY_VID_TO_RGB::Renderer_HSY_VID_TO_RGB(ConstFixedFunctionOpDataRcPtr & void Renderer_HSY_VID_TO_RGB::apply(const void * inImg, void * outImg, long numPixels) const { - applyHSYToRGB(inImg, outImg, numPixels, MIN_0); + applyHSYToRGB(inImg, outImg, numPixels, FixedFunctionOpData::HSY_VID_TO_RGB); } Renderer_XYZ_TO_xyY::Renderer_XYZ_TO_xyY(ConstFixedFunctionOpDataRcPtr & /*data*/) diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h index 1db6aa76dc..c7c92589d0 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h @@ -62,12 +62,12 @@ class FixedFunctionOpData : public OpData ACES_TONESCALE_COMPRESS_20_INV, // ACES2 Tonescale and chroma compression (inv) ACES_GAMUT_COMPRESS_20_FWD, // ACES2 Gamut compression ACES_GAMUT_COMPRESS_20_INV, // ACES2 Gamut compression (inv) - RGB_TO_HSY_LOG, // RGB to HSY (Hue, Saturation, Lightness) using log - RGB_TO_HSY_LIN, // RGB to HSY (Hue, Saturation, Lightness) using linear - RGB_TO_HSY_VID, // RGB to HSY (Hue, Saturation, Lightness) using video - HSY_LOG_TO_RGB, // HSY (Hue, Saturation, Lightness) using log to RGB - HSY_LIN_TO_RGB, // HSY (Hue, Saturation, Lightness) using linear to RGB - HSY_VID_TO_RGB // HSY (Hue, Saturation, Lightness) using video to RGB + RGB_TO_HSY_LIN, // RGB to HSY (Hue, Saturation, Luminance) for linear spaces + RGB_TO_HSY_LOG, // RGB to HSY (Hue, Saturation, Luma) for log spaces + RGB_TO_HSY_VID, // RGB to HSY (Hue, Saturation, Luma) for video spaces + HSY_LIN_TO_RGB, // HSY (Hue, Saturation, Luminance) to RGB for linear spaces + HSY_LOG_TO_RGB, // HSY (Hue, Saturation, Luma) to RGB for log spaces + HSY_VID_TO_RGB // HSY (Hue, Saturation, Luma) to RGB for video spaces }; static const char * ConvertStyleToString(Style style, bool detailed); diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp index 167432e112..c50ee8cf89 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp @@ -1692,7 +1692,9 @@ void Add_RGB_TO_HSV(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) ss.newLine() << pxl << ".rgb = " << ss.float3Const("hue * 1./6.", "sat", "val") << ";"; } -void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, float min0) +void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, + GpuShaderText & ss, + FixedFunctionOpData::Style funcStyle) { const std::string pxl(shaderCreator->getPixelName()); @@ -1703,7 +1705,7 @@ void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "float maxRGB = max( " << pxl << ".x, max( " << pxl << ".y, " << pxl << ".z ) );"; ss.newLine() << ss.float3Decl("RGBm") << " = " << pxl << ".rgb - luma;"; ss.newLine() << "float distRGB = dot( abs(RGBm), ones );"; - if (min0 > 0.f) + if (funcStyle == FixedFunctionOpData::RGB_TO_HSY_LIN) { ss.newLine() << "float sumRGB = dot( " << pxl << ".rgb, ones );"; ss.newLine() << "float sat_hi = distRGB / (0.15 + sumRGB);"; @@ -1713,11 +1715,11 @@ void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "float sat = sat_lo + alpha * (sat_hi - sat_lo);"; ss.newLine() << "sat *= 1.4;"; } - else if (min0 < 0.f) + else if (funcStyle == FixedFunctionOpData::RGB_TO_HSY_LOG) { ss.newLine() << "float sat = distRGB * 4.;"; } - else + else // RGB_TO_HSY_VID { ss.newLine() << "float sat = distRGB * 1.25;"; } @@ -1733,7 +1735,9 @@ void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "" << pxl << ".r = hue * 1./6.; " << pxl << ".g = sat; " << pxl << ".b = luma;"; } -void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, float min0) +void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, + GpuShaderText & ss, + FixedFunctionOpData::Style funcStyle) { const std::string pxl(shaderCreator->getPixelName()); @@ -1754,7 +1758,7 @@ void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "float sat = " << pxl << ".y;"; ss.newLine() << "float distRGB = dot( abs(RGB0 - luma), ones );"; - if (min0 > 0.f) + if (funcStyle == FixedFunctionOpData::HSY_LIN_TO_RGB) { ss.newLine() << "float sumRGB = dot( RGB0, ones );"; ss.newLine() << "float k = 0.15;"; @@ -1773,11 +1777,11 @@ void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f ss.newLine() << "sm = (sm >= 0.) ? sm : (2. * c) / (denom + discrim * 2.);"; ss.newLine() << "float gainS = (alpha == 1.) ? s1 : (alpha == 0.) ? s0 : sm;"; } - else if (min0 < 0.f) + else if (funcStyle == FixedFunctionOpData::HSY_LOG_TO_RGB) { ss.newLine() << "float gainS = sat / max(1e-10, distRGB * 4.);"; } - else + else // HSY_VID_TO_RGB { ss.newLine() << "float gainS = sat / max(1e-10, distRGB * 1.25);"; } @@ -1786,32 +1790,32 @@ void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, f void Add_RGB_TO_HSY_LOG(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) { - Add_RGB_TO_HSY(shaderCreator, ss, -0.1f); + Add_RGB_TO_HSY(shaderCreator, ss, FixedFunctionOpData::RGB_TO_HSY_LOG); } void Add_RGB_TO_HSY_LIN(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) { - Add_RGB_TO_HSY(shaderCreator, ss, 0.2f); + Add_RGB_TO_HSY(shaderCreator, ss, FixedFunctionOpData::RGB_TO_HSY_LIN); } void Add_RGB_TO_HSY_VID(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) { - Add_RGB_TO_HSY(shaderCreator, ss, 0.0f); + Add_RGB_TO_HSY(shaderCreator, ss, FixedFunctionOpData::RGB_TO_HSY_VID); } void Add_HSY_LOG_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) { - Add_HSY_TO_RGB(shaderCreator, ss, -0.1f); + Add_HSY_TO_RGB(shaderCreator, ss, FixedFunctionOpData::HSY_LOG_TO_RGB); } void Add_HSY_LIN_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) { - Add_HSY_TO_RGB(shaderCreator, ss, 0.2f); + Add_HSY_TO_RGB(shaderCreator, ss, FixedFunctionOpData::HSY_LIN_TO_RGB); } void Add_HSY_VID_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) { - Add_HSY_TO_RGB(shaderCreator, ss, 0.0f); + Add_HSY_TO_RGB(shaderCreator, ss, FixedFunctionOpData::HSY_VID_TO_RGB); } void Add_HSV_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss) diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp index c59ee944f9..84e9a71773 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurve.cpp @@ -10,8 +10,10 @@ namespace OCIO_NAMESPACE { + namespace { + static const std::vector DefaultHueHueCtrl{ {0.0f, 0.0f}, {1.f/6.f, 1.f/6.f}, {2.f/6.f, 2.f/6.f}, @@ -42,7 +44,7 @@ static const std::vector DefaultSatLumCtrl{ {0.0f, 1.0f}, { static const std::vector DefaultLumLumCtrl{ { 0.f, 0.f },{ 0.5f, 0.5f },{ 1.0f, 1.0f } }; static const std::vector DefaultLumLumLinCtrl{ { -7.0f, -7.0f },{ 0.f, 0.f },{ 7.0f, 7.0f } }; -} +} // anon const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueHue(DefaultHueHueCtrl, BSplineType::HUE_HUE_B_SPLINE ); const GradingBSplineCurveImpl GradingHueCurveImpl::DefaultHueSat(DefaultHueSatCtrl, BSplineType::PERIODIC_1_B_SPLINE ); @@ -337,6 +339,5 @@ bool operator!=(const GradingHueCurve & lhs, const GradingHueCurve & rhs) return !(lhs == rhs); } - } // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp index 10a3ef37cb..1915301319 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOp.cpp @@ -65,7 +65,6 @@ class GradingHueCurveOp : public Op } }; - GradingHueCurveOp::GradingHueCurveOp(GradingHueCurveOpDataRcPtr & hueCurveData) : Op() { @@ -198,15 +197,10 @@ void GradingHueCurveOp::extractGpuShaderInfo(GpuShaderCreatorRcPtr & shaderCreat GetGradingHueCurveGPUShaderProgram(shaderCreator, data); } - } // Anon namespace - - - /////////////////////////////////////////////////////////////////////////// - void CreateGradingHueCurveOp(OpRcPtrVec & ops, GradingHueCurveOpDataRcPtr & curveData, TransformDirection direction) diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp index 0ab1c053c1..69d970ade5 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp @@ -76,7 +76,6 @@ GradingHueCurveOpData & GradingHueCurveOpData::operator=(const GradingHueCurveOp m_value->makeDynamic(); } - return *this; } @@ -150,7 +149,7 @@ std::string GradingHueCurveOpData::getCacheID() const cacheIDStream << TransformDirectionToString(getDirection()) << " "; if (m_bypassRGBToHSY) { - cacheIDStream << " bypassRGBToHSY"; + cacheIDStream << " bypassRGBToHSY "; } if (!isDynamic()) { diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp index 53640cf529..39016a4aa4 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp @@ -304,7 +304,6 @@ void AddCurveEvalMethodTextToShaderProgram(GpuShaderCreatorRcPtr & shaderCreator shaderCreator->addToHelperShaderCode(st.string().c_str()); } - void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & st, const GCProperties & props, @@ -594,7 +593,7 @@ void AddGCInverseShader(GpuShaderCreatorRcPtr & shaderCreator, } } -} +} // anon void GetGradingHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, ConstGradingHueCurveOpDataRcPtr & gcData) diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h index 70fd5e3287..a0c6630211 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.h @@ -18,5 +18,3 @@ void GetGradingHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, } // namespace OCIO_NAMESPACE #endif - - diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index b8350278fb..55ad933e81 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -13,8 +13,10 @@ namespace OCIO_NAMESPACE { + namespace { + //------------------------------------------------------------------------------------------------ // void PrepHueCurveData(const std::vector& ctrlPnts, @@ -327,6 +329,8 @@ void EstimateHueSlopes(const std::vector& ctrlPnts, } } +//------------------------------------------------------------------------------------------------ +// void EstimateRGBSlopes(const std::vector & ctrlPnts, std::vector & slopes) { @@ -378,6 +382,8 @@ void EstimateRGBSlopes(const std::vector & ctrlPnts, slopes[0] = std::max(0.01f, 0.5f * (3.f * secantSlope[0] - slopes[1])); } +//------------------------------------------------------------------------------------------------ +// void FitRGBSpline(const std::vector & ctrlPnts, const std::vector & slopes, std::vector & knots, @@ -438,6 +444,8 @@ void FitRGBSpline(const std::vector & ctrlPnts, } } +//------------------------------------------------------------------------------------------------ +// bool AdjustRGBSlopes(const std::vector & ctrlPnts, std::vector & slopes, std::vector & knots) @@ -480,6 +488,8 @@ bool AdjustRGBSlopes(const std::vector & ctrlPnts, } // namespace +//------------------------------------------------------------------------------------------------ +// GradingBSplineCurveRcPtr GradingBSplineCurve::Create(size_t size) { auto newSpline = std::make_shared(size); @@ -641,6 +651,8 @@ bool GradingBSplineCurveImpl::slopesAreDefault() const return true; } +//------------------------------------------------------------------------------------------------ +// void GradingBSplineCurveImpl::validate() const { const size_t numPoints = m_controlPoints.size(); @@ -686,6 +698,8 @@ void GradingBSplineCurveImpl::validate() const } } +//------------------------------------------------------------------------------------------------ +// bool GradingBSplineCurveImpl::isIdentity() const { bool isIdentity = true; @@ -718,6 +732,8 @@ bool GradingBSplineCurveImpl::isIdentity() const return true; } +//------------------------------------------------------------------------------------------------ +// bool IsGradingCurveIdentity(const ConstGradingBSplineCurveRcPtr & curve) { auto curveImpl = dynamic_cast(curve.get()); @@ -944,7 +960,8 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefsForHueCurve(KnotsCoefs & knots knotsCoefs.m_numCoefs += newCoefs; } - +//------------------------------------------------------------------------------------------------ +// void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int curveIdx, bool drawCurveOnly) const { if(m_splineType == BSplineType::B_SPLINE) @@ -957,6 +974,8 @@ void GradingBSplineCurveImpl::computeKnotsAndCoefs(KnotsCoefs & knotsCoefs, int } } +//------------------------------------------------------------------------------------------------ +// void GradingBSplineCurveImpl::AddShaderEvalFwd(GpuShaderText & st, const std::string & knotsOffsets, const std::string & coefsOffsets, @@ -1020,6 +1039,8 @@ void GradingBSplineCurveImpl::AddShaderEvalFwd(GpuShaderText & st, st.newLine() << "return ( A * t + B ) * t + C;"; } +//------------------------------------------------------------------------------------------------ +// void GradingBSplineCurveImpl::AddShaderEvalRev(GpuShaderText & st, const std::string & knotsOffsets, const std::string & coefsOffsets, @@ -1093,6 +1114,8 @@ void GradingBSplineCurveImpl::AddShaderEvalRev(GpuShaderText & st, st.newLine() << "return kn + (-2. * C0) / (discrim + B);"; } +//------------------------------------------------------------------------------------------------ +// void GradingBSplineCurveImpl::AddShaderEvalRevHue(GpuShaderText & st, const std::string & knotsOffsets, const std::string & coefsOffsets, @@ -1165,6 +1188,8 @@ void GradingBSplineCurveImpl::AddShaderEvalRevHue(GpuShaderText & st, st.newLine() << "return kn + (-2. * C0) / (discrim + B);"; } +//------------------------------------------------------------------------------------------------ +// GradingBSplineCurveImpl::KnotsCoefs::KnotsCoefs(size_t numCurves) { m_knotsOffsetsArray.resize(2 * numCurves); @@ -1174,6 +1199,8 @@ GradingBSplineCurveImpl::KnotsCoefs::KnotsCoefs(size_t numCurves) m_knotsArray.resize(DynamicPropertyGradingRGBCurveImpl::GetMaxKnots()); } +//------------------------------------------------------------------------------------------------ +// float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x, float identity_x) const { // NB: When evaluating hue curves, x should be wrapped to [0,1) by the caller @@ -1225,6 +1252,8 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurve(int c, float x, float ident } } +//------------------------------------------------------------------------------------------------ +// float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const { // Note: This is only intended to invert the monotonic curve types. @@ -1291,6 +1320,8 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const } } +//------------------------------------------------------------------------------------------------ +// float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRevHue(int c, float y) const { // This function is specifically to invert the HueFX and Hue-Hue curve types. diff --git a/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp b/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp index 09adb77701..d5bee36af6 100644 --- a/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp +++ b/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp @@ -68,7 +68,7 @@ void bindPyGradingHueCurveTransform(py::module & m) DOC(GradingHueCurveTransform, setSlope)) .def("slopesAreDefault", &GradingHueCurveTransform::slopesAreDefault, "curve"_a, DOC(GradingHueCurveTransform, slopesAreDefault)) -// TODO: Add more pythonic version? +// TODO: The above follows what is in GradingRGBCurveTransform, but could add a more pythonic version? // .def("getSlopes", [](GradingHueCurveTransformRcPtr self, HueCurveType curveType) // { // std::vector slopes; diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index c1e36579ae..4778f0acc0 100644 --- a/tests/cpu/Config_tests.cpp +++ b/tests/cpu/Config_tests.cpp @@ -5397,9 +5397,7 @@ R"([OpenColorIO Warning]: FixedFunction style is experimental and may be removed OCIO_CHECK_NO_THROW(ss << *config.get()); OCIO_CHECK_EQUAL(ss.str(), str); } - } - } OCIO_ADD_TEST(Config, exposure_contrast_serialization) diff --git a/tests/cpu/DynamicProperty_tests.cpp b/tests/cpu/DynamicProperty_tests.cpp index fc7b3f3687..fbf6fe87f1 100644 --- a/tests/cpu/DynamicProperty_tests.cpp +++ b/tests/cpu/DynamicProperty_tests.cpp @@ -431,24 +431,31 @@ void checkKnotsAndCoefs( OCIO_ADD_TEST(DynamicPropertyImpl, grading_hue_curve_knots_coefs) { - - auto hh = OCIO::GradingBSplineCurve::Create({ {-0.1f, -0.15f}, {0.2f, 0.3f}, {0.5f, 0.25f}, - {0.8f, 0.7f}, {0.85f, 0.8f}, {1.05f, 0.9f} }, OCIO::HUE_HUE); - auto hs = OCIO::GradingBSplineCurve::Create({ {-0.15f, 1.25f}, {0.f, 0.8f}, {0.2f, 0.9f}, - {0.4f, 1.8f}, {0.6f, 1.4f}, {0.8f, 1.3f}, {0.9f, 1.1f}, {1.1f, 0.7f} }, OCIO::HUE_SAT); - auto hl = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.22f, 0.077f}, {0.36f, 0.092f}, - {0.51f, 0.27f}, {0.67f, 0.f}, {0.83f, 0.f} }, OCIO::HUE_LUM); + auto hh = OCIO::GradingBSplineCurve::Create( + { {-0.1f, -0.15f}, {0.2f, 0.3f}, {0.5f, 0.25f}, {0.8f, 0.7f}, {0.85f, 0.8f}, {1.05f, 0.9f} }, + OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create( + { {-0.15f, 1.25f}, {0.f, 0.8f}, {0.2f, 0.9f}, {0.4f, 1.8f}, {0.6f, 1.4f}, {0.8f, 1.3f}, {0.9f, 1.1f}, {1.1f, 0.7f} }, + OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.f}, {0.22f, 0.077f}, {0.36f, 0.092f}, {0.51f, 0.27f}, {0.67f, 0.f}, {0.83f, 0.f} }, + OCIO::HUE_LUM); // The rest are identities, but not the default curves. - auto ls = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {1.f, 1.f} }, + auto ls = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.f}, {1.f, 1.f} }, OCIO::LUM_SAT); - auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.25f, 0.25f}, {1.f, 1.f} }, + auto ss = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.f}, {0.25f, 0.25f}, {1.f, 1.f} }, OCIO::SAT_SAT); - auto ll = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.25f, 0.25f}, {0.5f, 0.5f}, {1.f, 1.f} }, + auto ll = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.f}, {0.25f, 0.25f}, {0.5f, 0.5f}, {1.f, 1.f} }, OCIO::LUM_LUM); - auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.25f, 1.f}, {0.5f, 1.f}, {1.f, 1.f} }, + auto sl = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.f}, {0.25f, 1.f}, {0.5f, 1.f}, {1.f, 1.f} }, OCIO::SAT_LUM); - auto hfx = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.1f, 0.f}, {0.2f, 0.f}, - {0.4f, 0.f}, {0.6f, 0.f}, {0.8f, 0.f} }, OCIO::HUE_FX); + auto hfx = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.f}, {0.1f, 0.f}, {0.2f, 0.f}, {0.4f, 0.f}, {0.6f, 0.f}, {0.8f, 0.f} }, + OCIO::HUE_FX); auto curves = OCIO::GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp index 34e212db48..083ef945fa 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp @@ -30,7 +30,7 @@ void ValidateImage(const float * expected, const float * res, long numPix, unsig } } } -} +} // anon OCIO_ADD_TEST(GradingHueCurveOpCPU, identity) { @@ -87,22 +87,30 @@ OCIO_ADD_TEST(GradingHueCurveOpCPU, identity) OCIO_ADD_TEST(GradingHueCurveOpCPU, log_identity) { // Identity curves (for log or video) that are different from the default curves. - auto hh = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.1f, 0.1f}, {0.2f, 0.2f}, - {0.4f, 0.4f}, {0.6f, 0.6f}, {0.8f, 0.8f} }, OCIO::HUE_HUE); - auto hs = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.1f, 1.f}, {0.2f, 1.f}, - {0.4f, 1.f}, {0.6f, 1.f}, {0.8f, 1.f} }, OCIO::HUE_SAT); - auto hl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.1f, 1.f}, {0.2f, 1.f}, - {0.4f, 1.f}, {0.6f, 1.f}, {0.8f, 1.f} }, OCIO::HUE_LUM); - auto ls = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {1.f, 1.f} }, + auto hh = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.f}, {0.1f, 0.1f}, {0.2f, 0.2f}, {0.4f, 0.4f}, {0.6f, 0.6f}, {0.8f, 0.8f} }, + OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.f}, {0.1f, 1.f}, {0.2f, 1.f}, {0.4f, 1.f}, {0.6f, 1.f}, {0.8f, 1.f} }, + OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.f}, {0.1f, 1.f}, {0.2f, 1.f}, {0.4f, 1.f}, {0.6f, 1.f}, {0.8f, 1.f} }, + OCIO::HUE_LUM); + auto ls = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.f}, {1.f, 1.f} }, OCIO::LUM_SAT); - auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.25f, 0.25f}, {1.f, 1.f} }, + auto ss = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.f}, {0.25f, 0.25f}, {1.f, 1.f} }, OCIO::SAT_SAT); - auto ll = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.25f, 0.25f}, {0.5f, 0.5f}, {1.f, 1.f} }, + auto ll = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.f}, {0.25f, 0.25f}, {0.5f, 0.5f}, {1.f, 1.f} }, OCIO::LUM_LUM); - auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.25f, 1.f}, {0.5f, 1.f}, {1.f, 1.f} }, + auto sl = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.f}, {0.25f, 1.f}, {0.5f, 1.f}, {1.f, 1.f} }, OCIO::SAT_LUM); - auto hfx = OCIO::GradingBSplineCurve::Create({ {0.f, 0.f}, {0.1f, 0.f}, {0.2f, 0.f}, - {0.4f, 0.f}, {0.6f, 0.f}, {0.8f, 0.f} }, OCIO::HUE_FX); + auto hfx = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.f}, {0.1f, 0.f}, {0.2f, 0.f}, {0.4f, 0.f}, {0.6f, 0.f}, {0.8f, 0.f} }, + OCIO::HUE_FX); auto gc = std::make_shared(OCIO::GRADING_LOG, hh, hs, hl, ls, ss, ll, sl, hfx); @@ -150,10 +158,12 @@ OCIO_ADD_TEST(GradingHueCurveOpCPU, hh_hfx_curves) auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.f}, {0.9f, 1.f} }, OCIO::SAT_LUM); // Set hh and hfx to non-identities. - auto hh = OCIO::GradingBSplineCurve::Create({ {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, - {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, OCIO::HUE_HUE); - auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, - { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); + auto hh = OCIO::GradingBSplineCurve::Create( + { {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, + OCIO::HUE_HUE); + auto hfx = OCIO::GradingBSplineCurve::Create( + { {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, { 0.8f, 0.05f}, {0.99f, -0.02f} }, + OCIO::HUE_FX); auto gc = std::make_shared(OCIO::GRADING_LOG, hh, hs, hl, ls, ss, ll, sl, hfx); @@ -197,22 +207,30 @@ OCIO_ADD_TEST(GradingHueCurveOpCPU, hh_hfx_curves) OCIO_ADD_TEST(GradingHueCurveOpCPU_1, log_all_curves) { // All curves are non-identities. - auto hh = OCIO::GradingBSplineCurve::Create({ {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, - {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, OCIO::HUE_HUE); - auto hs = OCIO::GradingBSplineCurve::Create({ {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, - {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, OCIO::HUE_SAT); - auto hl = OCIO::GradingBSplineCurve::Create({ {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, - {0.5f, 0.8f}, {0.8f, 0.5f} }, OCIO::HUE_LUM); - auto ls = OCIO::GradingBSplineCurve::Create({ {0.05f, 1.5f}, {0.5f, 0.9f}, {1.1f, 1.4f}, - }, OCIO::LUM_SAT); - auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, + auto hh = OCIO::GradingBSplineCurve::Create( + { {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, + OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create( + { {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, + OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create( + { {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, {0.5f, 0.8f}, {0.8f, 0.5f} }, + OCIO::HUE_LUM); + auto ls = OCIO::GradingBSplineCurve::Create( + { {0.05f, 1.5f}, {0.5f, 0.9f}, {1.1f, 1.4f} }, + OCIO::LUM_SAT); + auto ss = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, OCIO::SAT_SAT); - auto ll = OCIO::GradingBSplineCurve::Create({ {-0.02f, -0.04f}, {0.2f, 0.1f}, {0.8f, 0.95f}, {1.1f, 1.2f} }, + auto ll = OCIO::GradingBSplineCurve::Create( + { {-0.02f, -0.04f}, {0.2f, 0.1f}, {0.8f, 0.95f}, {1.1f, 1.2f} }, OCIO::LUM_LUM); - auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, + auto sl = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, OCIO::SAT_LUM); - auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, - { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); + auto hfx = OCIO::GradingBSplineCurve::Create( + { {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, { 0.8f, 0.05f}, {0.99f, -0.02f} }, + OCIO::HUE_FX); auto gc = std::make_shared(OCIO::GRADING_LOG, hh, hs, hl, ls, ss, ll, sl, hfx); @@ -258,22 +276,30 @@ OCIO_ADD_TEST(GradingHueCurveOpCPU_1, log_all_curves) OCIO_ADD_TEST(GradingHueCurveOpCPU, lin_all_curves) { // All curves are non-identities. - auto hh = OCIO::GradingBSplineCurve::Create({ {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, - {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, OCIO::HUE_HUE); - auto hs = OCIO::GradingBSplineCurve::Create({ {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, - {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, OCIO::HUE_SAT); - auto hl = OCIO::GradingBSplineCurve::Create({ {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, - {0.5f, 0.8f}, {0.8f, 0.5f} }, OCIO::HUE_LUM); - auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, + auto hh = OCIO::GradingBSplineCurve::Create( + { {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, + OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create( + { {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, + OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create( + { {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, {0.5f, 0.8f}, {0.8f, 0.5f} }, + OCIO::HUE_LUM); + auto ss = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, OCIO::SAT_SAT); - auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, + auto sl = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, OCIO::SAT_LUM); - auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, - { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); + auto hfx = OCIO::GradingBSplineCurve::Create( + { {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, { 0.8f, 0.05f}, {0.99f, -0.02f} }, + OCIO::HUE_FX); // Adjust these two, relative to previous test, to work in f-stops. - auto ls = OCIO::GradingBSplineCurve::Create({ {-6.f, 0.9f}, {-3.f, 0.8f}, {0.f, 1.2f}, - {2.f, 1.f}, {4.f, 0.6f}, {6.f, 0.55f} }, OCIO::LUM_SAT); - auto ll = OCIO::GradingBSplineCurve::Create({ {-8.f, -7.f}, {-2.f, -3.f}, {2.f, 3.5f}, {8.f, 7.f} }, + auto ls = OCIO::GradingBSplineCurve::Create( + { {-6.f, 0.9f}, {-3.f, 0.8f}, {0.f, 1.2f}, {2.f, 1.f}, {4.f, 0.6f}, {6.f, 0.55f} }, + OCIO::LUM_SAT); + auto ll = OCIO::GradingBSplineCurve::Create( + { {-8.f, -7.f}, {-2.f, -3.f}, {2.f, 3.5f}, {8.f, 7.f} }, OCIO::LUM_LUM); auto gc = std::make_shared(OCIO::GRADING_LIN, diff --git a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp index a089a42c57..b772d3315c 100644 --- a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp +++ b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp @@ -227,22 +227,30 @@ OCIO_ADD_TEST(GradingHueCurveTransform, serialization) { // Test the serialization of the transform. - auto hh = OCIO::GradingBSplineCurve::Create({ {-0.1f, -0.15f}, {0.2f, 0.3f}, {0.5f, 0.25f}, - {0.8f, 0.7f}, {0.85f, 0.8f}, {1.05f, 0.9f} }, OCIO::HUE_HUE); - auto hs = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.1f, 1.2f}, {0.4f, 0.7f}, - {0.6f, 0.3f}, {0.8f, 0.5f}, {0.9f, 0.8f} }, OCIO::HUE_SAT); - auto hl = OCIO::GradingBSplineCurve::Create({ {0.1f, 1.4f}, {0.2f, 1.4f}, {0.4f, 0.7f}, - {0.6f, 0.5f}, {0.8f, 0.8f} }, OCIO::HUE_LUM); - auto ls = OCIO::GradingBSplineCurve::Create({ {0.0f, 1.0f}, {0.5f, 1.5f}, {1.0f, 0.9f}, - {1.1f, 1.1f} }, OCIO::LUM_SAT); - auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.05f}, {0.5f, 0.8f}, {1.f, 1.05f} }, + auto hh = OCIO::GradingBSplineCurve::Create( + { {-0.1f, -0.15f}, {0.2f, 0.3f}, {0.5f, 0.25f}, {0.8f, 0.7f}, {0.85f, 0.8f}, {1.05f, 0.9f} }, + OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.2f}, {0.1f, 1.2f}, {0.4f, 0.7f}, {0.6f, 0.3f}, {0.8f, 0.5f}, {0.9f, 0.8f} }, + OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create( + { {0.1f, 1.4f}, {0.2f, 1.4f}, {0.4f, 0.7f}, {0.6f, 0.5f}, {0.8f, 0.8f} }, + OCIO::HUE_LUM); + auto ls = OCIO::GradingBSplineCurve::Create( + { {0.0f, 1.0f}, {0.5f, 1.5f}, {1.0f, 0.9f}, {1.1f, 1.1f} }, + OCIO::LUM_SAT); + auto ss = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.05f}, {0.5f, 0.8f}, {1.f, 1.05f} }, OCIO::SAT_SAT); - auto ll = OCIO::GradingBSplineCurve::Create({ {0.f, -0.0005f}, {0.5f, 0.3f}, {1.f, 0.9f} }, + auto ll = OCIO::GradingBSplineCurve::Create( + { {0.f, -0.0005f}, {0.5f, 0.3f}, {1.f, 0.9f} }, OCIO::LUM_LUM); - auto sl = OCIO::GradingBSplineCurve::Create({ {0.05f, 1.1f}, {0.3f, 1.f}, {1.2f, 0.9f} }, + auto sl = OCIO::GradingBSplineCurve::Create( + { {0.05f, 1.1f}, {0.3f, 1.f}, {1.2f, 0.9f} }, OCIO::SAT_LUM); - auto hfx = OCIO::GradingBSplineCurve::Create({ {-0.15f, 0.1f}, {0.f, -0.05f}, {0.2f, -0.1f}, - {0.4f, 0.3f}, {0.6f, 0.25f}, { 0.8f, 0.2f}, {0.9f, 0.05f}, {1.1f, -0.07f} }, OCIO::HUE_FX); + auto hfx = OCIO::GradingBSplineCurve::Create( + { {-0.15f, 0.1f}, {0.f, -0.05f}, {0.2f, -0.1f}, {0.4f, 0.3f}, {0.6f, 0.25f}, { 0.8f, 0.2f}, {0.9f, 0.05f}, {1.1f, -0.07f} }, + OCIO::HUE_FX); auto data = OCIO::GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); diff --git a/tests/gpu/GradingHueCurveOp_test.cpp b/tests/gpu/GradingHueCurveOp_test.cpp index 8a4f9ea548..224bc67787 100644 --- a/tests/gpu/GradingHueCurveOp_test.cpp +++ b/tests/gpu/GradingHueCurveOp_test.cpp @@ -32,22 +32,30 @@ void GenerateIdentityLut3D(OCIOGPUTest::CustomValues & values, int edgeLen, floa void GradingHueCurveLog(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) { // All curves are non-identities. - auto hh = OCIO::GradingBSplineCurve::Create({ {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, - {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, OCIO::HUE_HUE); - auto hs = OCIO::GradingBSplineCurve::Create({ {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, - {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, OCIO::HUE_SAT); - auto hl = OCIO::GradingBSplineCurve::Create({ {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, - {0.5f, 0.8f}, {0.8f, 0.5f} }, OCIO::HUE_LUM); - auto ls = OCIO::GradingBSplineCurve::Create({ {0.05f, 1.5f}, {0.5f, 0.9f}, {1.1f, 1.4f}, - }, OCIO::LUM_SAT); - auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, + auto hh = OCIO::GradingBSplineCurve::Create( + { {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, + OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create( + { {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, + OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create( + { {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, {0.5f, 0.8f}, {0.8f, 0.5f} }, + OCIO::HUE_LUM); + auto ls = OCIO::GradingBSplineCurve::Create( + { {0.05f, 1.5f}, {0.5f, 0.9f}, {1.1f, 1.4f} }, + OCIO::LUM_SAT); + auto ss = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, OCIO::SAT_SAT); - auto ll = OCIO::GradingBSplineCurve::Create({ {-0.02f, -0.04f}, {0.2f, 0.1f}, {0.8f, 0.95f}, {1.1f, 1.2f} }, + auto ll = OCIO::GradingBSplineCurve::Create( + { {-0.02f, -0.04f}, {0.2f, 0.1f}, {0.8f, 0.95f}, {1.1f, 1.2f} }, OCIO::LUM_LUM); - auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, + auto sl = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.1f} }, OCIO::SAT_LUM); - auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, - { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); + auto hfx = OCIO::GradingBSplineCurve::Create( + { {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, { 0.8f, 0.05f}, {0.99f, -0.02f} }, + OCIO::HUE_FX); auto curve = OCIO::GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); auto hc = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); @@ -98,23 +106,31 @@ OCIO_ADD_GPU_TEST(GradingHueCurve, style_log_rev_dynamic) void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) { // All curves are non-identities. - auto hh = OCIO::GradingBSplineCurve::Create({ {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, - {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, OCIO::HUE_HUE); - auto hs = OCIO::GradingBSplineCurve::Create({ {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, - {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, OCIO::HUE_SAT); - auto hl = OCIO::GradingBSplineCurve::Create({ {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, - {0.5f, 0.8f}, {0.8f, 0.5f} }, OCIO::HUE_LUM); - auto ss = OCIO::GradingBSplineCurve::Create({ {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, + auto hh = OCIO::GradingBSplineCurve::Create( + { {0.05f, 0.15f}, {0.2f, 0.3f}, {0.35f, 0.4f}, {0.45f, 0.45f}, {0.6f, 0.7f}, {0.8f, 0.85f} }, + OCIO::HUE_HUE); + auto hs = OCIO::GradingBSplineCurve::Create( + { {-0.1f, 1.2f}, {0.2f, 0.7f}, {0.4f, 1.5f}, {0.5f, 0.5f}, {0.6f, 1.4f}, {0.8f, 0.7f} }, + OCIO::HUE_SAT); + auto hl = OCIO::GradingBSplineCurve::Create( + { {0.1f, 1.5f}, {0.2f, 0.7f}, {0.4f, 1.4f}, {0.5f, 0.8f}, {0.8f, 0.5f} }, + OCIO::HUE_LUM); + auto ss = OCIO::GradingBSplineCurve::Create( + { {0.f, 0.1f}, {0.5f, 0.45f}, {1.f, 1.1f} }, OCIO::SAT_SAT); - auto hfx = OCIO::GradingBSplineCurve::Create({ {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, - { 0.8f, 0.05f}, {0.99f, -0.02f} }, OCIO::HUE_FX); + auto hfx = OCIO::GradingBSplineCurve::Create( + { {0.2f, 0.05f}, {0.4f, -0.09f}, {0.6f, -0.2f}, { 0.8f, 0.05f}, {0.99f, -0.02f} }, + OCIO::HUE_FX); // Adjust these two, relative to previous test, to work in f-stops. - auto ll = OCIO::GradingBSplineCurve::Create({ {-8.f, -7.f}, {-2.f, -3.f}, {2.f, 3.5f}, {8.f, 7.f} }, + auto ll = OCIO::GradingBSplineCurve::Create( + { {-8.f, -7.f}, {-2.f, -3.f}, {2.f, 3.5f}, {8.f, 7.f} }, OCIO::LUM_LUM); - auto ls = OCIO::GradingBSplineCurve::Create({ {-6.f, 0.9f}, {-3.f, 0.95f}, {0.f, 1.1f}, - {2.f, 1.f}, {4.f, 0.6f}, {6.f, 0.55f} }, OCIO::LUM_SAT); + auto ls = OCIO::GradingBSplineCurve::Create( + { {-6.f, 0.9f}, {-3.f, 0.95f}, {0.f, 1.1f}, {2.f, 1.f}, {4.f, 0.6f}, {6.f, 0.55f} }, + OCIO::LUM_SAT); // Adjusted this one, relative to above, to avoid some artifacts due to high sat boost. - auto sl = OCIO::GradingBSplineCurve::Create({ {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.05f}, {1.f, 1.1f} }, + auto sl = OCIO::GradingBSplineCurve::Create( + { {0.f, 1.2f}, {0.6f, 0.8f}, {0.9f, 1.05f}, {1.f, 1.1f} }, OCIO::SAT_LUM); auto curve = OCIO::GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); diff --git a/tests/osl/FixedFunctionOp_test.cpp b/tests/osl/FixedFunctionOp_test.cpp index 051c64b774..6e77c9546d 100644 --- a/tests/osl/FixedFunctionOp_test.cpp +++ b/tests/osl/FixedFunctionOp_test.cpp @@ -402,78 +402,6 @@ OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSV_inv_custom) m_data->m_threshold = 1e-6f; } -// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LIN_fwd) -// { -// OCIO::FixedFunctionTransformRcPtr func = -// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); -// func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); -// -// m_data->m_transform = func; -// -// m_data->m_threshold = 1e-6f; -// m_data->m_relativeComparison = true; -// } -// -// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LIN_inv) -// { -// OCIO::FixedFunctionTransformRcPtr func = -// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LIN); -// func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); -// -// m_data->m_transform = func; -// -// m_data->m_threshold = 1e-6f; -// m_data->m_relativeComparison = true; -// } -// -// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LOG_fwd) -// { -// OCIO::FixedFunctionTransformRcPtr func = -// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); -// func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); -// -// m_data->m_transform = func; -// -// m_data->m_threshold = 1e-6f; -// m_data->m_relativeComparison = true; -// } -// -// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_LOG_inv) -// { -// OCIO::FixedFunctionTransformRcPtr func = -// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_LOG); -// func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); -// -// m_data->m_transform = func; -// -// m_data->m_threshold = 1e-6f; -// m_data->m_relativeComparison = true; -// } -// -// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_VID_fwd) -// { -// OCIO::FixedFunctionTransformRcPtr func = -// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); -// func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); -// -// m_data->m_transform = func; -// -// m_data->m_threshold = 1e-6f; -// m_data->m_relativeComparison = true; -// } -// -// OCIO_OSL_TEST(FixedFunction, style_RGB_TO_HSY_VID_inv) -// { -// OCIO::FixedFunctionTransformRcPtr func = -// OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_RGB_TO_HSY_VID); -// func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); -// -// m_data->m_transform = func; -// -// m_data->m_threshold = 1e-6f; -// m_data->m_relativeComparison = true; -// } - OCIO_OSL_TEST(FixedFunction, style_XYZ_TO_xyY_fwd) { OCIO::FixedFunctionTransformRcPtr func = @@ -539,3 +467,5 @@ OCIO_OSL_TEST(FixedFunction, style_XYZ_TO_LUV_inv) m_data->m_threshold = 1e-5f; } + +// TODO: Add tests for: RGB_TO_HSY styles. diff --git a/tests/python/OpenColorIOTestSuite.py b/tests/python/OpenColorIOTestSuite.py index 545197fe60..668550818c 100755 --- a/tests/python/OpenColorIOTestSuite.py +++ b/tests/python/OpenColorIOTestSuite.py @@ -103,53 +103,53 @@ def suite(): suite = unittest.TestSuite() loader = unittest.TestLoader() -# suite.addTest(loader.loadTestsFromModule(AllocationTransformTest)) -# suite.addTest(loader.loadTestsFromModule(BakerTest)) -# suite.addTest(loader.loadTestsFromModule(BuiltinConfigRegistryTest)) -# suite.addTest(loader.loadTestsFromModule(BuiltinTransformRegistryTest)) -# suite.addTest(loader.loadTestsFromModule(BuiltinTransformTest)) -# suite.addTest(loader.loadTestsFromModule(CDLTransformTest)) -# suite.addTest(loader.loadTestsFromModule(ColorSpaceHelpersTest)) -# suite.addTest(loader.loadTestsFromModule(ColorSpaceTest)) -# suite.addTest(loader.loadTestsFromModule(ColorSpaceTransformTest)) -# suite.addTest(loader.loadTestsFromModule(ConfigTest)) -# suite.addTest(loader.loadTestsFromModule(ConstantsTest)) -# suite.addTest(loader.loadTestsFromModule(ContextTest)) -# suite.addTest(loader.loadTestsFromModule(CPUProcessorTest)) -# suite.addTest(loader.loadTestsFromModule(DisplayViewHelpersTest)) -# suite.addTest(loader.loadTestsFromModule(DisplayViewTransformTest)) -# suite.addTest(loader.loadTestsFromModule(ExponentTransformTest)) -# suite.addTest(loader.loadTestsFromModule(ExponentWithLinearTransformTest)) -# suite.addTest(loader.loadTestsFromModule(ExposureContrastTransformTest)) -# suite.addTest(loader.loadTestsFromModule(FileTransformTest)) -# suite.addTest(loader.loadTestsFromModule(FileRulesTest)) -# suite.addTest(loader.loadTestsFromModule(FixedFunctionTransformTest)) -# suite.addTest(loader.loadTestsFromModule(FormatMetadataTest)) -# suite.addTest(loader.loadTestsFromModule(GpuShaderDescTest)) + suite.addTest(loader.loadTestsFromModule(AllocationTransformTest)) + suite.addTest(loader.loadTestsFromModule(BakerTest)) + suite.addTest(loader.loadTestsFromModule(BuiltinConfigRegistryTest)) + suite.addTest(loader.loadTestsFromModule(BuiltinTransformRegistryTest)) + suite.addTest(loader.loadTestsFromModule(BuiltinTransformTest)) + suite.addTest(loader.loadTestsFromModule(CDLTransformTest)) + suite.addTest(loader.loadTestsFromModule(ColorSpaceHelpersTest)) + suite.addTest(loader.loadTestsFromModule(ColorSpaceTest)) + suite.addTest(loader.loadTestsFromModule(ColorSpaceTransformTest)) + suite.addTest(loader.loadTestsFromModule(ConfigTest)) + suite.addTest(loader.loadTestsFromModule(ConstantsTest)) + suite.addTest(loader.loadTestsFromModule(ContextTest)) + suite.addTest(loader.loadTestsFromModule(CPUProcessorTest)) + suite.addTest(loader.loadTestsFromModule(DisplayViewHelpersTest)) + suite.addTest(loader.loadTestsFromModule(DisplayViewTransformTest)) + suite.addTest(loader.loadTestsFromModule(ExponentTransformTest)) + suite.addTest(loader.loadTestsFromModule(ExponentWithLinearTransformTest)) + suite.addTest(loader.loadTestsFromModule(ExposureContrastTransformTest)) + suite.addTest(loader.loadTestsFromModule(FileTransformTest)) + suite.addTest(loader.loadTestsFromModule(FileRulesTest)) + suite.addTest(loader.loadTestsFromModule(FixedFunctionTransformTest)) + suite.addTest(loader.loadTestsFromModule(FormatMetadataTest)) + suite.addTest(loader.loadTestsFromModule(GpuShaderDescTest)) suite.addTest(loader.loadTestsFromModule(GradingDataTest)) -# suite.addTest(loader.loadTestsFromModule(GradingPrimaryTransformTest)) + suite.addTest(loader.loadTestsFromModule(GradingPrimaryTransformTest)) suite.addTest(loader.loadTestsFromModule(GradingHueCurveTransformTest)) -# suite.addTest(loader.loadTestsFromModule(GradingRGBCurveTransformTest)) -# suite.addTest(loader.loadTestsFromModule(GradingToneTransformTest)) -# suite.addTest(loader.loadTestsFromModule(GroupTransformTest)) -# suite.addTest(loader.loadTestsFromModule(LegacyViewingPipelineTest)) -# suite.addTest(loader.loadTestsFromModule(LogCameraTransformTest)) -# suite.addTest(loader.loadTestsFromModule(LogTransformTest)) -# suite.addTest(loader.loadTestsFromModule(LookTest)) -# suite.addTest(loader.loadTestsFromModule(LookTransformTest)) -# suite.addTest(loader.loadTestsFromModule(Lut1DTransformTest)) -# suite.addTest(loader.loadTestsFromModule(Lut3DTransformTest)) -# suite.addTest(loader.loadTestsFromModule(MatrixTransformTest)) -# suite.addTest(loader.loadTestsFromModule(MixingHelpersTest)) -# suite.addTest(loader.loadTestsFromModule(NamedTransformTest)) -# suite.addTest(loader.loadTestsFromModule(OCIOZArchiveTest)) -# suite.addTest(loader.loadTestsFromModule(OpenColorIOTest)) -# suite.addTest(loader.loadTestsFromModule(ProcessorCacheTest)) -# suite.addTest(loader.loadTestsFromModule(ProcessorTest)) -# suite.addTest(loader.loadTestsFromModule(RangeTransformTest)) -# suite.addTest(loader.loadTestsFromModule(TransformsTest)) -# suite.addTest(loader.loadTestsFromModule(ViewingRulesTest)) -# suite.addTest(loader.loadTestsFromModule(ViewTransformTest)) + suite.addTest(loader.loadTestsFromModule(GradingRGBCurveTransformTest)) + suite.addTest(loader.loadTestsFromModule(GradingToneTransformTest)) + suite.addTest(loader.loadTestsFromModule(GroupTransformTest)) + suite.addTest(loader.loadTestsFromModule(LegacyViewingPipelineTest)) + suite.addTest(loader.loadTestsFromModule(LogCameraTransformTest)) + suite.addTest(loader.loadTestsFromModule(LogTransformTest)) + suite.addTest(loader.loadTestsFromModule(LookTest)) + suite.addTest(loader.loadTestsFromModule(LookTransformTest)) + suite.addTest(loader.loadTestsFromModule(Lut1DTransformTest)) + suite.addTest(loader.loadTestsFromModule(Lut3DTransformTest)) + suite.addTest(loader.loadTestsFromModule(MatrixTransformTest)) + suite.addTest(loader.loadTestsFromModule(MixingHelpersTest)) + suite.addTest(loader.loadTestsFromModule(NamedTransformTest)) + suite.addTest(loader.loadTestsFromModule(OCIOZArchiveTest)) + suite.addTest(loader.loadTestsFromModule(OpenColorIOTest)) + suite.addTest(loader.loadTestsFromModule(ProcessorCacheTest)) + suite.addTest(loader.loadTestsFromModule(ProcessorTest)) + suite.addTest(loader.loadTestsFromModule(RangeTransformTest)) + suite.addTest(loader.loadTestsFromModule(TransformsTest)) + suite.addTest(loader.loadTestsFromModule(ViewingRulesTest)) + suite.addTest(loader.loadTestsFromModule(ViewTransformTest)) return suite diff --git a/tests/python/UnitTestUtils.py b/tests/python/UnitTestUtils.py index 07c5b0aff7..20e54b966f 100644 --- a/tests/python/UnitTestUtils.py +++ b/tests/python/UnitTestUtils.py @@ -67,20 +67,6 @@ def assertAlmostEqualVector(testCase, first, second, delta=1e-6): for pt1, pt2 in zip(first, second): testCase.assertAlmostEqual(pt1, pt2, delta=delta) -# -# for pt1 in first: -# try: -# pt2 = next(second) -# testCase.assertAlmostEqual(testCase, pt1, pt2, delta=delta) -# except StopIteration: -# raise AssertionError("Different number of elements") -# try: -# pt2 = next(second) -# except StopIteration: -# pass -# else: -# raise AssertionError("Different number of elements") - def assertEqualRGBCurve(testCase, first, second): assertEqualBSpline(testCase, first.red, second.red) assertEqualBSpline(testCase, first.green, second.green) From ac8a1bf0ddf22bdf936b7045307f2c0b8aa18519 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Wed, 30 Jul 2025 00:09:56 -0400 Subject: [PATCH 25/30] Op<< should contain slopes Signed-off-by: Doug Walker --- .../transforms/GradingRGBCurveTransform.cpp | 10 ++++++- .../GradingHueCurveTransform_tests.cpp | 28 +++++++++++-------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/OpenColorIO/transforms/GradingRGBCurveTransform.cpp b/src/OpenColorIO/transforms/GradingRGBCurveTransform.cpp index 77ace5ad4f..30cf5d2a67 100644 --- a/src/OpenColorIO/transforms/GradingRGBCurveTransform.cpp +++ b/src/OpenColorIO/transforms/GradingRGBCurveTransform.cpp @@ -160,7 +160,15 @@ std::ostream & operator<<(std::ostream & os, const GradingBSplineCurve & bspline const auto numPoints = bspline.getNumControlPoints(); for (size_t i = 0; i < numPoints; ++i) { - os << bspline.getControlPoint(i); + if (bspline.slopesAreDefault()) + { + os << bspline.getControlPoint(i); + } + else + { + const GradingControlPoint cp = bspline.getControlPoint(i); + os << ""; + } } os << "]>"; return os; diff --git a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp index b772d3315c..4508553159 100644 --- a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp +++ b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp @@ -252,6 +252,10 @@ OCIO_ADD_TEST(GradingHueCurveTransform, serialization) { {-0.15f, 0.1f}, {0.f, -0.05f}, {0.2f, -0.1f}, {0.4f, 0.3f}, {0.6f, 0.25f}, { 0.8f, 0.2f}, {0.9f, 0.05f}, {1.1f, -0.07f} }, OCIO::HUE_FX); + ss->setSlope(0, 1.2f); + ss->setSlope(1, 0.8f); + ss->setSlope(2, 0.4f); + auto data = OCIO::GradingHueCurve::Create(hh, hs, hl, ls, ss, ll, sl, hfx); auto curve = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_VIDEO); @@ -266,32 +270,32 @@ OCIO_ADD_TEST(GradingHueCurveTransform, serialization) "]>, hue_sat=" "]>, hue_lum=]>, lum_sat=" - "]>, sat_sat=" - "]>, lum_lum=]>, sat_sat=]>, lum_lum=]>, sat_lum=" "]>, hue_fx=" "]>>>"; { - std::ostringstream oss; - oss << *curve; + std::ostringstream oss; + oss << *curve; - OCIO_CHECK_EQUAL(oss.str(), CURVE_STR); + OCIO_CHECK_EQUAL(oss.str(), CURVE_STR); } OCIO::GroupTransformRcPtr grp = OCIO::GroupTransform::Create(); grp->appendTransform(OCIO::DynamicPtrCast(curve)); { - std::ostringstream oss; - oss << *grp; + std::ostringstream oss; + oss << *grp; - std::string GROUP_STR(" Date: Wed, 30 Jul 2025 00:40:14 -0400 Subject: [PATCH 26/30] Fix python doc line Signed-off-by: Doug Walker --- src/bindings/python/PyGradingData.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bindings/python/PyGradingData.cpp b/src/bindings/python/PyGradingData.cpp index 9e9e0c5beb..a1a093d7b3 100644 --- a/src/bindings/python/PyGradingData.cpp +++ b/src/bindings/python/PyGradingData.cpp @@ -346,8 +346,7 @@ void bindPyGradingData(py::module & m) slopes[pt] = self->getSlope(pt); } return slopes; - }, - DOC(GradingBSplineCurve, getSlopes)) + }) .def("setSlopes", [](GradingBSplineCurveRcPtr self, const std::vector & slopes) { const size_t numPts = self->getNumControlPoints(); @@ -360,8 +359,7 @@ void bindPyGradingData(py::module & m) { self->setSlope(pt, slopes[pt]); } - }, - DOC(GradingBSplineCurve, getSlopes)); + }); defRepr(clsGradingBSplineCurve); From 62a75d8528a8ce106f5091ba3d37249b916d3a5c Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Wed, 24 Sep 2025 17:44:00 -0400 Subject: [PATCH 27/30] Improve validation of control points Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTransforms.h | 10 ++- src/OpenColorIO/DynamicProperty.cpp | 7 ++ .../gradingrgbcurve/GradingBSplineCurve.cpp | 56 ++++++++++++-- .../ops/gradingrgbcurve/GradingRGBCurve.cpp | 9 +++ tests/cpu/Config_tests.cpp | 4 +- tests/cpu/DynamicProperty_tests.cpp | 73 ++++++++++++++++--- tests/cpu/fileformats/FileFormatCTF_tests.cpp | 70 ++++++++++-------- .../GradingHueCurveOpData_tests.cpp | 30 +++++++- .../GradingBSplineCurve_tests.cpp | 2 +- .../GradingRGBCurveOpData_tests.cpp | 16 +++- .../GradingRGBCurveOp_tests.cpp | 6 +- .../GradingHueCurveTransform_tests.cpp | 36 ++++++--- .../GradingRGBCurveTransform_tests.cpp | 2 +- 13 files changed, 248 insertions(+), 73 deletions(-) diff --git a/include/OpenColorIO/OpenColorTransforms.h b/include/OpenColorIO/OpenColorTransforms.h index 88da1c6f56..0818f83c0c 100644 --- a/include/OpenColorIO/OpenColorTransforms.h +++ b/include/OpenColorIO/OpenColorTransforms.h @@ -1309,14 +1309,18 @@ extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const GradingPrimary * The domain of the curves is [0,1] and control points outside that domain are mapped into it. * A hue of 0 or 1 corresponds to a magenta hue. * + * The control points are dynamic, so they may be adjusted even after the Transform is included + * in a Processor. However, creating a curve or setting the parameters will call the + * GradingBSplineCurveImpl::validate function, which will throw an exception if the control + * points do not meet certain requirements, for example that the X-coordinates are non-decreasing + * Please review that function for details on the validation. Applications that provide a UI to + * edit curves must ensure that they prevent users from creating control points that are not valid. + * * The transform is invertible as long as the curves allow it. For example, if saturation is * mapped to zero, obviously that cannot be resaturated. Care should be taken with the Hue-FX * curve because it is possible to fold hues over on themselves, which also cannot be inverted. * In most cases the Hue-FX curve is not necessary since the Hue-Hue curve provides similar * functionality with the added benefit of being strictly invertible. - * - * The control points are dynamic, so they may be adjusted even after the Transform is included - * in a Processor. */ class OCIOEXPORT GradingHueCurveTransform : public Transform { diff --git a/src/OpenColorIO/DynamicProperty.cpp b/src/OpenColorIO/DynamicProperty.cpp index 70db06f015..075973412f 100644 --- a/src/OpenColorIO/DynamicProperty.cpp +++ b/src/OpenColorIO/DynamicProperty.cpp @@ -137,6 +137,8 @@ DynamicPropertyDoubleImplRcPtr DynamicPropertyDoubleImpl::createEditableCopy() c return std::make_shared(getType(), getValue(), isDynamic()); } +//======================================================================================== + DynamicPropertyGradingPrimaryImpl::DynamicPropertyGradingPrimaryImpl(GradingStyle style, TransformDirection dir, const GradingPrimary & value, @@ -195,6 +197,8 @@ void DynamicPropertyGradingPrimaryImpl::setDirection(TransformDirection dir) noe } } +//======================================================================================== + DynamicPropertyGradingRGBCurveImpl::DynamicPropertyGradingRGBCurveImpl( const ConstGradingRGBCurveRcPtr & value, bool dynamic) : DynamicPropertyImpl(DYNAMIC_PROPERTY_GRADING_RGBCURVE, dynamic) @@ -288,6 +292,7 @@ DynamicPropertyGradingRGBCurveImplRcPtr DynamicPropertyGradingRGBCurveImpl::crea return res; } +//======================================================================================== DynamicPropertyGradingHueCurveImpl::DynamicPropertyGradingHueCurveImpl( const ConstGradingHueCurveRcPtr & value, bool dynamic) @@ -383,6 +388,8 @@ DynamicPropertyGradingHueCurveImplRcPtr DynamicPropertyGradingHueCurveImpl::crea return res; } +//======================================================================================== + DynamicPropertyGradingToneImpl::DynamicPropertyGradingToneImpl(const GradingTone & value, GradingStyle style, bool dynamic) diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index 55ad933e81..8dc8d8d23d 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -73,6 +73,7 @@ void PrepHueCurveData(const std::vector& ctrlPnts, } if (!isHorizontal) { + // Ensure that there is a minimum space between the y values. const float y_span = outCtrlPnts[numCtrlPnts - 1].m_y - outCtrlPnts[0].m_y; for (unsigned i = 1; i < outCtrlPnts.size(); ++i) { @@ -665,29 +666,72 @@ void GradingBSplineCurveImpl::validate() const throw Exception("The slopes array must be the same length as the control points."); } - // Make sure the points are non-decreasing. + // Make sure the x-coordinates are non-decreasing. float lastX = -std::numeric_limits::max(); for (size_t i = 0; i < numPoints; ++i) { - // Test x values only. const float x = m_controlPoints[i].m_x; if (x < lastX) { std::ostringstream oss; oss << "Control point at index " << i << " has a x coordinate '" << x << "' that is "; - oss << "less than previous control point x cooordinate '" << lastX << "'."; + oss << "less than previous control point x coordinate '" << lastX << "'."; throw Exception(oss.str().c_str()); } lastX = x; } + // The x-coordinates for a hue-hue spline must be in [0,1]. + if (m_splineType == HUE_HUE_B_SPLINE) + { + if (m_controlPoints[0].m_x < 0.f) + { + std::ostringstream oss; + oss << "The HUE-HUE spline may not have negative x coordinates."; + throw Exception(oss.str().c_str()); + } + else if (m_controlPoints[numPoints - 1].m_x > 1.f) + { + std::ostringstream oss; + oss << "The HUE-HUE spline may not have x coordinates greater than one."; + throw Exception(oss.str().c_str()); + } + } + + // Make sure the y-coordinates are non-decreasing, for diagonal spline types. + if ( m_splineType == B_SPLINE || + m_splineType == DIAGONAL_B_SPLINE || + m_splineType == HUE_HUE_B_SPLINE ) + { + float lastY = -std::numeric_limits::max(); + if (m_splineType == HUE_HUE_B_SPLINE) + { + // The curve is diagonal but continues in a periodic way, so wrap the last point + // around and ensure the first point would preserve monotonicity. + lastY = m_controlPoints[numPoints - 1].m_y - 1.f; + } + + for (size_t i = 0; i < numPoints; ++i) + { + const float y = m_controlPoints[i].m_y; + if (y < lastY) + { + std::ostringstream oss; + oss << "Control point at index " << i << " has a y coordinate '" << y << "' that is "; + oss << "less than previous control point y coordinate '" << lastY << "'."; + throw Exception(oss.str().c_str()); + } + lastY = y; + } + } + // Don't allow only x values of 0 and 1 for periodic curves (since they are essentially only one point). if (numPoints == 2) { // Periodic curve types only. - if( m_splineType == PERIODIC_1_B_SPLINE || - m_splineType == PERIODIC_0_B_SPLINE || - m_splineType == HUE_HUE_B_SPLINE ) + if ( m_splineType == PERIODIC_1_B_SPLINE || + m_splineType == PERIODIC_0_B_SPLINE || + m_splineType == HUE_HUE_B_SPLINE ) { const float del_x = m_controlPoints[1].m_x - m_controlPoints[0].m_x; if (std::abs(1.f - del_x) < 1e-3) diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurve.cpp index 196988a8e7..d223c5315b 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurve.cpp @@ -104,6 +104,15 @@ void GradingRGBCurveImpl::validate() const << "with: " << e.what(); throw Exception(oss.str().c_str()); } + + if (m_curves[c]->getSplineType() != B_SPLINE) + { + // TODO: Allow use of the hue curve diagonal spline types? + std::ostringstream oss; + oss << "GradingRGBCurve validation failed: '" << CurveType(c) << "' curve " + << "is of the wrong BSplineType."; + throw Exception(oss.str().c_str()); + } } } diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index 4778f0acc0..11d463b656 100644 --- a/tests/cpu/Config_tests.cpp +++ b/tests/cpu/Config_tests.cpp @@ -4650,7 +4650,7 @@ OCIO_ADD_TEST(Config, grading_huecurve_serialization) " children:\n" " - !\n" " style: log\n" - " hue_hue: {control_points: [0, 0, 0.5, 0.5, 1, 1.123456]}\n" + " hue_hue: {control_points: [0, 0.15, 0.5, 0.5, 1, 1.123456]}\n" " - !\n" " style: log\n" " hue_sat: {control_points: [0, 0, 0.5, 0.5, 1, 1.5]}\n" @@ -4663,7 +4663,7 @@ OCIO_ADD_TEST(Config, grading_huecurve_serialization) " lum_lum: {control_points: [-1, -1, 0, 0.1, 0.5, 0.6, 1, 1.1]}\n" " - !\n" " style: video\n" - " hue_hue: {control_points: [-0.2, 0, 0.5, 0.5, 1.2, 1.5]}\n" + " hue_hue: {control_points: [0.02, -0.1, 0.5, 0.5, 0.9, 0.8]}\n" " hue_lum: {control_points: [0, 0, 0.2, 0.5, 1, 1.5]}\n" " lum_sat: {control_points: [0, 0, 0.1, 0.5, 1, 1.5], slopes: [0, 1, 1.1]}\n" " sat_lum: {control_points: [-1, -1, 0, 0.1, 0.5, 0.6, 1, 1.1]}\n" diff --git a/tests/cpu/DynamicProperty_tests.cpp b/tests/cpu/DynamicProperty_tests.cpp index fbf6fe87f1..ce00daed79 100644 --- a/tests/cpu/DynamicProperty_tests.cpp +++ b/tests/cpu/DynamicProperty_tests.cpp @@ -257,6 +257,55 @@ OCIO_ADD_TEST(DynamicPropertyImpl, equal_grading_rgb_curve) OCIO_CHECK_ASSERT(!(*dp0 == *dpImplDouble)); } +OCIO_ADD_TEST(DynamicPropertyImpl, setter_validation) +{ + // Make an identity dynamic transform. + OCIO::GradingHueCurveTransformRcPtr gct = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); + gct->makeDynamic(); + + // Apply it on CPU. + OCIO::ConfigRcPtr config = OCIO::Config::Create(); + OCIO::ConstProcessorRcPtr processor = config->getProcessor(gct); + OCIO::ConstCPUProcessorRcPtr cpuProcessor = processor->getDefaultCPUProcessor(); + + float pixel[3] = { 0.4f, 0.3f, 0.2f }; + cpuProcessor->applyRGB(pixel); + + const float error = 1e-5f; + OCIO_CHECK_CLOSE(pixel[0], pixel[0], error); + OCIO_CHECK_CLOSE(pixel[1], pixel[1], error); + OCIO_CHECK_CLOSE(pixel[2], pixel[2], error); + + // Get a handle to the dynamic property. + OCIO::DynamicPropertyRcPtr dp; + OCIO_CHECK_NO_THROW(dp = cpuProcessor->getDynamicProperty(OCIO::DYNAMIC_PROPERTY_GRADING_HUECURVE)); + auto dpVal = OCIO::DynamicPropertyValue::AsGradingHueCurve(dp); + OCIO_REQUIRE_ASSERT(dpVal); + + // Set a non-identity value. + OCIO::GradingHueCurveRcPtr hueCurve = dpVal->getValue()->createEditableCopy(); + OCIO::GradingBSplineCurveRcPtr huehue = hueCurve->getCurve(OCIO::HUE_HUE); + huehue->setNumControlPoints(3); + huehue->getControlPoint(0) = OCIO::GradingControlPoint(0.f, -0.1f); + huehue->getControlPoint(1) = OCIO::GradingControlPoint(0.5f, 0.5f); + huehue->getControlPoint(2) = OCIO::GradingControlPoint(0.8f, 0.8f); + dpVal->setValue(hueCurve); + cpuProcessor->applyRGB(pixel); + + OCIO_CHECK_CLOSE(pixel[0], 0.4385873675f, error); + OCIO_CHECK_CLOSE(pixel[1], 0.2829087377f, error); + OCIO_CHECK_CLOSE(pixel[2], 0.2556785941f, error); + + // Ensure that validation of control points is happening as expected. Set the last point + // so that it is no longer monotonic with respect to the first point. Because it is periodic, + // the last point Y value becomes -0.05 when wrapped around to an X value of -0.2. + huehue->getControlPoint(2) = OCIO::GradingControlPoint(0.8f, 0.95f); + OCIO_CHECK_THROW_WHAT(dpVal->setValue(hueCurve), + OCIO::Exception, + "GradingHueCurve validation failed for 'hue_hue' curve with: Control point at index 0 " + "has a y coordinate '-0.1' that is less than previous control point y coordinate '-0.05'."); +} + OCIO_ADD_TEST(DynamicPropertyImpl, grading_rgb_curve_knots_coefs) { auto curve11 = OCIO::GradingBSplineCurve::Create({ { 0.f, 10.f },{ 2.f, 10.f },{ 3.f, 10.f }, @@ -431,8 +480,8 @@ void checkKnotsAndCoefs( OCIO_ADD_TEST(DynamicPropertyImpl, grading_hue_curve_knots_coefs) { - auto hh = OCIO::GradingBSplineCurve::Create( - { {-0.1f, -0.15f}, {0.2f, 0.3f}, {0.5f, 0.25f}, {0.8f, 0.7f}, {0.85f, 0.8f}, {1.05f, 0.9f} }, + auto hh = OCIO::GradingBSplineCurve::Create( + { {0.1f, 0.05f}, {0.2f, 0.3f}, {0.5f, 0.4f}, {0.8f, 0.7f}, {0.9f, 0.75f}, {1.0f, 0.9f} }, OCIO::HUE_HUE); auto hs = OCIO::GradingBSplineCurve::Create( { {-0.15f, 1.25f}, {0.f, 0.8f}, {0.2f, 0.9f}, {0.4f, 1.8f}, {0.6f, 1.4f}, {0.8f, 1.3f}, {0.9f, 1.1f}, {1.1f, 0.7f} }, @@ -505,20 +554,20 @@ OCIO_ADD_TEST(DynamicPropertyImpl, grading_hue_curve_knots_coefs) { // Hue-Hue + const float true_knots[15] = {-0.1f, -0.06928571f, 0.0f, 0.05642857f, 0.1f, 0.17549634f, 0.2f, 0.33714286f, + 0.5f, 0.62499860f, 0.8f, 0.85261905f, 0.9f, 0.93071429f, 1.f }; - const float true_knots[15] = {-0.1f, -0.01816964f, 0.05f, 0.125f, 0.2f, 0.35f, 0.5f, 0.57558291f, 0.8f, 0.82731918f, - 0.85f, 0.87808036f, 0.9f, 0.98183036f, 1.05f }; // Quadratic coefs. - const float true_coefsA[14] = { -2.29385141e+00f, 3.43265663e+00f, 2.95979024e+01f, -3.34850652e+01f, -2.11943922e-02f, - 2.11186821e-02f, 9.58311056e+00f, 3.05897301e-01f, 1.69849435e+01f, -2.62364216e+01f, - -5.36565978e+00f, -1.21350984e+01f, -2.29385141e+00f, 3.43265663e+00f}; + const float true_coefsA[14] = { 15.95930233f, -1.66237113f, -1.44778481f, 6.17827869f, 10.39930009f, + -58.70626575f, -1.54375789f, 1.03834397f, 3.7077401f, -2.12344738f, + -3.54260935f, 4.81365159f, 15.95930233f,-1.66237113f }; // Linear coefs. - const float true_coefsB[14] = { 5.00000000e-01f, 1.24586640e-01f, 5.92592593e-01f, 5.03227795e+00f, 9.51817043e-03f, - 3.15985276e-03f, 9.49545739e-03f, 1.45813415e+00f, 1.59543132e+00f, 2.52346065e+00f, - 1.33333333e+00f, 1.03199405e+00f, 5.00000000e-01f, 1.24586640e-01f }; + const float true_coefsB[14] = { 0.75f, 1.73035714f, 1.5f, 1.33660714f, 1.875f, 3.44521825f, 0.56818182f, + 0.14475108f, 0.48295455f, 1.40987919f, 0.66666667f, 0.29384921f, 0.75f, 1.73035714f }; + // Constant coefs. - const float true_coefsC[14] = { -0.15f, -0.12444493f, -0.1f, 0.11093265f, 0.3f, 0.30095085f, 0.3019f, 0.35736386f, - 0.7f, 0.75626237f, 0.8f, 0.83320962f, 0.85f, 0.87555507f }; + const float true_coefsC[14] = { -0.25f, -0.2119088f, -0.1f, -0.01996716f, 0.05f, 0.25082851f, 0.3f, + 0.34888683f, 0.4f, 0.51830078f, 0.7f, 0.72527072f, 0.75f, 0.7880912f }; checkKnotsAndCoefs(dp, 0, true_knots, true_coefsA, true_coefsB, true_coefsC, __LINE__); } diff --git a/tests/cpu/fileformats/FileFormatCTF_tests.cpp b/tests/cpu/fileformats/FileFormatCTF_tests.cpp index 538bc6d8b9..ecacc1cdac 100644 --- a/tests/cpu/fileformats/FileFormatCTF_tests.cpp +++ b/tests/cpu/fileformats/FileFormatCTF_tests.cpp @@ -4514,7 +4514,7 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_rgbcurves_log) - 0.015625 1 + 0.015625 0.1 2.5 0.5 3.5 1.5 @@ -4528,7 +4528,7 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_rgbcurves_log) - 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + 11 11 12.5 11.5 13.5 12.5 26.5 15 @@ -4567,11 +4567,11 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_rgbcurves_log) OCIO_CHECK_EQUAL(master->getControlPoint(0).m_x, 11.0f); OCIO_CHECK_EQUAL(master->getControlPoint(0).m_y, 11.0f); OCIO_CHECK_EQUAL(master->getControlPoint(1).m_x, 12.5f); - OCIO_CHECK_EQUAL(master->getControlPoint(1).m_y, 10.5f); + OCIO_CHECK_EQUAL(master->getControlPoint(1).m_y, 11.5f); OCIO_CHECK_EQUAL(master->getControlPoint(2).m_x, 13.5f); - OCIO_CHECK_EQUAL(master->getControlPoint(2).m_y, 0.5f); + OCIO_CHECK_EQUAL(master->getControlPoint(2).m_y, 12.5f); OCIO_CHECK_EQUAL(master->getControlPoint(3).m_x, 26.5f); - OCIO_CHECK_EQUAL(master->getControlPoint(3).m_y, -1.5f); + OCIO_CHECK_EQUAL(master->getControlPoint(3).m_y, 15.f); } OCIO_ADD_TEST(FileFormatCTF, load_grading_huecurves_log) @@ -4583,47 +4583,50 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_huecurves_log) 0.015625 0 - 0.5 0.5 - 2 2 + 0.5 0.6 + 0.9 0.8 0.015625 1 - 2.5 0.5 - 3.5 1.5 + 0.5 0.5 + 0.9 1.5 - -4 -4 - 4.5 0.5 - 5 3 + 0.1, 1.5, 0.2, 0.7, 0.4, 1.4, 0.5, 0.8, 0.8, 0.5 - 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + -0.1 1.0 + 0.5 1.5 + 1.0 0.9 + 1.1 1.2 - 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + 0., 0.1, 0.5, 0.45, 1., 1.1 - 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + 0. -0.0005 + 0.5 0.3 + 1. 0.9 - 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + 0., 1.2, 0.6, 0.8, 0.9, 1.1 - 11 11 12.5 10.5 13.5 0.5 26.5 -1.5 + 0.2, 0.05, .4, -0.09, .6, -0.2, .8, 0.05, 0.99, -0.02 @@ -4653,20 +4656,29 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_huecurves_log) OCIO_CHECK_EQUAL(hh->getControlPoint(0).m_x, 0.015625f); OCIO_CHECK_EQUAL(hh->getControlPoint(0).m_y, 0.0f); OCIO_CHECK_EQUAL(hh->getControlPoint(1).m_x, 0.5f); - OCIO_CHECK_EQUAL(hh->getControlPoint(1).m_y, 0.5f); - OCIO_CHECK_EQUAL(hh->getControlPoint(2).m_x, 2.0f); - OCIO_CHECK_EQUAL(hh->getControlPoint(2).m_y, 2.0f); + OCIO_CHECK_EQUAL(hh->getControlPoint(1).m_y, 0.6f); + OCIO_CHECK_EQUAL(hh->getControlPoint(2).m_x, 0.9f); + OCIO_CHECK_EQUAL(hh->getControlPoint(2).m_y, 0.8f); auto ls = curves->getCurve(OCIO::LUM_SAT); OCIO_CHECK_EQUAL(ls->getNumControlPoints(), 4); - OCIO_CHECK_EQUAL(ls->getControlPoint(0).m_x, 11.0f); - OCIO_CHECK_EQUAL(ls->getControlPoint(0).m_y, 11.0f); - OCIO_CHECK_EQUAL(ls->getControlPoint(1).m_x, 12.5f); - OCIO_CHECK_EQUAL(ls->getControlPoint(1).m_y, 10.5f); - OCIO_CHECK_EQUAL(ls->getControlPoint(2).m_x, 13.5f); - OCIO_CHECK_EQUAL(ls->getControlPoint(2).m_y, 0.5f); - OCIO_CHECK_EQUAL(ls->getControlPoint(3).m_x, 26.5f); - OCIO_CHECK_EQUAL(ls->getControlPoint(3).m_y, -1.5f); + OCIO_CHECK_EQUAL(ls->getControlPoint(0).m_x, -0.1f); + OCIO_CHECK_EQUAL(ls->getControlPoint(0).m_y, 1.0f); + OCIO_CHECK_EQUAL(ls->getControlPoint(1).m_x, 0.5f); + OCIO_CHECK_EQUAL(ls->getControlPoint(1).m_y, 1.5f); + OCIO_CHECK_EQUAL(ls->getControlPoint(2).m_x, 1.0f); + OCIO_CHECK_EQUAL(ls->getControlPoint(2).m_y, 0.9f); + OCIO_CHECK_EQUAL(ls->getControlPoint(3).m_x, 1.1f); + OCIO_CHECK_EQUAL(ls->getControlPoint(3).m_y, 1.2f); + + auto sl = curves->getCurve(OCIO::SAT_LUM); + OCIO_CHECK_EQUAL(sl->getNumControlPoints(), 3); + OCIO_CHECK_EQUAL(sl->getControlPoint(0).m_x, 0.0f); + OCIO_CHECK_EQUAL(sl->getControlPoint(0).m_y, 1.2f); + OCIO_CHECK_EQUAL(sl->getControlPoint(1).m_x, 0.6f); + OCIO_CHECK_EQUAL(sl->getControlPoint(1).m_y, 0.8f); + OCIO_CHECK_EQUAL(sl->getControlPoint(2).m_x, 0.9f); + OCIO_CHECK_EQUAL(sl->getControlPoint(2).m_y, 1.1f); } OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_errors) @@ -4750,7 +4762,7 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_errors) )"), OCIO::Exception, "Control point at index 2 has a x coordinate '-1' that is less than " - "previous control point x cooordinate '0'"); + "previous control point x coordinate '0'"); // Number of slopes matches control points. OCIO_CHECK_THROW_WHAT(ParseString(R"( diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp index 41bf26fe76..1cdd83b9e2 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp @@ -159,12 +159,34 @@ OCIO_ADD_TEST(GradingHueCurveOpData, validate) OCIO_CHECK_THROW_WHAT(auto curves = OCIO::GradingHueCurve::Create( curve, curve, curve, curve, curve, curve, curve, curve), OCIO::Exception, "has a x coordinate '0.5' that is less than previous control " - "point x cooordinate '0.7'."); + "point x coordinate '0.7'."); + + // A hue-hue curve must have x-coordinates in [0,1]. + curve = OCIO::GradingBSplineCurve::Create({ { 0.1f,0.05f },{ 1.1f,1.05f } }, OCIO::HUE_HUE); + OCIO_CHECK_THROW_WHAT(auto curves = OCIO::GradingHueCurve::Create( + curve, curve, curve, curve, curve, curve, curve, curve), + OCIO::Exception, "The HUE-HUE spline may not have x coordinates greater than one."); // Fix the curve x coordinate. - curve->getControlPoint(1).m_x = 0.3f; - // But the curves are not of the correct BSplineType. + curve->getControlPoint(1).m_x = 1.f; + // But the curves are not all of the correct BSplineType. + OCIO_CHECK_THROW_WHAT(auto curves = OCIO::GradingHueCurve::Create( + curve, curve, curve, curve, curve, curve, curve, curve), + OCIO::Exception, "GradingHueCurve validation failed: 'hue_sat' curve is of the wrong BSplineType."); + + // Curve y coordinates have to increase. + curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.3f,0.3f }, + { 0.5f,0.27f },{ 1.f,1.f } }); + OCIO_CHECK_THROW_WHAT(auto curves = OCIO::GradingHueCurve::Create( + curve, curve, curve, curve, curve, curve, curve, curve), + OCIO::Exception, "point at index 2 has a y coordinate '0.27' that is less than " + "previous control point y coordinate '0.3'."); + + // Curve y coordinates have to increase. For hue-hue this includes comparing the wrapped last + // point to the first point. In this case, 1.1 at the end is equivalent to 0.1 at the start. + curve = OCIO::GradingBSplineCurve::Create({ { 0.1f,0.05f },{ 1.f,1.1f } }, OCIO::HUE_HUE); OCIO_CHECK_THROW_WHAT(auto curves = OCIO::GradingHueCurve::Create( curve, curve, curve, curve, curve, curve, curve, curve), - OCIO::Exception, "GradingHueCurve validation failed: 'hue_hue' curve is of the wrong BSplineType."); + OCIO::Exception, "Control point at index 0 has a y coordinate '0.05' that is less than " + "previous control point y coordinate '0.1'."); } diff --git a/tests/cpu/ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp b/tests/cpu/ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp index 2be380eeb4..b0f5a48c40 100644 --- a/tests/cpu/ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp +++ b/tests/cpu/ops/gradingrgbcurve/GradingBSplineCurve_tests.cpp @@ -72,7 +72,7 @@ OCIO_ADD_TEST(GradingBSplineCurve, validate) { 0.5f,0.7f },{ 1.f,1.f } }); OCIO_CHECK_THROW_WHAT(curve->validate(), OCIO::Exception, "has a x coordinate '0.5' that is less than previous control " - "point x cooordinate '0.7'."); + "point x coordinate '0.7'."); curve->getControlPoint(1).m_x = 0.3f; OCIO_CHECK_NO_THROW(curve->validate()); diff --git a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOpData_tests.cpp b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOpData_tests.cpp index 4063151167..38431dc170 100644 --- a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOpData_tests.cpp +++ b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOpData_tests.cpp @@ -145,11 +145,25 @@ OCIO_ADD_TEST(GradingRGBCurveOpData, validate) curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, "has a x coordinate '0.5' that is less than previous control " - "point x cooordinate '0.7'."); + "point x coordinate '0.7'."); // Fix the curve x coordinate. curve->getControlPoint(1).m_x = 0.3f; curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); OCIO_CHECK_NO_THROW(gc.setValue(curves)); OCIO_CHECK_NO_THROW(gc.validate()); + + // Curve y coordinates have to increase. + curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.3f,0.3f }, + { 0.5f,0.27f },{ 1.f,1.f } }); + curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); + OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, + "point at index 2 has a y coordinate '0.27' that is less than " + "previous control point y coordinate '0.3'."); + + // Curve must use the proper spline type. + curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.f },{ 0.9f,0.f } }, OCIO::HUE_FX); + curves = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); + OCIO_CHECK_THROW_WHAT(gc.setValue(curves), OCIO::Exception, + "validation failed: 'red' curve is of the wrong BSplineType."); } diff --git a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOp_tests.cpp b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOp_tests.cpp index fbdd4a5b93..755a047f22 100644 --- a/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOp_tests.cpp +++ b/tests/cpu/ops/gradingrgbcurve/GradingRGBCurveOp_tests.cpp @@ -101,7 +101,7 @@ OCIO_ADD_TEST(GradingRGBCurveOp, build_ops) // Sharing of dynamic properties is done through processor, changing the source will not // change the op. - auto curve = OCIO::GradingBSplineCurve::Create({ { 0.f,1.f },{ 0.2f,0.3f }, + auto curve = OCIO::GradingBSplineCurve::Create({ { 0.f,0.1f },{ 0.2f,0.3f }, { 0.5f,0.8f },{ 2.f,1.5f } }); auto rgbCurve = OCIO::GradingRGBCurve::Create(curve, curve, curve, curve); gcTransform->setValue(rgbCurve); @@ -131,7 +131,7 @@ OCIO_ADD_TEST(GradingRGBCurveOp, build_ops) // Control point has moved. cpu->applyRGB(pixel); - OCIO_CHECK_CLOSE(pixel[0], 1.11148262f, error); - OCIO_CHECK_CLOSE(pixel[1], 0.04518771f, error); + OCIO_CHECK_CLOSE(pixel[0], 0.18597151f, error); + OCIO_CHECK_CLOSE(pixel[1], 0.47056902f, error); OCIO_CHECK_CLOSE(pixel[2], 1.32527864f, error); } diff --git a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp index 4508553159..7e0923d928 100644 --- a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp +++ b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp @@ -84,14 +84,28 @@ OCIO_ADD_TEST(GradingHueCurveTransform, basic) "There are '3' control points. '4' is out of bounds."); // X-coordinate has to be increasing. - OCIO::GradingHueCurveTransformRcPtr hct = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_VIDEO); - OCIO::GradingHueCurveRcPtr hueCurve = hct->getValue()->createEditableCopy(); - OCIO::GradingBSplineCurveRcPtr lumsat = hueCurve->getCurve(OCIO::LUM_SAT); - OCIO::GradingControlPoint & cp = lumsat->getControlPoint(0); - cp = OCIO::GradingControlPoint(0.7f, 1.f); - OCIO_CHECK_THROW_WHAT(hct->setValue(hueCurve), OCIO::Exception, - "has a x coordinate '0.5' that is less than previous control " - "point x cooordinate '0.7'."); + { + OCIO::GradingHueCurveTransformRcPtr hct = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_VIDEO); + OCIO::GradingHueCurveRcPtr hueCurve = hct->getValue()->createEditableCopy(); + OCIO::GradingBSplineCurveRcPtr lumsat = hueCurve->getCurve(OCIO::LUM_SAT); + OCIO::GradingControlPoint & cp = lumsat->getControlPoint(0); + cp = OCIO::GradingControlPoint(0.7f, 1.f); + OCIO_CHECK_THROW_WHAT(hct->setValue(hueCurve), OCIO::Exception, + "has a x coordinate '0.5' that is less than previous control " + "point x coordinate '0.7'."); + } + + // Y-coordinate has to be increasing, for diagonal curves. + { + OCIO::GradingHueCurveTransformRcPtr hct = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_VIDEO); + OCIO::GradingHueCurveRcPtr hueCurve = hct->getValue()->createEditableCopy(); + OCIO::GradingBSplineCurveRcPtr lumlum = hueCurve->getCurve(OCIO::LUM_LUM); + OCIO::GradingControlPoint & cp = lumlum->getControlPoint(0); + cp = OCIO::GradingControlPoint(0.f, 0.6f); + OCIO_CHECK_THROW_WHAT(hct->setValue(hueCurve), OCIO::Exception, + "has a y coordinate '0.5' that is less than previous control " + "point y coordinate '0.6'."); + } // Check slopes. gct->setSlope(OCIO::LUM_LUM, 2, 0.9f); @@ -228,7 +242,7 @@ OCIO_ADD_TEST(GradingHueCurveTransform, serialization) // Test the serialization of the transform. auto hh = OCIO::GradingBSplineCurve::Create( - { {-0.1f, -0.15f}, {0.2f, 0.3f}, {0.5f, 0.25f}, {0.8f, 0.7f}, {0.85f, 0.8f}, {1.05f, 0.9f} }, + { {0.1f, -0.05f}, {0.2f, 0.23f}, {0.5f, 0.25f}, {0.8f, 0.7f}, {0.85f, 0.8f}, {0.95f, 0.9f} }, OCIO::HUE_HUE); auto hs = OCIO::GradingBSplineCurve::Create( { {0.f, 1.2f}, {0.1f, 1.2f}, {0.4f, 0.7f}, {0.6f, 0.3f}, {0.8f, 0.5f}, {0.9f, 0.8f} }, @@ -266,8 +280,8 @@ OCIO_ADD_TEST(GradingHueCurveTransform, serialization) static constexpr char CURVE_STR[] = "" - "]>, hue_sat=" + "" + "]>, hue_sat=" "]>, hue_lum=]>, lum_sat=" "]>, sat_sat=setValue(newCurve), OCIO::Exception, "has a x coordinate '0.2' that is less than previous control " - "point x cooordinate '0.5'."); + "point x coordinate '0.5'."); // Check slopes. gct->setSlope(OCIO::RGB_BLUE, 2, 0.9f); From 32fe77b69bdf90c6ae50ae9cd5143881fcc91967 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Fri, 26 Sep 2025 22:18:27 -0400 Subject: [PATCH 28/30] Update API calls based on Vulkan PR Signed-off-by: Doug Walker --- .../gradinghuecurve/GradingHueCurveOpGPU.cpp | 18 ++++++++++-------- .../gradingrgbcurve/GradingRGBCurveOpGPU.cpp | 6 ++++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp index 39016a4aa4..2c3cc36e93 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp @@ -90,12 +90,12 @@ void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, const std::string & name) { // Add the uniform if it does not already exist. - if (shaderCreator->addUniform(name.c_str(), getSize, getVector)) + if (shaderCreator->addUniform(name.c_str(), getSize, getVector, maxSize)) { // Declare uniform. GpuShaderText stDecl(shaderCreator->getLanguage()); stDecl.declareUniformArrayFloat(name, maxSize); - shaderCreator->addToDeclareShaderCode(stDecl.string().c_str()); + shaderCreator->addToParameterDeclareShaderCode(stDecl.string().c_str()); } } @@ -104,14 +104,16 @@ void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, const GpuShaderCreator::VectorIntGetter & getVector, const std::string & name) { + static constexpr unsigned arrayLen = 16u; // 8 curves x 2 values (count and offset) + // Add the uniform if it does not already exist. - if (shaderCreator->addUniform(name.c_str(), getSize, getVector)) + if (shaderCreator->addUniform(name.c_str(), getSize, getVector, arrayLen)) { // Declare uniform. GpuShaderText stDecl(shaderCreator->getLanguage()); // Need 2 ints for each curve. - stDecl.declareUniformArrayInt(name, 16); // TODO: Avoid magic numbers (8 Curves * 2 values) - shaderCreator->addToDeclareShaderCode(stDecl.string().c_str()); + stDecl.declareUniformArrayInt(name, arrayLen); + shaderCreator->addToParameterDeclareShaderCode(stDecl.string().c_str()); } } @@ -125,7 +127,7 @@ void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, // Declare uniform. GpuShaderText stDecl(shaderCreator->getLanguage()); stDecl.declareUniformBool(name); - shaderCreator->addToDeclareShaderCode(stDecl.string().c_str()); + shaderCreator->addToParameterDeclareShaderCode(stDecl.string().c_str()); } } @@ -327,7 +329,7 @@ void AddGCForwardShader(GpuShaderCreatorRcPtr & shaderCreator, if (dyn) { - st.newLine() << "if (!" << props.m_localBypass << ")"; + st.newLine() << "if (!" << st.castToBool(props.m_localBypass) << ")"; st.newLine() << "{"; st.indent(); } @@ -470,7 +472,7 @@ void AddGCInverseShader(GpuShaderCreatorRcPtr & shaderCreator, if (dyn) { - st.newLine() << "if (!" << props.m_localBypass << ")"; + st.newLine() << "if (!" << st.castToBool(props.m_localBypass) << ")"; st.newLine() << "{"; st.indent(); } diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp index 16891e77c0..f368358226 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp @@ -100,13 +100,15 @@ void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, const GpuShaderCreator::VectorIntGetter & getVector, const std::string & name) { + static constexpr unsigned arrayLen = 8u; // 4 curves x 2 values (count and offset) + // Add the uniform if it does not already exist. - if (shaderCreator->addUniform(name.c_str(), getSize, getVector, 8)) + if (shaderCreator->addUniform(name.c_str(), getSize, getVector, arrayLen)) { // Declare uniform. GpuShaderText stDecl(shaderCreator->getLanguage()); // Need 2 ints for each RGBM curve. - stDecl.declareUniformArrayInt(name, 8); + stDecl.declareUniformArrayInt(name, arrayLen); shaderCreator->addToParameterDeclareShaderCode(stDecl.string().c_str()); } } From 5f7c012a8e7e1ea23cd48167d58ccadc9d046bb7 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Tue, 30 Sep 2025 00:35:02 -0400 Subject: [PATCH 29/30] Saturation robustness Signed-off-by: Doug Walker --- .../ops/fixedfunction/FixedFunctionOpCPU.cpp | 12 +++++++++--- .../ops/fixedfunction/FixedFunctionOpGPU.cpp | 6 ++++-- tests/cpu/fileformats/FileFormatCTF_tests.cpp | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp index 8140d6e472..ed7fcb362e 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp @@ -1625,8 +1625,14 @@ void applyHSYToRGB(const void * inImg, void * outImg, long numPixels, FixedFunct const float loGain = 5.f; sat /= 1.4f; - const float tmp = -sat * sumRgb + sat * 3.f * luma + distRgb; - const float s1 = (tmp == 0.f) ? 0.f : sat * (k + 3.f * luma) / tmp; + float tmp = -sat * sumRgb + sat * 3.f * luma + distRgb; + // Don't allow tmp to go negative, which would cause a negative gainS. + tmp = std::max( 1e-6f, tmp ); + + float s1 = sat * (k + 3.f * luma) / tmp; + // Prevent gainS from becoming too extreme. + s1 = std::min(s1, 50.f ); + const float s0 = sat / std::max(1e-10f, distRgb * loGain); const float maxLum = 0.01f; @@ -1702,7 +1708,7 @@ void applyRGBToHSY(const void * inImg, void * outImg, long numPixels, FixedFunct { const float sumRgb = red + grn + blu; const float k = 0.15f; - const float satHi = distRgb / (k + sumRgb); + const float satHi = distRgb / std::max( 0.07f * distRgb + 1e-6f, k + sumRgb); const float loGain = 5.f; const float satLo = distRgb * loGain; const float maxLum = 0.01f; diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp index 49b8aba90c..0ead67f67c 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp @@ -1708,7 +1708,7 @@ void Add_RGB_TO_HSY(GpuShaderCreatorRcPtr & shaderCreator, if (funcStyle == FixedFunctionOpData::RGB_TO_HSY_LIN) { ss.newLine() << "float sumRGB = dot( " << pxl << ".rgb, ones );"; - ss.newLine() << "float sat_hi = distRGB / (0.15 + sumRGB);"; + ss.newLine() << "float sat_hi = distRGB / max(0.07 * distRGB + 1e-6, 0.15 + sumRGB);"; ss.newLine() << "float sat_lo = distRGB * 5.;"; ss.newLine() << "float alpha = clamp( (luma - 0.001) / (0.01 - 0.001), 0., 1.);"; @@ -1765,7 +1765,9 @@ void Add_HSY_TO_RGB(GpuShaderCreatorRcPtr & shaderCreator, ss.newLine() << "float lo_gain = 5.;"; ss.newLine() << "sat /= 1.4;"; ss.newLine() << "float tmp = -sat * sumRGB + sat * 3. * luma + distRGB;"; - ss.newLine() << "float s1 = (tmp == 0.) ? 0. : sat * (k + 3. * luma) / tmp;"; + ss.newLine() << "tmp = max(1e-6, tmp);"; + ss.newLine() << "float s1 = sat * (k + 3. * luma) / tmp;"; + ss.newLine() << "s1 = min(s1, 50.);"; ss.newLine() << "float s0 = sat / max(1e-10, distRGB * lo_gain);"; ss.newLine() << "float alpha = clamp( (luma - 0.001) / (0.01 - 0.001), 0., 1.);"; ss.newLine() << "float a = distRGB * lo_gain * (1. - alpha) * (sumRGB - 3. * luma);"; diff --git a/tests/cpu/fileformats/FileFormatCTF_tests.cpp b/tests/cpu/fileformats/FileFormatCTF_tests.cpp index df51cd49d1..30c8bc5d2f 100644 --- a/tests/cpu/fileformats/FileFormatCTF_tests.cpp +++ b/tests/cpu/fileformats/FileFormatCTF_tests.cpp @@ -7283,7 +7283,7 @@ OCIO_ADD_TEST(CTFTransform, grading_huecurve_lin_ctf) // Note: The default curves are not saved. OCIO::GradingHueCurveRcPtr curves = OCIO::GradingHueCurve::Create(OCIO::GRADING_LIN); - curves->getCurve(OCIO::LUM_SAT)->getControlPoint(0).m_y = 1.1; + curves->getCurve(OCIO::LUM_SAT)->getControlPoint(0).m_y = 1.1f; curves->getCurve(OCIO::LUM_LUM)->setNumControlPoints(4); curves->getCurve(OCIO::LUM_LUM)->getControlPoint(3).m_x = 16.f; curves->getCurve(OCIO::LUM_LUM)->getControlPoint(3).m_y = 10.f; From 91bffc9fcccb79e9120e331737f9a7f8ec19abf5 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Tue, 30 Sep 2025 22:08:12 -0400 Subject: [PATCH 30/30] HSY transform setter adjustment Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTransforms.h | 8 ++--- include/OpenColorIO/OpenColorTypes.h | 36 +++++++++++-------- src/OpenColorIO/OCIOYaml.cpp | 18 ++++++---- .../fileformats/ctf/CTFReaderHelper.cpp | 10 +++--- .../fileformats/ctf/CTFReaderUtils.h | 2 +- .../fileformats/ctf/CTFTransform.cpp | 4 +-- .../gradinghuecurve/GradingHueCurveOpCPU.cpp | 2 +- .../gradinghuecurve/GradingHueCurveOpData.cpp | 8 ++--- .../gradinghuecurve/GradingHueCurveOpData.h | 6 ++-- .../gradinghuecurve/GradingHueCurveOpGPU.cpp | 5 +-- .../gradingrgbcurve/GradingRGBCurveOpGPU.cpp | 3 +- .../transforms/GradingHueCurveTransform.cpp | 12 +++---- .../transforms/GradingHueCurveTransform.h | 4 +-- .../transforms/GradingRGBCurveTransform.cpp | 4 +++ src/bindings/python/PyTypes.cpp | 10 ++++++ .../transforms/PyGradingHueCurveTransform.cpp | 8 ++--- tests/cpu/Config_tests.cpp | 16 ++++++++- tests/cpu/fileformats/FileFormatCTF_tests.cpp | 16 ++++++--- .../GradingHueCurveOpCPU_tests.cpp | 2 +- .../GradingHueCurveOpData_tests.cpp | 6 ++-- .../GradingHueCurveTransform_tests.cpp | 10 +++--- tests/gpu/GradingHueCurveOp_test.cpp | 2 +- tests/python/GradingHueCurveTransformTest.py | 10 +++--- 23 files changed, 126 insertions(+), 76 deletions(-) diff --git a/include/OpenColorIO/OpenColorTransforms.h b/include/OpenColorIO/OpenColorTransforms.h index 0818f83c0c..8ef04be8c8 100644 --- a/include/OpenColorIO/OpenColorTransforms.h +++ b/include/OpenColorIO/OpenColorTransforms.h @@ -1361,11 +1361,11 @@ class OCIOEXPORT GradingHueCurveTransform : public Transform /** * By default, the input is transformed into HSY space to apply the hue curves and then the result is - * transformed back to RGB. However, this may be bypassed to use other hue/sat/luma type transforms - * applied separately before and after this transform. + * transformed back to RGB. However, this may be set to HSY_TRANSFORM_NONE to bypass this in order to + * use other hue/sat/luma type transforms applied separately before and after this transform. */ - virtual bool getBypassRGBToHSY() const = 0; - virtual void setBypassRGBToHSY(bool bypass) = 0; + virtual HSYTransformStyle getRGBToHSY() const = 0; + virtual void setRGBToHSY(HSYTransformStyle style) = 0; ///** // * Parameters can be made dynamic so the values can be changed through the CPU or GPU processor, diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index b0f5f3f1cd..a995c849a2 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -583,25 +583,33 @@ enum RGBCurveType /// Types for GradingHueCurve. enum HueCurveType { - HUE_HUE = 0, - HUE_SAT, - HUE_LUM, - LUM_SAT, - SAT_SAT, - LUM_LUM, - SAT_LUM, - HUE_FX, + HUE_HUE = 0, //!< Map input hue to output hue (where a diagonal line is the identity). + HUE_SAT, //!< Adjust saturation as a function of hue (a value of 1.0 is the identity). + HUE_LUM, //!< Adjust luma as a function of hue (a value of 1.0 is the identity). + LUM_SAT, //!< Adjust saturation as a function of luma (a value of 1.0 is the identity). + SAT_SAT, //!< Adjust saturation as a function of saturation (a diagonal is the identity). + LUM_LUM, //!< Adjust luma as a function of luma, maintaining hue & sat (diagonal is identity). + SAT_LUM, //!< Adjust luma as a function of saturation (a value of 1.0 is the identity). + HUE_FX, //!< Map input hue to delta output hue (a value of 0.0 is the identity). HUE_NUM_CURVES }; +/// Types for GradingHueCurve. +enum HSYTransformStyle +{ + HSY_TRANSFORM_NONE = 0, //!< No RGB to HSY conversion (use an outboard conversion). + HSY_TRANSFORM_1 //!< Default RGB to Hue, Saturation, Luma conversion. +}; + +/// Types for GradingBSplineCurve. enum BSplineType { - B_SPLINE = 0, //!< Monotonic quadratic B-spline used for the RGBM curves. - DIAGONAL_B_SPLINE, //!< Monotonic quadratic B-spline for the sat-sat and lum-lum curves. - HUE_HUE_B_SPLINE, //!< Monotonic and periodic B-spline used for the hue-hue curve. - PERIODIC_1_B_SPLINE, //!< Periodic, horizontal (at 1) B-spline for hue-sat and hue-lum curves. - PERIODIC_0_B_SPLINE, //!< Periodic, horizontal (at 0) B-spline used for the hue-fx curve. - HORIZONTAL1_B_SPLINE, //!< Horizontal (at 1) B-spline used for the lum-sat and sat-lum curves. + B_SPLINE = 0, //!< Monotonic quadratic B-spline used for the RGBM curves. + DIAGONAL_B_SPLINE, //!< Monotonic quadratic B-spline for the sat-sat and lum-lum curves. + HUE_HUE_B_SPLINE, //!< Monotonic and periodic B-spline used for the hue-hue curve. + PERIODIC_1_B_SPLINE, //!< Periodic, horizontal (at 1) B-spline for hue-sat and hue-lum curves. + PERIODIC_0_B_SPLINE, //!< Periodic, horizontal (at 0) B-spline used for the hue-fx curve. + HORIZONTAL1_B_SPLINE, //!< Horizontal (at 1) B-spline used for the lum-sat and sat-lum curves. }; /// Types for uniform data. diff --git a/src/OpenColorIO/OCIOYaml.cpp b/src/OpenColorIO/OCIOYaml.cpp index 818299484a..c9361579fe 100644 --- a/src/OpenColorIO/OCIOYaml.cpp +++ b/src/OpenColorIO/OCIOYaml.cpp @@ -2178,11 +2178,15 @@ inline void load(const YAML::Node & node, GradingHueCurveTransformRcPtr & t) load(iter->second, val); t->setDirection(val); } - else if (key == "rgbtohsy_bypass") + else if (key == "hsy_transform") { - bool bypass = true; - load(iter->second, bypass); - t->setBypassRGBToHSY(bypass); + std::string value; + load(iter->second, value); + if (value != "none") + { + throwValueError(node.Tag(), iter->first, "Unknown hsy_transform value."); + } + t->setRGBToHSY(HSY_TRANSFORM_NONE); } else if (key == "hue_hue") { @@ -2280,10 +2284,10 @@ inline void save(YAML::Emitter & out, ConstGradingHueCurveTransformRcPtr t) out << YAML::Key << "style"; out << YAML::Value << YAML::Flow << GradingStyleToString(style); - if (t->getBypassRGBToHSY()) + if (t->getRGBToHSY() == HSY_TRANSFORM_NONE) { - out << YAML::Key << "rgbtohsy_bypass"; - out << YAML::Value << YAML::Flow << true; + out << YAML::Key << "hsy_transform"; + out << YAML::Value << YAML::Flow << "none"; } static const std::vector curveNames = { "hue_hue", "hue_sat", "hue_lum", diff --git a/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp b/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp index ba139739ac..79393fc489 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp @@ -2665,7 +2665,7 @@ bool CTFReaderGradingHueCurveElt::isOpParameterValid(const char * att) const noe { return CTFReaderOpElt::isOpParameterValid(att) || 0 == Platform::Strcasecmp(ATTR_STYLE, att) || - 0 == Platform::Strcasecmp(ATTR_BYPASS_RGB_TO_HSY, att); + 0 == Platform::Strcasecmp(ATTR_RGB_TO_HSY, att); } void CTFReaderGradingHueCurveElt::start(const char ** atts) @@ -2696,17 +2696,17 @@ void CTFReaderGradingHueCurveElt::start(const char ** atts) } isStyleFound = true; } - else if (0 == Platform::Strcasecmp(ATTR_BYPASS_RGB_TO_HSY, atts[i])) + else if (0 == Platform::Strcasecmp(ATTR_RGB_TO_HSY, atts[i])) { - if (0 != Platform::Strcasecmp("true", atts[i + 1])) + if (0 != Platform::Strcasecmp("none", atts[i + 1])) { std::ostringstream oss; - oss << "Unknown bypassRGBToHSY value: '" << atts[i + 1]; + oss << "Unknown hsyTransform value: '" << atts[i + 1]; oss << "' while parsing HueCurve."; throwMessage(oss.str()); } - m_gradingHueCurve->setBypassRGBToHSY(true); + m_gradingHueCurve->setRGBToHSY(HSYTransformStyle::HSY_TRANSFORM_NONE); } i += 2; diff --git a/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h b/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h index 68cf80f702..805e95bb12 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h +++ b/src/OpenColorIO/fileformats/ctf/CTFReaderUtils.h @@ -98,7 +98,7 @@ static constexpr char ATTR_BITDEPTH_IN[] = "inBitDepth"; static constexpr char ATTR_BITDEPTH_OUT[] = "outBitDepth"; static constexpr char ATTR_BYPASS[] = "bypass"; static constexpr char ATTR_BYPASS_LIN_TO_LOG[] = "bypassLinToLog"; -static constexpr char ATTR_BYPASS_RGB_TO_HSY[] = "bypassRGBToHSY"; +static constexpr char ATTR_RGB_TO_HSY[] = "hsyTransform"; static constexpr char ATTR_CENTER[] = "center"; static constexpr char ATTR_CHAN[] = "channel"; static constexpr char ATTR_COMP_CLF_VERSION[] = "compCLFversion"; diff --git a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp index c018d7ef6f..5a21b6cb1d 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp @@ -1682,9 +1682,9 @@ void GradingHueCurveWriter::getAttributes(XmlFormatter::Attributes& attributes) const auto styleStr = ConvertGradingStyleAndDirToString(style, dir); attributes.push_back(XmlFormatter::Attribute(ATTR_STYLE, styleStr)); - if (m_curves->getBypassRGBToHSY()) + if (m_curves->getRGBToHSY() == HSYTransformStyle::HSY_TRANSFORM_NONE) { - attributes.push_back(XmlFormatter::Attribute(ATTR_BYPASS_RGB_TO_HSY, "true")); + attributes.push_back(XmlFormatter::Attribute(ATTR_RGB_TO_HSY, "none")); } } diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp index d10e1037fd..c3728eb882 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp @@ -105,7 +105,7 @@ GradingHueCurveOpCPU::GradingHueCurveOpCPU(ConstGradingHueCurveOpDataRcPtr & gcD break; } - if (gcData->getBypassRGBToHSY()) + if (gcData->getRGBToHSY() == HSYTransformStyle::HSY_TRANSFORM_NONE) { // TODO: Could template the apply function to avoid the need for a matrix op. ConstMatrixOpDataRcPtr fwdOpData = std::make_shared(TRANSFORM_DIR_FORWARD); diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp index 69d970ade5..8d19439b2f 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.cpp @@ -67,7 +67,7 @@ GradingHueCurveOpData & GradingHueCurveOpData::operator=(const GradingHueCurveOp m_direction = rhs.m_direction; m_style = rhs.m_style; - m_bypassRGBToHSY = rhs.m_bypassRGBToHSY; + m_RGBToHSY = rhs.m_RGBToHSY; // Copy dynamic properties. Sharing happens when needed, with CPUOp for instance. m_value->setValue(rhs.m_value->getValue()); @@ -115,7 +115,7 @@ bool GradingHueCurveOpData::isInverse(ConstGradingHueCurveOpDataRcPtr & r) const } if (m_style == r->m_style && - (m_style != GRADING_LIN || m_bypassRGBToHSY == r->m_bypassRGBToHSY) && + (m_style != GRADING_LIN || m_RGBToHSY == r->m_RGBToHSY) && m_value->equals(*r->m_value)) { if (CombineTransformDirections(getDirection(), r->getDirection()) == TRANSFORM_DIR_INVERSE) @@ -147,7 +147,7 @@ std::string GradingHueCurveOpData::getCacheID() const cacheIDStream << GradingStyleToString(getStyle()) << " "; cacheIDStream << TransformDirectionToString(getDirection()) << " "; - if (m_bypassRGBToHSY) + if (m_RGBToHSY != HSY_TRANSFORM_1) { cacheIDStream << " bypassRGBToHSY "; } @@ -227,7 +227,7 @@ bool GradingHueCurveOpData::equals(const OpData & other) const if (m_direction != rop->m_direction || m_style != rop->m_style || - m_bypassRGBToHSY != rop->m_bypassRGBToHSY || + m_RGBToHSY != rop->m_RGBToHSY || !m_value->equals( *(rop->m_value) )) { return false; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h index 059b679250..665acfed56 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpData.h @@ -63,8 +63,8 @@ class GradingHueCurveOpData : public OpData void setSlope(HueCurveType c, size_t index, float slope); bool slopesAreDefault(HueCurveType c) const; - bool getBypassRGBToHSY() const noexcept { return m_bypassRGBToHSY; } - void setBypassRGBToHSY(bool bypass) noexcept { m_bypassRGBToHSY = bypass; } + HSYTransformStyle getRGBToHSY() const noexcept { return m_RGBToHSY; } + void setRGBToHSY(HSYTransformStyle style) noexcept { m_RGBToHSY = style; } TransformDirection getDirection() const noexcept; void setDirection(TransformDirection dir) noexcept; @@ -84,7 +84,7 @@ class GradingHueCurveOpData : public OpData private: GradingStyle m_style; DynamicPropertyGradingHueCurveImplRcPtr m_value; - bool m_bypassRGBToHSY{ false }; + HSYTransformStyle m_RGBToHSY{ HSYTransformStyle::HSY_TRANSFORM_1 }; TransformDirection m_direction{ TRANSFORM_DIR_FORWARD }; }; diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp index 2c3cc36e93..4050c11fc9 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpGPU.cpp @@ -104,7 +104,8 @@ void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, const GpuShaderCreator::VectorIntGetter & getVector, const std::string & name) { - static constexpr unsigned arrayLen = 16u; // 8 curves x 2 values (count and offset) + // 8 curves x 2 values (count and offset). + static constexpr unsigned arrayLen = HueCurveType::HUE_NUM_CURVES * 2; // Add the uniform if it does not already exist. if (shaderCreator->addUniform(name.c_str(), getSize, getVector, arrayLen)) @@ -660,7 +661,7 @@ void GetGradingHueCurveGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, } const bool doLinToLog = style == GRADING_LIN; - const bool doRGBToHSY = !gcData->getBypassRGBToHSY(); + const bool doRGBToHSY = gcData->getRGBToHSY() == HSY_TRANSFORM_1; switch (dir) { case TRANSFORM_DIR_FORWARD: diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp index f368358226..e2ae5d7b86 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingRGBCurveOpGPU.cpp @@ -100,7 +100,8 @@ void AddUniform(GpuShaderCreatorRcPtr & shaderCreator, const GpuShaderCreator::VectorIntGetter & getVector, const std::string & name) { - static constexpr unsigned arrayLen = 8u; // 4 curves x 2 values (count and offset) + // 4 curves x 2 values (count and offset). + static constexpr unsigned arrayLen = RGBCurveType::RGB_NUM_CURVES * 2; // Add the uniform if it does not already exist. if (shaderCreator->addUniform(name.c_str(), getSize, getVector, arrayLen)) diff --git a/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp b/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp index cbc46051bf..da959e7b48 100644 --- a/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp +++ b/src/OpenColorIO/transforms/GradingHueCurveTransform.cpp @@ -111,14 +111,14 @@ bool GradingHueCurveTransformImpl::slopesAreDefault(HueCurveType c) const return data().slopesAreDefault(c); } -bool GradingHueCurveTransformImpl::getBypassRGBToHSY() const noexcept +HSYTransformStyle GradingHueCurveTransformImpl::getRGBToHSY() const noexcept { - return data().getBypassRGBToHSY(); + return data().getRGBToHSY(); } -void GradingHueCurveTransformImpl::setBypassRGBToHSY(bool bypass) noexcept +void GradingHueCurveTransformImpl::setRGBToHSY(HSYTransformStyle style) noexcept { - data().setBypassRGBToHSY(bypass); + data().setRGBToHSY(style); } bool GradingHueCurveTransformImpl::isDynamic() const noexcept @@ -142,9 +142,9 @@ std::ostream& operator<< (std::ostream & os, const GradingHueCurveTransform & t) os << "direction=" << TransformDirectionToString(t.getDirection()); os << ", style=" << GradingStyleToString(t.getStyle()); os << ", values=" << *t.getValue(); - if (t.getBypassRGBToHSY()) + if (t.getRGBToHSY() == HSY_TRANSFORM_NONE) { - os << ", bypassRGBToHSY=true"; + os << ", hsy_transform=none"; } if (t.isDynamic()) { diff --git a/src/OpenColorIO/transforms/GradingHueCurveTransform.h b/src/OpenColorIO/transforms/GradingHueCurveTransform.h index d499bb38d7..6881232d50 100644 --- a/src/OpenColorIO/transforms/GradingHueCurveTransform.h +++ b/src/OpenColorIO/transforms/GradingHueCurveTransform.h @@ -46,8 +46,8 @@ class GradingHueCurveTransformImpl : public GradingHueCurveTransform void setSlope(HueCurveType c, size_t index, float slope) override; bool slopesAreDefault(HueCurveType c) const override; - bool getBypassRGBToHSY() const noexcept override; - void setBypassRGBToHSY(bool bypass) noexcept override; + HSYTransformStyle getRGBToHSY() const noexcept override; + void setRGBToHSY(HSYTransformStyle style) noexcept override; bool isDynamic() const noexcept override; void makeDynamic() noexcept override; diff --git a/src/OpenColorIO/transforms/GradingRGBCurveTransform.cpp b/src/OpenColorIO/transforms/GradingRGBCurveTransform.cpp index 30cf5d2a67..e9772e667e 100644 --- a/src/OpenColorIO/transforms/GradingRGBCurveTransform.cpp +++ b/src/OpenColorIO/transforms/GradingRGBCurveTransform.cpp @@ -140,6 +140,10 @@ std::ostream& operator<< (std::ostream & os, const GradingRGBCurveTransform & t) os << "direction=" << TransformDirectionToString(t.getDirection()); os << ", style=" << GradingStyleToString(t.getStyle()); os << ", values=" << *t.getValue(); + if (t.getBypassLinToLog()) + { + os << ", bypass_lintolog"; + } if (t.isDynamic()) { os << ", dynamic"; diff --git a/src/bindings/python/PyTypes.cpp b/src/bindings/python/PyTypes.cpp index d7cc71a3c4..01eba8ef4e 100644 --- a/src/bindings/python/PyTypes.cpp +++ b/src/bindings/python/PyTypes.cpp @@ -727,6 +727,16 @@ void bindPyTypes(py::module & m) DOC(PyOpenColorIO, HueCurveType, HUE_FX)) .export_values(); + py::enum_( + m, "HSYTransformStyle", + DOC(PyOpenColorIO, HSYTransformStyle)) + + .value("HSY_TRANSFORM_NONE", HSY_TRANSFORM_NONE, + DOC(PyOpenColorIO, HSYTransformStyle, HSY_TRANSFORM_NONE)) + .value("HSY_TRANSFORM_1", HSY_TRANSFORM_1, + DOC(PyOpenColorIO, HSYTransformStyle, HSY_TRANSFORM_1)) + .export_values(); + py::enum_( m, "BSplineType", DOC(PyOpenColorIO, BSplineType)) diff --git a/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp b/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp index d5bee36af6..1f6c992d9a 100644 --- a/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp +++ b/src/bindings/python/transforms/PyGradingHueCurveTransform.cpp @@ -98,10 +98,10 @@ void bindPyGradingHueCurveTransform(py::module & m) // } // }, // DOC(GradingHueCurveTransform, getSlopes)); - .def("getBypassRGBToHSY", &GradingHueCurveTransform::getBypassRGBToHSY, - DOC(GradingHueCurveTransform, getBypassRGBToHSY)) - .def("setBypassRGBToHSY", &GradingHueCurveTransform::setBypassRGBToHSY, "bypass"_a, - DOC(GradingHueCurveTransform, setBypassRGBToHSY)) + .def("getRGBToHSY", &GradingHueCurveTransform::getRGBToHSY, + DOC(GradingHueCurveTransform, getRGBToHSY)) + .def("setRGBToHSY", &GradingHueCurveTransform::setRGBToHSY, "style"_a, + DOC(GradingHueCurveTransform, setRGBToHSY)) .def("isDynamic", &GradingHueCurveTransform::isDynamic, DOC(GradingHueCurveTransform, isDynamic)) .def("makeDynamic", &GradingHueCurveTransform::makeDynamic, diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index 47ed9af97e..b3d69c1688 100644 --- a/tests/cpu/Config_tests.cpp +++ b/tests/cpu/Config_tests.cpp @@ -4772,7 +4772,7 @@ OCIO_ADD_TEST(Config, grading_huecurve_serialization) " direction: inverse\n" " - !\n" " style: linear\n" - " rgbtohsy_bypass: true\n" + " hsy_transform: none\n" " sat_sat: {control_points: [0, 0, 0.1, 0.2, 0.5, 0.5, 0.7, 0.6, 1, 1.5]}\n" " lum_lum: {control_points: [-1, -1, 0, 0.1, 0.5, 0.6, 1, 1.1]}\n" " - !\n" @@ -4828,6 +4828,20 @@ OCIO_ADD_TEST(Config, grading_huecurve_serialization) OCIO_CHECK_THROW_WHAT(OCIO::Config::CreateFromStream(is), OCIO::Exception, "Number of slopes must match number of control points"); } + + { + const std::string strEnd = + " from_reference: !\n" + " children:\n" + " - ! {style: linear, hsy_transform: hsy1}\n"; + const std::string str = PROFILE_START_V<2, 5>() + strEnd; + + std::istringstream is; + is.str(str); + + OCIO_CHECK_THROW_WHAT(OCIO::Config::CreateFromStream(is), OCIO::Exception, + "Unknown hsy_transform value"); + } } OCIO_ADD_TEST(Config, grading_tone_serialization) diff --git a/tests/cpu/fileformats/FileFormatCTF_tests.cpp b/tests/cpu/fileformats/FileFormatCTF_tests.cpp index 30c8bc5d2f..a022f06041 100644 --- a/tests/cpu/fileformats/FileFormatCTF_tests.cpp +++ b/tests/cpu/fileformats/FileFormatCTF_tests.cpp @@ -4579,7 +4579,7 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_huecurves_log) std::istringstream ctfLog; ctfLog.str(R"( - + 0.015625 0 @@ -4648,7 +4648,7 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_huecurves_log) OCIO_REQUIRE_ASSERT(gradingCurves0); OCIO_CHECK_EQUAL(gradingCurves0->getStyle(), OCIO::GRADING_LOG); OCIO_CHECK_EQUAL(gradingCurves0->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); - OCIO_CHECK_ASSERT(!gradingCurves0->getBypassRGBToHSY()); + OCIO_CHECK_EQUAL(gradingCurves0->getRGBToHSY(), OCIO::HSY_TRANSFORM_NONE); OCIO_CHECK_ASSERT(gradingCurves0->isDynamic()); auto curves = gradingCurves0->getValue(); auto hh = curves->getCurve(OCIO::HUE_HUE); @@ -4798,6 +4798,14 @@ OCIO_ADD_TEST(FileFormatCTF, load_grading_curves_errors) OCIO_REQUIRE_EQUAL(parts.size(), 2); OCIO_CHECK_NE(std::string::npos, StringUtils::Find(parts[0], "Unrecognized element 'Grn'")); OCIO_CHECK_NE(std::string::npos, StringUtils::Find(parts[1], "Unrecognized element 'ControlPoints'")); + + OCIO_CHECK_THROW_WHAT(ParseString(R"( + + + + + +)"), OCIO::Exception, "Unknown hsyTransform value: 'hsy1'"); } OCIO_ADD_TEST(CTFTransform, load_grading_tone) @@ -7347,7 +7355,7 @@ OCIO_ADD_TEST(CTFTransform, grading_huecurve_lin_ctf) // All curves are default curves, no curve is saved. curves = OCIO::GradingHueCurve::Create(OCIO::GRADING_LIN); gradingCurves->setValue(curves); - gradingCurves->setBypassRGBToHSY(true); + gradingCurves->setRGBToHSY(OCIO::HSY_TRANSFORM_NONE); // Make it dynamic so it is not identity. gradingCurves->makeDynamic(); { @@ -7360,7 +7368,7 @@ OCIO_ADD_TEST(CTFTransform, grading_huecurve_lin_ctf) const std::string expected{ R"( - + diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp index 083ef945fa..5fe9ad4f97 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpCPU_tests.cpp @@ -393,7 +393,7 @@ OCIO_ADD_TEST(GradingHueCurveOpCPU, bypass_rgbtohsy) { auto gc = std::make_shared(OCIO::GRADING_LOG); - gc->setBypassRGBToHSY(true); + gc->setRGBToHSY(OCIO::HSY_TRANSFORM_NONE); auto val = gc->getValue()->createEditableCopy(); auto spline = val->getCurve(OCIO::SAT_SAT); diff --git a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp index 1cdd83b9e2..6aeabd0260 100644 --- a/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp +++ b/tests/cpu/ops/gradinghuecurve/GradingHueCurveOpData_tests.cpp @@ -32,12 +32,12 @@ OCIO_ADD_TEST(GradingHueCurveOpData, accessors) OCIO_CHECK_ASSERT(gc.isIdentity()); OCIO_CHECK_ASSERT(gc.isNoOp()); OCIO_CHECK_ASSERT(gc.hasChannelCrosstalk()); - OCIO_CHECK_ASSERT(!gc.getBypassRGBToHSY()); + OCIO_CHECK_EQUAL(gc.getRGBToHSY(), OCIO::HSY_TRANSFORM_1); gc.setStyle(OCIO::GRADING_LIN); OCIO_CHECK_EQUAL(gc.getStyle(), OCIO::GRADING_LIN); - gc.setBypassRGBToHSY(true); - OCIO_CHECK_ASSERT(gc.getBypassRGBToHSY()); + gc.setRGBToHSY(OCIO::HSY_TRANSFORM_NONE); + OCIO_CHECK_EQUAL(gc.getRGBToHSY(), OCIO::HSY_TRANSFORM_NONE); // Get dynamic property as a generic dynamic property and as a typed one and verify they are // the same and can be made dynamic. diff --git a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp index 7e0923d928..26d5bba759 100644 --- a/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp +++ b/tests/cpu/transforms/GradingHueCurveTransform_tests.cpp @@ -19,7 +19,7 @@ OCIO_ADD_TEST(GradingHueCurveTransform, basic) OCIO_CHECK_EQUAL(gctLin->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); gctLin->setDirection(OCIO::TRANSFORM_DIR_INVERSE); OCIO_CHECK_EQUAL(gctLin->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); - OCIO_CHECK_ASSERT(!gctLin->getBypassRGBToHSY()); + OCIO_CHECK_EQUAL(gctLin->getRGBToHSY(), OCIO::HSY_TRANSFORM_1); OCIO_CHECK_ASSERT(!gctLin->isDynamic()); auto crv = gctLin->getValue()->getCurve(OCIO::LUM_SAT); OCIO_CHECK_EQUAL(crv->getNumControlPoints(), 3); @@ -34,7 +34,7 @@ OCIO_ADD_TEST(GradingHueCurveTransform, basic) auto gctLog = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_LOG); OCIO_CHECK_EQUAL(gctLog->getStyle(), OCIO::GRADING_LOG); OCIO_CHECK_EQUAL(gctLog->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); - OCIO_CHECK_ASSERT(!gctLog->getBypassRGBToHSY()); + OCIO_CHECK_EQUAL(gctLog->getRGBToHSY(), OCIO::HSY_TRANSFORM_1); OCIO_CHECK_ASSERT(!gctLog->isDynamic()); crv = gctLog->getValue()->getCurve(OCIO::LUM_SAT); OCIO_CHECK_EQUAL(crv->getNumControlPoints(), 3); @@ -50,7 +50,7 @@ OCIO_ADD_TEST(GradingHueCurveTransform, basic) auto gctVid = OCIO::GradingHueCurveTransform::Create(OCIO::GRADING_VIDEO); OCIO_CHECK_EQUAL(gctVid->getStyle(), OCIO::GRADING_VIDEO); OCIO_CHECK_EQUAL(gctVid->getDirection(), OCIO::TRANSFORM_DIR_FORWARD); - OCIO_CHECK_ASSERT(!gctVid->getBypassRGBToHSY()); + OCIO_CHECK_EQUAL(gctVid->getRGBToHSY(), OCIO::HSY_TRANSFORM_1); OCIO_CHECK_ASSERT(!gctVid->isDynamic()); crv = gctVid->getValue()->getCurve(OCIO::LUM_SAT); OCIO_CHECK_EQUAL(crv->getNumControlPoints(), 3); @@ -70,8 +70,8 @@ OCIO_ADD_TEST(GradingHueCurveTransform, basic) OCIO_CHECK_EQUAL(gct->getStyle(), OCIO::GRADING_LIN); gct->setDirection(OCIO::TRANSFORM_DIR_INVERSE); OCIO_CHECK_EQUAL(gct->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); - gct->setBypassRGBToHSY(true); - OCIO_CHECK_ASSERT(gct->getBypassRGBToHSY()); + gct->setRGBToHSY(OCIO::HSY_TRANSFORM_NONE); + OCIO_CHECK_EQUAL(gct->getRGBToHSY(), OCIO::HSY_TRANSFORM_NONE); gct->makeDynamic(); OCIO_CHECK_ASSERT(gct->isDynamic()); gct->setValue(gctLin->getValue()); diff --git a/tests/gpu/GradingHueCurveOp_test.cpp b/tests/gpu/GradingHueCurveOp_test.cpp index 224bc67787..3eb31c0826 100644 --- a/tests/gpu/GradingHueCurveOp_test.cpp +++ b/tests/gpu/GradingHueCurveOp_test.cpp @@ -238,7 +238,7 @@ void BypassRGBtoHSY(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynam OCIO::GradingBSplineCurveRcPtr satsat = hueCurve->getCurve(OCIO::SAT_SAT); satsat->getControlPoint(1) = OCIO::GradingControlPoint(0.4f, 0.6f); - hc->setBypassRGBToHSY(true); + hc->setRGBToHSY(OCIO::HSY_TRANSFORM_NONE); hc->setValue(hueCurve); hc->setDirection(dir); diff --git a/tests/python/GradingHueCurveTransformTest.py b/tests/python/GradingHueCurveTransformTest.py index cca643a21e..96bdc8e301 100644 --- a/tests/python/GradingHueCurveTransformTest.py +++ b/tests/python/GradingHueCurveTransformTest.py @@ -29,14 +29,14 @@ def test_contructor(self): self.assertEqual(gct.getStyle(), OCIO.GRADING_LOG) assertEqualHueCurve(self, gct.getValue(), self.valDefaultLog) self.assertEqual(gct.isDynamic(), False) - self.assertEqual(gct.getBypassRGBToHSY(), False) + self.assertEqual(gct.getRGBToHSY(), OCIO.HSY_TRANSFORM_1) self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_FORWARD) gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LIN) self.assertEqual(gct.getStyle(), OCIO.GRADING_LIN) assertEqualHueCurve(self, gct.getValue(), self.valDefaultLin) self.assertEqual(gct.isDynamic(), False) - self.assertEqual(gct.getBypassRGBToHSY(), False) + self.assertEqual(gct.getRGBToHSY(), OCIO.HSY_TRANSFORM_1) self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_FORWARD) vals = OCIO.GradingHueCurve(OCIO.GRADING_LOG) @@ -69,9 +69,9 @@ def test_misc(self): """ gct = OCIO.GradingHueCurveTransform(OCIO.GRADING_LOG) - self.assertEqual(gct.getBypassRGBToHSY(), False) - gct.setBypassRGBToHSY(True) - self.assertEqual(gct.getBypassRGBToHSY(), True) + self.assertEqual(gct.getRGBToHSY(), OCIO.HSY_TRANSFORM_1) + gct.setRGBToHSY(OCIO.HSY_TRANSFORM_NONE) + self.assertEqual(gct.getRGBToHSY(), OCIO.HSY_TRANSFORM_NONE) self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_FORWARD) gct.setDirection(OCIO.TRANSFORM_DIR_INVERSE) self.assertEqual(gct.getDirection(), OCIO.TRANSFORM_DIR_INVERSE)