From d5fddb09eccfe714c706c61955d23a6edfab6369 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Sun, 30 Aug 2026 20:58:43 -0400 Subject: [PATCH 1/4] Fix CPU GradingHueCurve bug Signed-off-by: Doug Walker --- .../gradinghuecurve/GradingHueCurveOpCPU.cpp | 12 +-- tests/gpu/GradingHueCurveOp_test.cpp | 74 +++++++++++++++++++ 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp index c3728eb882..60eab00f12 100644 --- a/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp +++ b/src/OpenColorIO/ops/gradinghuecurve/GradingHueCurveOpCPU.cpp @@ -66,7 +66,7 @@ class GradingHueCurveOpCPU : public OpCPU protected: DynamicPropertyGradingHueCurveImplRcPtr m_ghuecurve; - bool m_isLinear = false; + bool m_isLog = false; ConstOpCPURcPtr m_rgbToHsyOp; ConstOpCPURcPtr m_hsyToRgbOp; @@ -93,11 +93,11 @@ GradingHueCurveOpCPU::GradingHueCurveOpCPU(ConstGradingHueCurveOpDataRcPtr & gcD 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; + m_isLog = true; break; case GRADING_VIDEO: fwdStyle = FixedFunctionOpData::RGB_TO_HSY_VID; @@ -263,8 +263,8 @@ void GradingHueCurveFwdOpCPU::apply(const void * inImg, void * outImg, long numP 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; + out[2] = m_isLog ? out[2] + (hueLumGain + satLumGain - 2.f) * 0.1f : + out[2] * hueLumGain * satLumGain; // HUE-FX out[0] = out[0] - std::floor(out[0]); // wrap to [0,1) @@ -331,8 +331,8 @@ void GradingHueCurveRevOpCPU::apply(const void * inImg, void * outImg, long numP // 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; + out[2] = m_isLog ? out[2] - (hue_lum_gain + sat_lum_gain - 2.f) * 0.1f : + out[2] / std::max(0.01f, lum_gain); m_applyLinLog(out); diff --git a/tests/gpu/GradingHueCurveOp_test.cpp b/tests/gpu/GradingHueCurveOp_test.cpp index 9a11626939..00dee33eff 100644 --- a/tests/gpu/GradingHueCurveOp_test.cpp +++ b/tests/gpu/GradingHueCurveOp_test.cpp @@ -103,6 +103,80 @@ OCIO_ADD_GPU_TEST(GradingHueCurve, style_log_rev_dynamic) GradingHueCurveLog(test, OCIO::TRANSFORM_DIR_INVERSE, true); } +void GradingHueCurveVid(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_VIDEO); + 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 = 21; + OCIOGPUTest::CustomValues values; + // 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); + test.setExpectedMinimalValue(1.0f); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, style_vid_fwd) +{ + GradingHueCurveVid(test, OCIO::TRANSFORM_DIR_FORWARD, false); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, style_vid_fwd_dynamic) +{ + GradingHueCurveVid(test, OCIO::TRANSFORM_DIR_FORWARD, true); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, style_vid_rev) +{ + GradingHueCurveVid(test, OCIO::TRANSFORM_DIR_INVERSE, false); +} + +OCIO_ADD_GPU_TEST(GradingHueCurve, style_vid_rev_dynamic) +{ + GradingHueCurveVid(test, OCIO::TRANSFORM_DIR_INVERSE, true); +} + void HueCurveLin(OCIOGPUTest & test, OCIO::TransformDirection dir, bool dynamic) { // All curves are non-identities. From 0301731b2e340ad26ed26457226052e5aa994262 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Sun, 30 Aug 2026 21:01:07 -0400 Subject: [PATCH 2/4] Add built-in ACES 2 look transform Signed-off-by: Doug Walker --- src/OpenColorIO/transforms/builtins/ACES.cpp | 97 +++++++++++++++++++ .../cpu/transforms/BuiltinTransform_tests.cpp | 3 + 2 files changed, 100 insertions(+) diff --git a/src/OpenColorIO/transforms/builtins/ACES.cpp b/src/OpenColorIO/transforms/builtins/ACES.cpp index 8f08c5829d..0a445b0ffe 100644 --- a/src/OpenColorIO/transforms/builtins/ACES.cpp +++ b/src/OpenColorIO/transforms/builtins/ACES.cpp @@ -9,6 +9,7 @@ #include #include "ops/fixedfunction/FixedFunctionOp.h" +#include "ops/gradinghuecurve/GradingHueCurveOp.h" #include "ops/gradingrgbcurve/GradingRGBCurveOp.h" #include "ops/log/LogOp.h" #include "ops/matrix/MatrixOp.h" @@ -794,6 +795,102 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept "LMT (applied in ACES2065-1) to compress scene-referred values from common cameras into the AP1 gamut", GAMUT_COMP_13_Functor); } + { + auto ACES2_LMT_DCC_Functor = [](OpRcPtrVec & ops) + { + // Set up the GradingHueCurve. + + auto gc = std::make_shared(GRADING_VIDEO); + { + // TODO: These parameter values are not final. + auto ll = GradingBSplineCurve::Create({ + {0.f, 0.f}, + {0.030268261f, 0.033965353f}, + {0.063408233f, 0.071113035f}, + {0.11234653f, 0.13219112f}, + {0.15547293f, 0.19494583f}, + {0.252096f, 0.38115612f}, + {0.76718688f, 2.0309019f} + }, LUM_LUM); + float slopes[] = { 1.136f, 1.107f, 1.1390001f, 1.355f, 1.5549999f, 2.3699999f, 5.f }; + for (size_t i = 0; i < 7; ++i) + { + ll->setSlope( i, slopes[i] ); + } + + auto ls = GradingBSplineCurve::Create({ + {0.f, 1.f}, + {0.05f, 1.125f}, + {0.15f, 1.4f}, + {0.32f, 1.84f}, + {0.75f, 2.f}, + {1.f, 2.f} + }, LUM_SAT); + + auto sl = GradingBSplineCurve::Create({ + {0.f, 1.f}, + {0.7f, 1.f}, + {1.f, 0.92f}, + {2.f, 0.75f}, + {2.2f, 0.75f} + }, SAT_LUM); + + // Only lumSat, lumLum, and satLum are being customized above, so pull the + // remaining five curves from the style's defaults. + auto defaultCurves = GradingHueCurve::Create(GRADING_VIDEO); + + auto curves = GradingHueCurve::Create( + defaultCurves->getCurve(HUE_HUE), + defaultCurves->getCurve(HUE_SAT), + defaultCurves->getCurve(HUE_LUM), + ls, // lumSat + defaultCurves->getCurve(SAT_SAT), + ll, // lumLum + sl, // satLum + defaultCurves->getCurve(HUE_FX)); + + gc->setValue(curves); + + // Use the supplied HMJ transform rather than the built-in HSY transform. + gc->setRGBToHSY(HSY_TRANSFORM_NONE); + } + + // Create the built-in. + + // TODO: The above parameters are based on the existing FF, will switch before merging. + //CreateFixedFunctionOp(ops, FixedFunctionOpData::ACES_RGB_TO_HMJ_20, { + CreateFixedFunctionOp(ops, FixedFunctionOpData::ACES_RGB_TO_JMh_20, { + // ACES2065-1 + 0.7347, 0.2653, 0.0000, 1.0000, 0.0001, -0.0770, 0.32168, 0.33767 + }); + + // TODO: This is temporary, will remove before merging. + static constexpr double SWAP_CHANS[4 * 4] + { + 0., 0., 0.0025, 0., + 0., 0.01, 0., 0., + 0.0025, 0., 0., 0., + 0., 0., 0., 1. + }; + CreateMatrixOp(ops, &SWAP_CHANS[0], TRANSFORM_DIR_FORWARD); + + CreateGradingHueCurveOp(ops, gc, TRANSFORM_DIR_FORWARD); + + // TODO: This is temporary, will remove before merging. + CreateMatrixOp(ops, &SWAP_CHANS[0], TRANSFORM_DIR_INVERSE); + + // TODO: The above parameters are based on the existing FF, will switch before merging. + //CreateFixedFunctionOp(ops, FixedFunctionOpData::ACES_HMJ_TO_RGB_20, { + CreateFixedFunctionOp(ops, FixedFunctionOpData::ACES_JMh_TO_RGB_20, { + // ACES2065-1 + 0.7347, 0.2653, 0.0000, 1.0000, 0.0001, -0.0770, 0.32168, 0.33767 + }); + }; + + registry.addBuiltin("ACES-LMT - ACES 2.0 DCC Look 1", + "LMT (applied in ACES2065-1) to give a brighter, more contrasty look with ACES 2 Output Transforms", + ACES2_LMT_DCC_Functor); + } // // ACES OUTPUT TRANSFORMS diff --git a/tests/cpu/transforms/BuiltinTransform_tests.cpp b/tests/cpu/transforms/BuiltinTransform_tests.cpp index cc57edf412..62d8b1fc24 100644 --- a/tests/cpu/transforms/BuiltinTransform_tests.cpp +++ b/tests/cpu/transforms/BuiltinTransform_tests.cpp @@ -412,6 +412,9 @@ AllValues UnitTestValues { "ACES-LMT - ACES 1.3 Reference Gamut Compression", { 1.0e-6f, { 0.5f, 0.4f, -0.3f }, { 0.54812347889f, 0.42805567384f, -0.00588858686f } } }, + { "ACES-LMT - ACES 2.0 DCC Look 1", + { 1.0e-6f, + { 0.5f, 0.4f, 0.3f }, { 0.875603199f, 0.673843086f, 0.47828418f } } }, { "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-CINEMA_1.0", { 1.0e-6f, From e79fb8d5a22f526e4ee551e026bc9997f55dd80a Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Sun, 30 Aug 2026 21:25:27 -0400 Subject: [PATCH 3/4] Add RGB to HMJ Fixed Function Signed-off-by: Doug Walker --- docs/releases/_index.rst | 1 + docs/releases/ocio_2_6.rst | 59 ++++++ include/OpenColorIO/OpenColorTypes.h | 1 + src/OpenColorIO/Config.cpp | 38 +++- src/OpenColorIO/ParseUtils.cpp | 2 + .../fileformats/ctf/CTFTransform.cpp | 5 + .../fileformats/ctf/CTFTransform.h | 5 +- .../ops/fixedfunction/FixedFunctionOpCPU.cpp | 101 +++++++++ .../ops/fixedfunction/FixedFunctionOpData.cpp | 38 +++- .../ops/fixedfunction/FixedFunctionOpData.h | 2 + .../ops/fixedfunction/FixedFunctionOpGPU.cpp | 36 ++++ src/bindings/python/PyTypes.cpp | 2 + tests/cpu/fileformats/FileFormatCTF_tests.cpp | 55 ++++- .../FixedFunctionOpCPU_tests.cpp | 33 +++ .../FixedFunctionOpData_tests.cpp | 12 ++ .../FixedFunctionTransform_tests.cpp | 19 ++ .../BuiltinTransformRegistry_tests.cpp | 2 +- tests/gpu/FixedFunctionOp_test.cpp | 197 ++++++++++++------ 18 files changed, 534 insertions(+), 74 deletions(-) create mode 100644 docs/releases/ocio_2_6.rst diff --git a/docs/releases/_index.rst b/docs/releases/_index.rst index 20e5d707ff..50f882c067 100644 --- a/docs/releases/_index.rst +++ b/docs/releases/_index.rst @@ -10,6 +10,7 @@ Releases .. toctree:: :caption: Upgrading to v2 + ocio_2_6 ocio_2_5 ocio_2_4 ocio_2_3 diff --git a/docs/releases/ocio_2_6.rst b/docs/releases/ocio_2_6.rst new file mode 100644 index 0000000000..e7920a2961 --- /dev/null +++ b/docs/releases/ocio_2_6.rst @@ -0,0 +1,59 @@ +.. + SPDX-License-Identifier: CC-BY-4.0 + Copyright Contributors to the OpenColorIO Project. + + +OCIO 2.6 Release +================ + +Timeline +******** + +OpenColorIO 2.6 was delivered in September 2026 and is in the VFX Reference Platform for +calendar year 2027. + +New Feature Guide +================= + +New Fixed Function Transforms +***************************** + +For Config Authors +++++++++++++++++++ + +The following new styles are available for use with FixedFunctionTransforms in config +files with ``ocio_profile_version`` set to 2.6 or higher. They implement a conversion from +a linear RGB space with customizable primaries to the JMh (lightness, colorfulness, hue) +color appearance space used in the ACES 2.0 Output Transforms. The ordering is hue, +colorfulness, lightness, rather than JMh since that is the order already established by +the built-in HSV and HSY Fixed Functions and it conforms to the order expected by the +GradingHueCurveTransform. The scaling is {h/360, M/200, J/100} to allow the resulting +images to be easier to work with in DCCs. It takes the following eight parameters to +describe the primaries and white point of the RGB space: +[ red_x, red_y, green_x, green_y, blue_x, blue_y, white_x, white_y ]. + +* ``FIXED_FUNCTION_ACES_RGB_TO_HMJ_20`` + + +New Built-in Transforms +*********************** + +For Config Authors +++++++++++++++++++ + +In config files with ``ocio_profile_version`` set to 2.6 or higher, config authors may take +advantage of the following new BuiltinTransform style that provides a new look transform +intended for use with the ACES 2.0 Output Transforms (SDR only) that provides a brighter, +higher contrast look that is a more finished/graded look. The brightness is adjusted to be +more appropriate for using ACES 2 Output Transforms outside of cinema or TV where the image +has a brighter surround (such as on a web page or video game). + +* ``ACES-LMT - ACES 2.0 DCC Look 1`` + + +Release Notes +============= + +For additional details, please see the GitHub release page: + +`OCIO 2.6.0 `_ diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 39a181c467..782fa03309 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -516,6 +516,7 @@ enum FixedFunctionStyle FIXED_FUNCTION_LIN_TO_DOUBLE_LOG, ///< Two parameterized LogAffineTransforms with a middle linear segment 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_RGB_TO_HMJ_20, ///< ACES 2.0 RGB to HMJ (h/360, M/200, J/100) -- 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, Luminance) for linear spaces diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index a8f25d5cad..ccff83c97d 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -248,7 +248,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 - 5 // Version 2 + 6 // Version 2 }; } // namespace @@ -5674,6 +5674,27 @@ void Config::Impl::checkVersionConsistency(ConstTransformRcPtr & transform) cons << blt->getStyle() << "'."; throw Exception(os.str().c_str()); } + if (m_majorVersion == 2 && m_minorVersion < 5 + && ( 0 == Platform::Strcasecmp(blt->getStyle(), "DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.709 - MIRROR NEGS") + || 0 == Platform::Strcasecmp(blt->getStyle(), "DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.2020 - MIRROR NEGS") + || 0 == Platform::Strcasecmp(blt->getStyle(), "DISPLAY - CIE-XYZ-D65_to_G2.2-REC.709 - MIRROR NEGS") + || 0 == Platform::Strcasecmp(blt->getStyle(), "DISPLAY - CIE-XYZ-D65_to_sRGB - MIRROR NEGS") + || 0 == Platform::Strcasecmp(blt->getStyle(), "DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D65 - MIRROR NEGS") ) + ) + { + std::ostringstream os; + os << "Only config version 2.5 (or higher) can have BuiltinTransform style '" + << blt->getStyle() << "'."; + throw Exception(os.str().c_str()); + } + if (m_majorVersion == 2 && m_minorVersion < 6 + && 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-LMT - ACES 2.0 DCC Look 1")) + { + std::ostringstream os; + os << "Only config version 2.6 (or higher) can have BuiltinTransform style '" + << blt->getStyle() << "'."; + throw Exception(os.str().c_str()); + } } else if (ConstCDLTransformRcPtr cdl = DynamicPtrCast(transform)) { @@ -5765,12 +5786,23 @@ void Config::Impl::checkVersionConsistency(ConstTransformRcPtr & transform) cons if (m_majorVersion == 2 && m_minorVersion < 5 ) { - if( ffstyle == FIXED_FUNCTION_RGB_TO_HSY_LIN || + 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 '" + ss << "Only config version 2.5 (or higher) can have FixedFunctionTransform style '" + << FixedFunctionStyleToString(ffstyle) << "'."; + throw Exception(ss.str().c_str()); + } + } + + if (m_majorVersion == 2 && m_minorVersion < 6 ) + { + if( ffstyle == FIXED_FUNCTION_ACES_RGB_TO_HMJ_20 ) + { + std::ostringstream ss; + ss << "Only config version 2.6 (or higher) can have FixedFunctionTransform style '" << FixedFunctionStyleToString(ffstyle) << "'."; throw Exception(ss.str().c_str()); } diff --git a/src/OpenColorIO/ParseUtils.cpp b/src/OpenColorIO/ParseUtils.cpp index 47d4f2641a..1cff02479a 100644 --- a/src/OpenColorIO/ParseUtils.cpp +++ b/src/OpenColorIO/ParseUtils.cpp @@ -363,6 +363,7 @@ const char * FixedFunctionStyleToString(FixedFunctionStyle style) case FIXED_FUNCTION_ACES_GAMUT_COMP_13: return "ACES_GamutComp13"; case FIXED_FUNCTION_ACES_OUTPUT_TRANSFORM_20: return "ACES2_OutputTransform"; case FIXED_FUNCTION_ACES_RGB_TO_JMH_20: return "ACES2_RGB_TO_JMh"; + case FIXED_FUNCTION_ACES_RGB_TO_HMJ_20: return "ACES2_RGB_TO_HMJ"; case FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20: return "ACES2_TonescaleCompress"; case FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20: return "ACES2_GamutCompress"; case FIXED_FUNCTION_REC2100_SURROUND: return "REC2100_Surround"; @@ -400,6 +401,7 @@ FixedFunctionStyle FixedFunctionStyleFromString(const char * style) else if(str == "aces_gamutcomp13") return FIXED_FUNCTION_ACES_GAMUT_COMP_13; else if(str == "aces2_outputtransform") return FIXED_FUNCTION_ACES_OUTPUT_TRANSFORM_20; else if(str == "aces2_rgb_to_jmh") return FIXED_FUNCTION_ACES_RGB_TO_JMH_20; + else if(str == "aces2_rgb_to_hmj") return FIXED_FUNCTION_ACES_RGB_TO_HMJ_20; else if(str == "aces2_tonescalecompress") return FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20; else if(str == "aces2_gamutcompress") return FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20; else if(str == "rec2100_surround") return FIXED_FUNCTION_REC2100_SURROUND; diff --git a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp index 55f6877adf..1e693addc7 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFTransform.cpp @@ -312,6 +312,11 @@ CTFVersion GetOpMinimumVersion(const ConstOpDataRcPtr & op) { minVersion = CTF_PROCESS_LIST_VERSION_2_5; } + else if ( ff->getStyle() == FixedFunctionOpData::ACES_RGB_TO_HMJ_20 + || ff->getStyle() == FixedFunctionOpData::ACES_HMJ_TO_RGB_20 ) + { + minVersion = CTF_PROCESS_LIST_VERSION_2_6; + } break; } case OpData::GradingPrimaryType: diff --git a/src/OpenColorIO/fileformats/ctf/CTFTransform.h b/src/OpenColorIO/fileformats/ctf/CTFTransform.h index dec4c71532..647142760d 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFTransform.h +++ b/src/OpenColorIO/fileformats/ctf/CTFTransform.h @@ -137,9 +137,12 @@ 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); +// Version 2.6 2026-08 adds the FixedFunction FIXED_FUNCTION_ACES_RGB_TO_HMJ_20. +static const CTFVersion CTF_PROCESS_LIST_VERSION_2_6 = CTFVersion(2, 6); + // 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_5; +static const CTFVersion CTF_PROCESS_LIST_VERSION = CTF_PROCESS_LIST_VERSION_2_6; // 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 ed7fcb362e..14ff00d07e 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp @@ -169,6 +169,23 @@ class Renderer_ACES_RGB_TO_JMh_20 : public OpCPU ACES2::JMhParams m_p; }; +class Renderer_ACES_RGB_TO_HMJ_20 : public OpCPU +{ +public: + Renderer_ACES_RGB_TO_HMJ_20() = delete; + explicit Renderer_ACES_RGB_TO_HMJ_20(ConstFixedFunctionOpDataRcPtr & data); + + void apply(const void * inImg, void * outImg, long numPixels) const override; + +private: + void fwd(const void * inImg, void * outImg, long numPixels) const; + void inv(const void * inImg, void * outImg, long numPixels) const; + +protected: + bool m_fwd; + ACES2::JMhParams m_p; +}; + class Renderer_ACES_TONESCALE_COMPRESS_20 : public OpCPU { public: @@ -1240,6 +1257,83 @@ void Renderer_ACES_RGB_TO_JMh_20::inv(const void * inImg, void * outImg, long nu } } +Renderer_ACES_RGB_TO_HMJ_20::Renderer_ACES_RGB_TO_HMJ_20(ConstFixedFunctionOpDataRcPtr & data) + : OpCPU() +{ + m_fwd = FixedFunctionOpData::ACES_RGB_TO_HMJ_20 == data->getStyle(); + + const float red_x = (float) data->getParams()[0]; + const float red_y = (float) data->getParams()[1]; + const float green_x = (float) data->getParams()[2]; + const float green_y = (float) data->getParams()[3]; + const float blue_x = (float) data->getParams()[4]; + const float blue_y = (float) data->getParams()[5]; + const float white_x = (float) data->getParams()[6]; + const float white_y = (float) data->getParams()[7]; + + const Primaries primaries = { + {red_x , red_y }, + {green_x, green_y}, + {blue_x , blue_y }, + {white_x, white_y} + }; + + m_p = ACES2::init_JMhParams(primaries); +} + +void Renderer_ACES_RGB_TO_HMJ_20::apply(const void * inImg, void * outImg, long numPixels) const +{ + if (m_fwd) + { + fwd(inImg, outImg, numPixels); + } + else + { + inv(inImg, outImg, numPixels); + } +} + +void Renderer_ACES_RGB_TO_HMJ_20::fwd(const void * inImg, void * outImg, long numPixels) const +{ + const float * in = (const float *)inImg; + float * out = (float *)outImg; + + for(long idx=0; idx(func); } + case FixedFunctionOpData::ACES_RGB_TO_HMJ_20: + case FixedFunctionOpData::ACES_HMJ_TO_RGB_20: + { + // Sharing same renderer (param will be inverted to handle direction). + return std::make_shared(func); + } + case FixedFunctionOpData::ACES_TONESCALE_COMPRESS_20_FWD: case FixedFunctionOpData::ACES_TONESCALE_COMPRESS_20_INV: { diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp index 8e5d5833d0..3778019492 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.cpp @@ -56,6 +56,8 @@ constexpr char ACES_OUTPUT_TRANSFORM_20_FWD_STR[] = "ACESOutputTransform20Fwd" constexpr char ACES_OUTPUT_TRANSFORM_20_INV_STR[] = "ACESOutputTransform20Inv"; constexpr char ACES_RGB_TO_JMh_20_STR[] = "RGB_TO_JMh_20"; constexpr char ACES_JMh_TO_RGB_20_STR[] = "JMh_TO_RGB_20"; +constexpr char ACES_RGB_TO_HMJ_20_STR[] = "RGB_TO_HMJ_20"; +constexpr char ACES_HMJ_TO_RGB_20_STR[] = "HMJ_TO_RGB_20"; constexpr char ACES_TONESCALE_COMPRESS_20_FWD_STR[] = "ToneScaleCompress20Fwd"; constexpr char ACES_TONESCALE_COMPRESS_20_INV_STR[] = "ToneScaleCompress20Inv"; constexpr char ACES_GAMUT_COMPRESS_20_FWD_STR[] = "GamutCompress20Fwd"; @@ -125,6 +127,10 @@ const char * FixedFunctionOpData::ConvertStyleToString(Style style, bool detaile return ACES_RGB_TO_JMh_20_STR; case ACES_JMh_TO_RGB_20: return ACES_JMh_TO_RGB_20_STR; + case ACES_RGB_TO_HMJ_20: + return ACES_RGB_TO_HMJ_20_STR; + case ACES_HMJ_TO_RGB_20: + return ACES_HMJ_TO_RGB_20_STR; case ACES_TONESCALE_COMPRESS_20_FWD: return detailed ? "ACES_ToneScaleCompress20 (Forward)" : ACES_TONESCALE_COMPRESS_20_FWD_STR; case ACES_TONESCALE_COMPRESS_20_INV: @@ -254,6 +260,14 @@ FixedFunctionOpData::Style FixedFunctionOpData::GetStyle(const char * name) { return ACES_JMh_TO_RGB_20; } + else if (0 == Platform::Strcasecmp(name, ACES_RGB_TO_HMJ_20_STR)) + { + return ACES_RGB_TO_HMJ_20; + } + else if (0 == Platform::Strcasecmp(name, ACES_HMJ_TO_RGB_20_STR)) + { + return ACES_HMJ_TO_RGB_20; + } else if (0 == Platform::Strcasecmp(name, ACES_TONESCALE_COMPRESS_20_FWD_STR)) { return ACES_TONESCALE_COMPRESS_20_FWD; @@ -415,6 +429,11 @@ FixedFunctionOpData::Style FixedFunctionOpData::ConvertStyle(FixedFunctionStyle return isForward ? FixedFunctionOpData::ACES_RGB_TO_JMh_20 : FixedFunctionOpData::ACES_JMh_TO_RGB_20; } + case FIXED_FUNCTION_ACES_RGB_TO_HMJ_20: + { + return isForward ? FixedFunctionOpData::ACES_RGB_TO_HMJ_20 : + FixedFunctionOpData::ACES_HMJ_TO_RGB_20; + } case FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20: { return isForward ? FixedFunctionOpData::ACES_TONESCALE_COMPRESS_20_FWD : @@ -528,6 +547,10 @@ FixedFunctionStyle FixedFunctionOpData::ConvertStyle(FixedFunctionOpData::Style case FixedFunctionOpData::ACES_JMh_TO_RGB_20: return FIXED_FUNCTION_ACES_RGB_TO_JMH_20; + case FixedFunctionOpData::ACES_RGB_TO_HMJ_20: + case FixedFunctionOpData::ACES_HMJ_TO_RGB_20: + return FIXED_FUNCTION_ACES_RGB_TO_HMJ_20; + case FixedFunctionOpData::ACES_TONESCALE_COMPRESS_20_FWD: case FixedFunctionOpData::ACES_TONESCALE_COMPRESS_20_INV: return FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20; @@ -668,7 +691,8 @@ void FixedFunctionOpData::validate() const check_param_bounds("peak_luminance", peak_luminance, 1, 10000); check_param_no_frac("peak_luminance", peak_luminance); } - else if (m_style == ACES_RGB_TO_JMh_20 || m_style == ACES_JMh_TO_RGB_20) + else if (m_style == ACES_RGB_TO_JMh_20 || m_style == ACES_JMh_TO_RGB_20 || + m_style == ACES_RGB_TO_HMJ_20 || m_style == ACES_HMJ_TO_RGB_20) { if (m_params.size() != 8) { @@ -939,6 +963,16 @@ void FixedFunctionOpData::invert() noexcept setStyle(ACES_RGB_TO_JMh_20); break; } + case ACES_RGB_TO_HMJ_20: + { + setStyle(ACES_HMJ_TO_RGB_20); + break; + } + case ACES_HMJ_TO_RGB_20: + { + setStyle(ACES_RGB_TO_HMJ_20); + break; + } case ACES_TONESCALE_COMPRESS_20_FWD: { setStyle(ACES_TONESCALE_COMPRESS_20_INV); @@ -1107,6 +1141,7 @@ TransformDirection FixedFunctionOpData::getDirection() const noexcept case FixedFunctionOpData::ACES_GAMUT_COMP_13_FWD: case FixedFunctionOpData::ACES_OUTPUT_TRANSFORM_20_FWD: case FixedFunctionOpData::ACES_RGB_TO_JMh_20: + case FixedFunctionOpData::ACES_RGB_TO_HMJ_20: case FixedFunctionOpData::ACES_TONESCALE_COMPRESS_20_FWD: case FixedFunctionOpData::ACES_GAMUT_COMPRESS_20_FWD: case FixedFunctionOpData::REC2100_SURROUND_FWD: @@ -1130,6 +1165,7 @@ TransformDirection FixedFunctionOpData::getDirection() const noexcept case FixedFunctionOpData::ACES_GAMUT_COMP_13_INV: case FixedFunctionOpData::ACES_OUTPUT_TRANSFORM_20_INV: case FixedFunctionOpData::ACES_JMh_TO_RGB_20: + case FixedFunctionOpData::ACES_HMJ_TO_RGB_20: case FixedFunctionOpData::ACES_TONESCALE_COMPRESS_20_INV: case FixedFunctionOpData::ACES_GAMUT_COMPRESS_20_INV: case FixedFunctionOpData::REC2100_SURROUND_INV: diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h index c7c92589d0..b9f14a76fa 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpData.h @@ -58,6 +58,8 @@ class FixedFunctionOpData : public OpData ACES_OUTPUT_TRANSFORM_20_INV, // ACES2 Output transform (inverse) ACES_RGB_TO_JMh_20, // ACES2 RGB to JMh ACES_JMh_TO_RGB_20, // ACES2 JMh to RGB + ACES_RGB_TO_HMJ_20, // ACES2 RGB to HMJ (h/360, M/200, J/100) + ACES_HMJ_TO_RGB_20, // ACES2 HMJ to RGB 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 diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp index 59ab7121c0..6a56c1a710 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp @@ -1491,6 +1491,32 @@ void Add_JMh_to_RGB_Shader( _Add_JMh_to_RGB_Shader(shaderCreator, ss, p); } +void Add_RGB_to_HMJ_Shader( + GpuShaderCreatorRcPtr & shaderCreator, + GpuShaderText & ss, + const FixedFunctionOpData::Params & params) +{ + const std::string pxl(shaderCreator->getPixelName()); + + // Compute JMh (in pxl.rgb) and then repack it as HMJ, scaled for a [0,1]-ish range. + Add_RGB_to_JMh_Shader(shaderCreator, ss, params); + + ss.newLine() << pxl << ".rgb = " << ss.float3Const(pxl + ".b / 360.0", pxl + ".g / 200.0", pxl + ".r / 100.0") << ";"; +} + +void Add_HMJ_to_RGB_Shader( + GpuShaderCreatorRcPtr & shaderCreator, + GpuShaderText & ss, + const FixedFunctionOpData::Params & params) +{ + const std::string pxl(shaderCreator->getPixelName()); + + // Unpack HMJ back into JMh (in pxl.rgb) and reuse the existing JMh to RGB conversion. + ss.newLine() << pxl << ".rgb = " << ss.float3Const(pxl + ".b * 100.0", pxl + ".g * 200.0", pxl + ".r * 360.0") << ";"; + + Add_JMh_to_RGB_Shader(shaderCreator, ss, params); +} + void Add_Tonescale_Compress_Fwd_Shader( GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, @@ -2349,6 +2375,16 @@ void GetFixedFunctionGPUProcessingText(GpuShaderCreatorRcPtr & shaderCreator, Add_JMh_to_RGB_Shader(shaderCreator, ss, func->getParams()); break; } + case FixedFunctionOpData::ACES_RGB_TO_HMJ_20: + { + Add_RGB_to_HMJ_Shader(shaderCreator, ss, func->getParams()); + break; + } + case FixedFunctionOpData::ACES_HMJ_TO_RGB_20: + { + Add_HMJ_to_RGB_Shader(shaderCreator, ss, func->getParams()); + break; + } case FixedFunctionOpData::ACES_TONESCALE_COMPRESS_20_FWD: { Add_Tonescale_Compress_Fwd_Shader(shaderCreator, ss, func->getParams()); diff --git a/src/bindings/python/PyTypes.cpp b/src/bindings/python/PyTypes.cpp index e109708c35..c8358dacc6 100644 --- a/src/bindings/python/PyTypes.cpp +++ b/src/bindings/python/PyTypes.cpp @@ -615,6 +615,8 @@ void bindPyTypes(py::module & m) DOC(PyOpenColorIO, FixedFunctionStyle, FIXED_FUNCTION_ACES_OUTPUT_TRANSFORM_20)) .value("FIXED_FUNCTION_ACES_RGB_TO_JMH_20", FIXED_FUNCTION_ACES_RGB_TO_JMH_20, DOC(PyOpenColorIO, FixedFunctionStyle, FIXED_FUNCTION_ACES_RGB_TO_JMH_20)) + .value("FIXED_FUNCTION_ACES_RGB_TO_HMJ_20", FIXED_FUNCTION_ACES_RGB_TO_HMJ_20, + DOC(PyOpenColorIO, FixedFunctionStyle, FIXED_FUNCTION_ACES_RGB_TO_HMJ_20)) .value("FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20", FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20, DOC(PyOpenColorIO, FixedFunctionStyle, FIXED_FUNCTION_ACES_TONESCALE_COMPRESS_20)) .value("FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20", FIXED_FUNCTION_ACES_GAMUT_COMPRESS_20, diff --git a/tests/cpu/fileformats/FileFormatCTF_tests.cpp b/tests/cpu/fileformats/FileFormatCTF_tests.cpp index fad4d45ba6..e46555c74b 100644 --- a/tests/cpu/fileformats/FileFormatCTF_tests.cpp +++ b/tests/cpu/fileformats/FileFormatCTF_tests.cpp @@ -4196,7 +4196,7 @@ void WriteGroupCLF(OCIO::ConstGroupTransformRcPtr group, std::ostringstream & ou 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. + // Validate the load & save for any FixedFunction style. std::ostringstream ffStr; ffStr << "getFormatMetadata().addAttribute(OCIO::METADATA_ID, "UIDFF42"); + group->appendTransform(ff); + + 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, fixed_function_aces_hmj_to_rgb_20_ctf) +{ + const double data[8] = { 0.7347, 0.2653, 0.0000, 1.0000, 0.0001, -0.0770, 0.32168, 0.33767 }; + OCIO::FixedFunctionTransformRcPtr ff = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_ACES_RGB_TO_HMJ_20, &data[0], 8); + ff->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + OCIO::GroupTransformRcPtr group = OCIO::GroupTransform::Create(); + group->getFormatMetadata().addAttribute(OCIO::METADATA_ID, "UIDFF42"); + group->appendTransform(ff); + + 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, exposure_contrast_video_ctf) { OCIO::ExposureContrastTransformRcPtr ec = OCIO::ExposureContrastTransform::Create(); diff --git a/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp b/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp index 7af081d047..03c9dfa739 100644 --- a/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp +++ b/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp @@ -788,6 +788,39 @@ OCIO_ADD_TEST(FixedFunctionOpCPU, aces_rgb_to_jmh_20) funcData2, 1e-4f, __LINE__); + + // FIXED_FUNCTION_ACES_RGB_TO_HMJ_20 repacks the same JMh values as [ h/360, M/200, J/100 ]. + float expected_hmj_32f[num_samples*4]; + for (unsigned idx = 0; idx < num_samples; ++idx) + { + const float J = expected_32f[idx*4 + 0]; + const float M = expected_32f[idx*4 + 1]; + const float h = expected_32f[idx*4 + 2]; + expected_hmj_32f[idx*4 + 0] = h / 360.0f; + expected_hmj_32f[idx*4 + 1] = M / 200.0f; + expected_hmj_32f[idx*4 + 2] = J / 100.0f; + expected_hmj_32f[idx*4 + 3] = expected_32f[idx*4 + 3]; + } + + memcpy(&input2_32f[0], &input_32f[0], sizeof(float)*num_samples * 4); + + OCIO::ConstFixedFunctionOpDataRcPtr funcData3 + = std::make_shared(OCIO::FixedFunctionOpData::ACES_RGB_TO_HMJ_20, + params); + + ApplyFixedFunction(&input2_32f[0], &expected_hmj_32f[0], num_samples, + funcData3, + 1e-5f, + __LINE__); + + OCIO::ConstFixedFunctionOpDataRcPtr funcData4 + = std::make_shared(OCIO::FixedFunctionOpData::ACES_HMJ_TO_RGB_20, + params); + + ApplyFixedFunction(&input2_32f[0], &input_32f[0], num_samples, + funcData4, + 1e-4f, + __LINE__); } OCIO_ADD_TEST(FixedFunctionOpCPU, aces_tonescale_compress_20) diff --git a/tests/cpu/ops/fixedfunction/FixedFunctionOpData_tests.cpp b/tests/cpu/ops/fixedfunction/FixedFunctionOpData_tests.cpp index 0844e11d35..6796860c20 100644 --- a/tests/cpu/ops/fixedfunction/FixedFunctionOpData_tests.cpp +++ b/tests/cpu/ops/fixedfunction/FixedFunctionOpData_tests.cpp @@ -372,4 +372,16 @@ OCIO_ADD_TEST(FixedFunctionOpData, is_inverse) OCIO::FixedFunctionOpData::ACES_GAMUT_COMP_13_INV, p7); OCIO_CHECK_ASSERT(!f_gm_inv->isInverse(f_gm)); OCIO_CHECK_ASSERT(!f_gm->isInverse(f_gm_inv)); + + OCIO::FixedFunctionOpData::Params p8 = + { 0.7347, 0.2653, 0.0000, 1.0000, 0.0001, -0.0770, 0.32168, 0.33767 }; + auto f_hmj = std::make_shared( + OCIO::FixedFunctionOpData::ACES_RGB_TO_HMJ_20, p8); + auto f_hmj_inv = std::make_shared( + OCIO::FixedFunctionOpData::ACES_HMJ_TO_RGB_20, p8); + OCIO_CHECK_ASSERT(f_hmj->isInverse(f_hmj_inv)); + OCIO_CHECK_ASSERT(f_hmj_inv->isInverse(f_hmj)); + OCIO_CHECK_ASSERT(!f_hmj->isInverse(f_hmj)); + OCIO_CHECK_ASSERT(!f_hmj_inv->isInverse(f_hmj_inv)); + OCIO_CHECK_ASSERT(!f_hmj->isInverse(f_gm)); } diff --git a/tests/cpu/transforms/FixedFunctionTransform_tests.cpp b/tests/cpu/transforms/FixedFunctionTransform_tests.cpp index c6c00d3c32..ae38dd29fd 100644 --- a/tests/cpu/transforms/FixedFunctionTransform_tests.cpp +++ b/tests/cpu/transforms/FixedFunctionTransform_tests.cpp @@ -78,6 +78,25 @@ OCIO_ADD_TEST(FixedFunctionTransform, basic) OCIO::Exception, "Unimplemented fixed function types: " "FIXED_FUNCTION_ACES_GAMUTMAP_02, " "FIXED_FUNCTION_ACES_GAMUTMAP_07."); + + OCIO_CHECK_NO_THROW(func->setParams(&values_7[0], 7)); + OCIO_CHECK_NO_THROW(func->setStyle(OCIO::FIXED_FUNCTION_ACES_RGB_TO_HMJ_20)); + OCIO_CHECK_EQUAL(func->getStyle(), OCIO::FIXED_FUNCTION_ACES_RGB_TO_HMJ_20); + OCIO_CHECK_NO_THROW(func->setDirection(OCIO::TRANSFORM_DIR_INVERSE)); + OCIO_CHECK_EQUAL(func->getDirection(), OCIO::TRANSFORM_DIR_INVERSE); + OCIO_CHECK_THROW_WHAT(func->validate(), OCIO::Exception, + "The style 'HMJ_TO_RGB_20' must have " + "8 parameters but 7 found."); + + OCIO::FixedFunctionOpData::Params values_8 = + { 0.7347, 0.2653, 0.0000, 1.0000, 0.0001, -0.0770, 0.32168, 0.33767 }; + OCIO_CHECK_NO_THROW(func->setParams(&values_8[0], 8)); + OCIO_CHECK_EQUAL(func->getNumParams(), 8); + OCIO_CHECK_NO_THROW(func->validate()); + + OCIO_CHECK_NO_THROW(func->setDirection(OCIO::TRANSFORM_DIR_FORWARD)); + OCIO_CHECK_EQUAL(func->getStyle(), OCIO::FIXED_FUNCTION_ACES_RGB_TO_HMJ_20); + OCIO_CHECK_NO_THROW(func->validate()); } OCIO_ADD_TEST(FixedFunctionTransform, createEditableCopy) diff --git a/tests/cpu/transforms/builtins/BuiltinTransformRegistry_tests.cpp b/tests/cpu/transforms/builtins/BuiltinTransformRegistry_tests.cpp index c67245debd..2e2dffe8c5 100644 --- a/tests/cpu/transforms/builtins/BuiltinTransformRegistry_tests.cpp +++ b/tests/cpu/transforms/builtins/BuiltinTransformRegistry_tests.cpp @@ -96,7 +96,7 @@ OCIO_ADD_TEST(Builtins, read_write) // builtin transforms. static constexpr char CONFIG_BUILTIN_TRANSFORMS[] { -R"(ocio_profile_version: 2.4 +R"(ocio_profile_version: 2.6 environment: {} diff --git a/tests/gpu/FixedFunctionOp_test.cpp b/tests/gpu/FixedFunctionOp_test.cpp index 08a6dd4534..402491b77f 100644 --- a/tests/gpu/FixedFunctionOp_test.cpp +++ b/tests/gpu/FixedFunctionOp_test.cpp @@ -777,6 +777,93 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_aces2_nan_bug) test.setErrorThreshold(0.01f); } +namespace +{ +// Shared test data for the FIXED_FUNCTION_ACES_RGB_TO_JMH_20 / FIXED_FUNCTION_ACES_RGB_TO_HMJ_20 +// GPU tests below (ACEScg primaries and secondaries scaled by 4, some OCIO test values, and +// ColorChecker24 SMPTE 2065-1 2021 values). +const std::vector g_aces2_rgb_values = +{ + 2.781808965f, 0.179178253f, -0.022103530f, 1.0f, + 3.344523751f, 3.617862727f, -0.006002689f, 1.0f, + 0.562714786f, 3.438684474f, 0.016100841f, 1.0f, + 1.218191035f, 3.820821747f, 4.022103530f, 1.0f, + 0.655476249f, 0.382137273f, 4.006002689f, 1.0f, + 3.437285214f, 0.561315526f, 3.983899159f, 1.0f, + 0.110000000f, 0.020000000f, 0.040000000f, 0.5f, + 0.710000000f, 0.510000000f, 0.810000000f, 1.0f, + 0.430000000f, 0.820000000f, 0.710000000f, 0.0f, + 0.118770000f, 0.087090000f, 0.058950000f, 1.0f, + 0.400020000f, 0.319160000f, 0.237360000f, 1.0f, + 0.184760000f, 0.203980000f, 0.313110000f, 1.0f, + 0.109010000f, 0.135110000f, 0.064930000f, 1.0f, + 0.266840000f, 0.246040000f, 0.409320000f, 1.0f, + 0.322830000f, 0.462080000f, 0.406060000f, 1.0f, + 0.386050000f, 0.227430000f, 0.057770000f, 1.0f, + 0.138220000f, 0.130370000f, 0.337030000f, 1.0f, + 0.302020000f, 0.137520000f, 0.127580000f, 1.0f, + 0.093100000f, 0.063470000f, 0.135250000f, 1.0f, + 0.348760000f, 0.436540000f, 0.106130000f, 1.0f, + 0.486550000f, 0.366850000f, 0.080610000f, 1.0f, + 0.087320000f, 0.074430000f, 0.272740000f, 1.0f, + 0.153660000f, 0.256920000f, 0.090710000f, 1.0f, + 0.217420000f, 0.070700000f, 0.051300000f, 1.0f, + 0.589190000f, 0.539430000f, 0.091570000f, 1.0f, + 0.309040000f, 0.148180000f, 0.274260000f, 1.0f, + 0.149010000f, 0.233780000f, 0.359390000f, 1.0f, +}; + +// The JMh values corresponding to g_aces2_rgb_values above, run through RGB_TO_JMH_20. +const std::vector g_aces2_jmh_values = +{ + 107.480636597f, 206.827301025f, 25.025110245f, 1.0f, + 173.194076538f, 133.330886841f, 106.183448792f, 1.0f, + 139.210220337f, 191.922363281f, 147.056488037f, 1.0f, + 157.905166626f, 111.975311279f, 192.204727173f, 1.0f, + 79.229278564f, 100.424659729f, 268.442108154f, 1.0f, + 132.888137817f, 173.358779907f, 341.715240479f, 1.0f, + 26.112514496f, 42.523605347f, 4.173158169f, 0.5f, + 79.190460205f, 25.002300262f, 332.159759521f, 1.0f, + 81.912559509f, 39.754810333f, 182.925750732f, 0.0f, + 33.924663544f, 12.254567146f, 38.146659851f, 1.0f, + 61.332393646f, 15.169423103f, 39.841842651f, 1.0f, + 47.191543579f, 11.839941978f, 249.107116699f, 1.0f, + 37.328300476f, 13.224150658f, 128.878036499f, 1.0f, + 53.465549469f, 13.121579170f, 285.658966064f, 1.0f, + 65.414512634f, 19.172147751f, 179.324264526f, 1.0f, + 55.711513519f, 37.182041168f, 50.924011230f, 1.0f, + 40.020961761f, 20.762512207f, 271.008331299f, 1.0f, + 47.704769135f, 35.791145325f, 13.975610733f, 1.0f, + 30.385913849f, 14.544739723f, 317.544281006f, 1.0f, + 64.222846985f, 33.487697601f, 119.145133972f, 1.0f, + 65.570358276f, 35.864013672f, 70.842193604f, 1.0f, + 31.800464630f, 23.920211792f, 273.228973389f, 1.0f, + 47.950405121f, 28.027387619f, 144.154159546f, 1.0f, + 38.440967560f, 42.604164124f, 17.892261505f, 1.0f, + 75.117736816f, 40.952045441f, 90.752044678f, 1.0f, + 49.311210632f, 33.812240601f, 348.832092285f, 1.0f, + 47.441757202f, 22.915655136f, 218.454376221f, 1.0f, +}; + +// Reorders/scales a JMh values vector into the HMJ layout used by FIXED_FUNCTION_ACES_RGB_TO_HMJ_20: +// [ h/360, M/200, J/100 ]. +std::vector JMhToHMJ(const std::vector & jmh) +{ + std::vector hmj(jmh.size()); + for (size_t idx = 0; idx < jmh.size(); idx += 4) + { + const float J = jmh[idx + 0]; + const float M = jmh[idx + 1]; + const float h = jmh[idx + 2]; + hmj[idx + 0] = h / 360.0f; + hmj[idx + 1] = M / 200.0f; + hmj[idx + 2] = J / 100.0f; + hmj[idx + 3] = jmh[idx + 3]; + } + return hmj; +} +} // anon. + OCIO_ADD_GPU_TEST(FixedFunction, style_aces2_rgb_to_jmh_fwd) { // ACES AP0 @@ -788,41 +875,10 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_aces2_rgb_to_jmh_fwd) test.setProcessor(func); OCIOGPUTest::CustomValues values; - values.m_inputValues = - { - // ACEScg primaries and secondaries scaled by 4 - 2.781808965f, 0.179178253f, -0.022103530f, 1.0f, - 3.344523751f, 3.617862727f, -0.006002689f, 1.0f, - 0.562714786f, 3.438684474f, 0.016100841f, 1.0f, - 1.218191035f, 3.820821747f, 4.022103530f, 1.0f, - 0.655476249f, 0.382137273f, 4.006002689f, 1.0f, - 3.437285214f, 0.561315526f, 3.983899159f, 1.0f, - // OCIO test values - 0.110000000f, 0.020000000f, 0.040000000f, 0.5f, - 0.710000000f, 0.510000000f, 0.810000000f, 1.0f, - 0.430000000f, 0.820000000f, 0.710000000f, 0.0f, - // ColorChecker24 (SMPTE 2065-1 2021) - 0.118770000f, 0.087090000f, 0.058950000f, 1.0f, - 0.400020000f, 0.319160000f, 0.237360000f, 1.0f, - 0.184760000f, 0.203980000f, 0.313110000f, 1.0f, - 0.109010000f, 0.135110000f, 0.064930000f, 1.0f, - 0.266840000f, 0.246040000f, 0.409320000f, 1.0f, - 0.322830000f, 0.462080000f, 0.406060000f, 1.0f, - 0.386050000f, 0.227430000f, 0.057770000f, 1.0f, - 0.138220000f, 0.130370000f, 0.337030000f, 1.0f, - 0.302020000f, 0.137520000f, 0.127580000f, 1.0f, - 0.093100000f, 0.063470000f, 0.135250000f, 1.0f, - 0.348760000f, 0.436540000f, 0.106130000f, 1.0f, - 0.486550000f, 0.366850000f, 0.080610000f, 1.0f, - 0.087320000f, 0.074430000f, 0.272740000f, 1.0f, - 0.153660000f, 0.256920000f, 0.090710000f, 1.0f, - 0.217420000f, 0.070700000f, 0.051300000f, 1.0f, - 0.589190000f, 0.539430000f, 0.091570000f, 1.0f, - 0.309040000f, 0.148180000f, 0.274260000f, 1.0f, - 0.149010000f, 0.233780000f, 0.359390000f, 1.0f, - }; + values.m_inputValues = g_aces2_rgb_values; test.setCustomValues(values); + // Large because JMh is scaled in the hundreds. test.setErrorThreshold(2e-4f); } @@ -837,42 +893,49 @@ OCIO_ADD_GPU_TEST(FixedFunction, style_aces2_rgb_to_jmh_inv) test.setProcessor(func); OCIOGPUTest::CustomValues values; - values.m_inputValues = - { - // ACEScg primaries and secondaries scaled by 4 - 107.480636597f, 206.827301025f, 25.025110245f, 1.0f, - 173.194076538f, 133.330886841f, 106.183448792f, 1.0f, - 139.210220337f, 191.922363281f, 147.056488037f, 1.0f, - 157.905166626f, 111.975311279f, 192.204727173f, 1.0f, - 79.229278564f, 100.424659729f, 268.442108154f, 1.0f, - 132.888137817f, 173.358779907f, 341.715240479f, 1.0f, - // OCIO test values - 26.112514496f, 42.523605347f, 4.173158169f, 0.5f, - 79.190460205f, 25.002300262f, 332.159759521f, 1.0f, - 81.912559509f, 39.754810333f, 182.925750732f, 0.0f, - // ColorChecker24 (SMPTE 2065-1 2021) - 33.924663544f, 12.254567146f, 38.146659851f, 1.0f, - 61.332393646f, 15.169423103f, 39.841842651f, 1.0f, - 47.191543579f, 11.839941978f, 249.107116699f, 1.0f, - 37.328300476f, 13.224150658f, 128.878036499f, 1.0f, - 53.465549469f, 13.121579170f, 285.658966064f, 1.0f, - 65.414512634f, 19.172147751f, 179.324264526f, 1.0f, - 55.711513519f, 37.182041168f, 50.924011230f, 1.0f, - 40.020961761f, 20.762512207f, 271.008331299f, 1.0f, - 47.704769135f, 35.791145325f, 13.975610733f, 1.0f, - 30.385913849f, 14.544739723f, 317.544281006f, 1.0f, - 64.222846985f, 33.487697601f, 119.145133972f, 1.0f, - 65.570358276f, 35.864013672f, 70.842193604f, 1.0f, - 31.800464630f, 23.920211792f, 273.228973389f, 1.0f, - 47.950405121f, 28.027387619f, 144.154159546f, 1.0f, - 38.440967560f, 42.604164124f, 17.892261505f, 1.0f, - 75.117736816f, 40.952045441f, 90.752044678f, 1.0f, - 49.311210632f, 33.812240601f, 348.832092285f, 1.0f, - 47.441757202f, 22.915655136f, 218.454376221f, 1.0f, - }; + values.m_inputValues = g_aces2_jmh_values; test.setCustomValues(values); - test.setErrorThreshold(1e-4f); + test.setErrorThreshold(2e-5f); +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_aces2_rgb_to_hmj_fwd) +{ + // Same RGB values as style_aces2_rgb_to_jmh_fwd above -- FIXED_FUNCTION_ACES_RGB_TO_HMJ_20 + // computes the same JMh internally, just repacked as [ h/360, M/200, J/100 ]. + + // ACES AP0 + const double data[8] = { 0.7347, 0.2653, 0.0000, 1.0000, 0.0001, -0.0770, 0.32168, 0.33767 }; + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_ACES_RGB_TO_HMJ_20, &data[0], 8); + func->setDirection(OCIO::TRANSFORM_DIR_FORWARD); + + test.setProcessor(func); + + OCIOGPUTest::CustomValues values; + values.m_inputValues = g_aces2_rgb_values; + test.setCustomValues(values); + + test.setErrorThreshold(1e-6f); +} + +OCIO_ADD_GPU_TEST(FixedFunction, style_aces2_rgb_to_hmj_inv) +{ + // Same JMh values as style_aces2_rgb_to_jmh_inv above, reordered/scaled into HMJ. + + // ACES AP0 + const double data[8] = { 0.7347, 0.2653, 0.0000, 1.0000, 0.0001, -0.0770, 0.32168, 0.33767 }; + OCIO::FixedFunctionTransformRcPtr func = + OCIO::FixedFunctionTransform::Create(OCIO::FIXED_FUNCTION_ACES_RGB_TO_HMJ_20, &data[0], 8); + func->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + + test.setProcessor(func); + + OCIOGPUTest::CustomValues values; + values.m_inputValues = JMhToHMJ(g_aces2_jmh_values); + test.setCustomValues(values); + + test.setErrorThreshold(2e-5f); } OCIO_ADD_GPU_TEST(FixedFunction, style_aces2_tonescale_compress_fwd) From a973e31536ddff4bd91fca1e4c96851043cfacd6 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Sun, 30 Aug 2026 23:01:26 -0400 Subject: [PATCH 4/4] Fix unit tests Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorTypes.h | 2 +- src/OpenColorIO/Config.cpp | 2 +- tests/cpu/Config_tests.cpp | 48 ++++++++++++++++++- .../cpu/transforms/BuiltinTransform_tests.cpp | 40 +++++++++++++++- 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 782fa03309..9a42a9a3e0 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -516,9 +516,9 @@ enum FixedFunctionStyle FIXED_FUNCTION_LIN_TO_DOUBLE_LOG, ///< Two parameterized LogAffineTransforms with a middle linear segment 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_RGB_TO_HMJ_20, ///< ACES 2.0 RGB to HMJ (h/360, M/200, J/100) -- 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_RGB_TO_HMJ_20, ///< ACES 2.0 RGB to HMJ (h/360, M/200, J/100) 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 diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index ccff83c97d..cd0bc01cea 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -5786,7 +5786,7 @@ void Config::Impl::checkVersionConsistency(ConstTransformRcPtr & transform) cons if (m_majorVersion == 2 && m_minorVersion < 5 ) { - if( ffstyle == FIXED_FUNCTION_RGB_TO_HSY_LIN || + if( ffstyle == FIXED_FUNCTION_RGB_TO_HSY_LIN || ffstyle == FIXED_FUNCTION_RGB_TO_HSY_LOG || ffstyle == FIXED_FUNCTION_RGB_TO_HSY_VID ) { diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index 28d5e430b1..5fc9300564 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 5"); + "Maximum minor version is 6"); 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 5"); + "Maximum minor version is 6"); } { @@ -5356,6 +5356,50 @@ R"([OpenColorIO Warning]: FixedFunction style is experimental and may be removed } } + { + const std::string strEnd = + " from_scene_reference: !\n" + " children:\n" + " - ! {style: ACES2_RGB_TO_HMJ, params: [0.64, 0.33, 0.3, 0.6, 0.15, 0.06, 0.3127, 0.329]}\n" + " - ! {style: ACES2_RGB_TO_HMJ, params: [0.64, 0.33, 0.3, 0.6, 0.15, 0.06, 0.3127, 0.329], direction: inverse}\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, + "Only config version 2.6 (or higher) can have FixedFunctionTransform style 'ACES2_RGB_TO_HMJ'."); + } + + { + const std::string str = PROFILE_START_V<2, 6>() + strEnd; + + std::istringstream is; + is.str(str); + + OCIO_CHECK_NO_THROW(OCIO::Config::CreateFromStream(is)); + } + + { + const std::string str2End = + " from_scene_reference: !\n" + " children:\n" + " - ! {style: ACES2_RGB_TO_HMJ, params: [0.64, 0.33, 0.3, 0.6, 0.15, 0.06, 0.3127, 0.329, 0.], direction: inverse}\n"; + + const std::string str = PROFILE_START_V<2, 6>() + str2End; + + std::istringstream is; + is.str(str); + + OCIO::ConstConfigRcPtr config; + OCIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is)); + OCIO_CHECK_THROW_WHAT(config->validate(), OCIO::Exception, + "The style 'HMJ_TO_RGB_20' must have 8 parameters but 9 found."); + } + } + { const std::string strEnd = " from_scene_reference: !\n" diff --git a/tests/cpu/transforms/BuiltinTransform_tests.cpp b/tests/cpu/transforms/BuiltinTransform_tests.cpp index 62d8b1fc24..7cb447f881 100644 --- a/tests/cpu/transforms/BuiltinTransform_tests.cpp +++ b/tests/cpu/transforms/BuiltinTransform_tests.cpp @@ -773,7 +773,7 @@ namespace { void ValidateDisplayViewRoundTrip(const char * display_style, const char * view_style, - float scale, float errorThreshold, + float scale, float errorThreshold, bool applyLMT, std::vector difficultItems, float difficultThreshold, int lineNo) { @@ -791,11 +791,23 @@ void ValidateDisplayViewRoundTrip(const char * display_style, const char * view_ auto view_builtin_inv = view_builtin->createEditableCopy(); view_builtin_inv->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + // Built-in transform for the (optional) look. + OCIO::BuiltinTransformRcPtr look_builtin = OCIO::BuiltinTransform::Create(); + OCIO_CHECK_NO_THROW_FROM(look_builtin->setStyle("ACES-LMT - ACES 2.0 DCC Look 1"), lineNo); + OCIO_CHECK_NO_THROW_FROM(look_builtin->validate(), lineNo); + auto look_builtin_inv = look_builtin->createEditableCopy(); + look_builtin_inv->setDirection(OCIO::TRANSFORM_DIR_INVERSE); + // Assemble inverse and forward transform into a group transform that goes from // display code values to ACES and back to code values. OCIO::GroupTransformRcPtr group = OCIO::GroupTransform::Create(); group->appendTransform(display_builtin_inv); group->appendTransform(view_builtin_inv); + if (applyLMT) + { + group->appendTransform(look_builtin_inv); + group->appendTransform(look_builtin); + } group->appendTransform(view_builtin); group->appendTransform(display_builtin); @@ -878,6 +890,7 @@ OCIO_ADD_TEST(Builtins, aces2_displayview_roundtrip) "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-REC709_2.0", 1.0f, // scale factor 0.004f, // tolerance + false, // apply LMT {}, 0.f, __LINE__); @@ -885,6 +898,7 @@ OCIO_ADD_TEST(Builtins, aces2_displayview_roundtrip) "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-P3-D65_2.0", 1.0f, // scale factor 0.001f, // tolerance + false, // apply LMT {}, 0.f, __LINE__); @@ -893,6 +907,7 @@ OCIO_ADD_TEST(Builtins, aces2_displayview_roundtrip) // Need to lower the max value from 1000 to 990 nits. 0.7507f, // scale factor = 990 nits 0.005f, // main tolerance + false, // apply LMT {168, 196, 364, 392, 1344}, // difficult values 0.03f, // tolerance for difficult values __LINE__); @@ -902,6 +917,7 @@ OCIO_ADD_TEST(Builtins, aces2_displayview_roundtrip) // Need to lower the max value from 4000 to 3860 nits. 0.8987f, // scale factor = 3860 nits 0.007f, // main tolerance + false, // apply LMT {168, 196, 392, 396, 588, 592, 952, 1148, 1196, 1200, 1260, 1288}, 0.2f, // tolerance for difficult values __LINE__); @@ -911,13 +927,35 @@ OCIO_ADD_TEST(Builtins, aces2_displayview_roundtrip) // "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-1000nit-REC2020_2.0", // 0.7507f, // scale factor = 990 nits // 5e-3f, // tolerance + // false, // apply LMT // __LINE__); // // ValidateDisplayViewRoundTrip("DISPLAY - CIE-XYZ-D65_to_REC.2100-PQ", // "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-4000nit-REC2020_2.0", // 0.8987f, // scale factor = 3860 nits // 5e-3f, // tolerance + // false, // apply LMT // __LINE__); + + // Test the SDR transforms with an LMT in place. + + ValidateDisplayViewRoundTrip("DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.709", + "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-REC709_2.0", + 1.0f, // scale factor + 0.004f, // tolerance + true, // apply LMT + {192, // {1, 1, 0} leaves 0.0053 in blue + 1344}, // {0, 1, 1} leaves 0.0044 in red + 0.006f, // tolerance for difficult values + __LINE__); + + ValidateDisplayViewRoundTrip("DISPLAY - CIE-XYZ-D65_to_DisplayP3", + "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-P3-D65_2.0", + 1.0f, // scale factor + 0.001f, // tolerance + true, // apply LMT + {}, 0.f, + __LINE__); } OCIO_ADD_TEST(Builtins, aces2_Aab_to_RGB_nan)