diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index c3d63698b..d6d4201ee 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -5234,6 +5234,8 @@ void Config::Impl::checkVersionConsistency(ConstTransformRcPtr & transform) cons if (m_majorVersion == 2 && m_minorVersion < 4 && ( 0 == Platform::Strcasecmp(blt->getStyle(), "APPLE_LOG_to_ACES2065-1") || 0 == Platform::Strcasecmp(blt->getStyle(), "CURVE - APPLE_LOG_to_LINEAR") + || 0 == Platform::Strcasecmp(blt->getStyle(), "DISPLAY - CIE-XYZ-D65_to_DCDM-D65") + || 0 == Platform::Strcasecmp(blt->getStyle(), "DISPLAY - CIE-XYZ-D65_to_ST2084-DCDM-D65") || 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-REC709_2.0") || 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - SDR-100nit-P3-D65_2.0") || 0 == Platform::Strcasecmp(blt->getStyle(), "ACES-OUTPUT - ACES2065-1_to_CIE-XYZ-D65 - HDR-108nit-P3-D65_2.0") diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp index 3cc15ecf6..a1cd68a79 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpCPU.cpp @@ -212,6 +212,7 @@ class Renderer_REC2100_Surround : public OpCPU protected: float m_gamma; + float m_minLum; }; class Renderer_RGB_TO_HSV : public OpCPU @@ -1171,7 +1172,11 @@ Renderer_REC2100_Surround::Renderer_REC2100_Surround(ConstFixedFunctionOpDataRcP : OpCPU() { const auto fwd = FixedFunctionOpData::REC2100_SURROUND_FWD == data->getStyle(); - const float gamma = fwd ? (float)data->getParams()[0] : (float)(1. / data->getParams()[0]); + float gamma = (float)data->getParams()[0]; + + m_minLum = fwd ? 1e-4f : powf(1e-4f, gamma); + + gamma = fwd ? gamma : 1.f / gamma; m_gamma = gamma - 1.f; // compute Y^gamma / Y } @@ -1187,22 +1192,20 @@ void Renderer_REC2100_Surround::apply(const void * inImg, void * outImg, long nu const float grn = in[1]; const float blu = in[2]; + // Calculate luminance assuming input is Rec.2100 RGB. + float Y = 0.2627f * red + 0.6780f * grn + 0.0593f * blu; + + // Mirror the function around the origin. + Y = std::abs(Y); + + // Since the slope may approach infinity as Y approaches 0, limit the min value + // to avoid gaining up the RGB values (which may not be as close to 0). + // // This threshold needs to be bigger than 1e-10 (used above) to prevent extreme // gain in dark colors, yet smaller than 1e-2 to prevent distorting the shape of // the HLG EOTF curve. Max gain = 1e-4 ** (0.78-1) = 7.6 for HLG min gamma of 0.78. - // - // TODO: Should have forward & reverse versions of this so the threshold can be - // adjusted correctly for the reverse direction. - constexpr float minLum = 1e-4f; + Y = std::max(m_minLum, Y); - // Calculate luminance assuming input is Rec.2100 RGB. - // TODO: Add another parameter to allow using other primaries. - const float Y = std::max( minLum, ( 0.2627f * red + - 0.6780f * grn + - 0.0593f * blu ) ); - - // TODO: Currently our fast approx. requires SSE registers. - // Either make this whole routine SSE or make fast scalar pow. const float Ypow_over_Y = powf(Y, m_gamma); out[0] = red * Ypow_over_Y; diff --git a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp index 7161deafb..b34c5bc1d 100644 --- a/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp +++ b/src/OpenColorIO/ops/fixedfunction/FixedFunctionOpGPU.cpp @@ -1663,15 +1663,24 @@ void Add_Surround_10_Fwd_Shader(GpuShaderCreatorRcPtr & shaderCreator, GpuShader ss.newLine() << pxl << ".rgb = " << pxl << ".rgb * Ypow_over_Y;"; } -void Add_Surround_Shader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, float gamma) +void Add_Rec2100_Surround_Shader(GpuShaderCreatorRcPtr & shaderCreator, GpuShaderText & ss, + float gamma, bool isForward) { const std::string pxl(shaderCreator->getPixelName()); - // TODO: -- add vector inner product to GPUShaderUtils + float minLum = 1e-4f; + if (!isForward) + { + minLum = powf(minLum, gamma); + gamma = 1.f / gamma; + } + ss.newLine() << ss.floatDecl("Y") - << " = max( 1e-4, 0.2627 * " << pxl << ".rgb.r + " - << "0.6780 * " << pxl << ".rgb.g + " - << "0.0593 * " << pxl << ".rgb.b );"; + << " = 0.2627 * " << pxl << ".rgb.r + " + << "0.6780 * " << pxl << ".rgb.g + " + << "0.0593 * " << pxl << ".rgb.b;"; + + ss.newLine() << "Y = max( " << minLum << ", abs(Y) );"; ss.newLine() << ss.floatDecl("Ypow_over_Y") << " = pow( Y, " << (gamma - 1.f) << ");"; @@ -1965,12 +1974,14 @@ void GetFixedFunctionGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, } case FixedFunctionOpData::REC2100_SURROUND_FWD: { - Add_Surround_Shader(shaderCreator, ss, (float) func->getParams()[0]); + Add_Rec2100_Surround_Shader(shaderCreator, ss, + (float) func->getParams()[0], true); break; } case FixedFunctionOpData::REC2100_SURROUND_INV: { - Add_Surround_Shader(shaderCreator, ss, (float)(1. / func->getParams()[0])); + Add_Rec2100_Surround_Shader(shaderCreator, ss, + (float) func->getParams()[0], false); break; } case FixedFunctionOpData::RGB_TO_HSV: diff --git a/src/OpenColorIO/transforms/builtins/Displays.cpp b/src/OpenColorIO/transforms/builtins/Displays.cpp index 8d8af9782..31f4fd2e6 100644 --- a/src/OpenColorIO/transforms/builtins/Displays.cpp +++ b/src/OpenColorIO/transforms/builtins/Displays.cpp @@ -201,6 +201,25 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept CIE_XYZ_D65_to_P3_D60_BFD_Functor); } + { + auto CIE_XYZ_D65_to_DCDM_D65_Functor = [](OpRcPtrVec & ops) + { + const double scale = 48.0 / 52.37; + const double scale4[4] = { scale, scale, scale, 1. }; + CreateScaleOp(ops, scale4, TRANSFORM_DIR_FORWARD); + + const GammaOpData::Params rgbParams = { 2.6 }; + const GammaOpData::Params alphaParams = { 1.0 }; + auto gammaData = std::make_shared(GammaOpData::BASIC_REV, + rgbParams, rgbParams, rgbParams, alphaParams); + CreateGammaOp(ops, gammaData, TRANSFORM_DIR_FORWARD); + }; + + registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_DCDM-D65", + "Convert CIE XYZ (D65 white) to Gamma 2.6 (D65 white in XYZ-E encoding)", + CIE_XYZ_D65_to_DCDM_D65_Functor); + } + { auto CIE_XYZ_D65_to_DisplayP3_Functor = [](OpRcPtrVec & ops) { @@ -285,6 +304,17 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept CIE_XYZ_D65_to_ST2084_P3_D65_Functor); } + { + auto CIE_XYZ_D65_to_ST2084_DCDM_D65_Functor = [](OpRcPtrVec & ops) + { + ST_2084::GenerateLinearToPQOps(ops); + }; + + registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_ST2084-DCDM-D65", + "Convert CIE XYZ (D65 white) to ST-2084 (PQ) (D65 white in XYZ-E encoding)", + CIE_XYZ_D65_to_ST2084_DCDM_D65_Functor); + } + { auto CIE_XYZ_D65_to_REC2100_HLG_1000nit_Functor = [](OpRcPtrVec & ops) { diff --git a/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp b/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp index 02d0d133f..4ad549731 100644 --- a/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp +++ b/tests/cpu/ops/fixedfunction/FixedFunctionOpCPU_tests.cpp @@ -927,44 +927,83 @@ OCIO_ADD_TEST(FixedFunctionOpCPU, aces_gamut_map_20) OCIO_ADD_TEST(FixedFunctionOpCPU, rec2100_surround) { - const unsigned num_samples = 4; + const unsigned num_samples = 5; float input_32f[num_samples*4] = { - 0.11f, 0.02f, 0.04f, 0.5f, - 0.71f, 0.51f, 0.81f, 1.0f, - 0.43f, 0.82f, 0.71f, 0.0f, - -1.0f, -0.001f, 1.2f, 0.0f + 8.4e-5f, 2.4e-5f, 1.4e-4f, 0.1f, + 0.11f, 0.02f, 0.04f, 0.5f, + 0.71f, 0.51f, 0.81f, 1.0f, + 0.43f, 0.82f, 0.71f, 0.0f, + -1.00f, -0.001f, 1.2f, 0.0f }; + { + OCIO::FixedFunctionOpData::Params params = { 0.78 }; - float input2_32f[num_samples * 4]; - memcpy(&input2_32f[0], &input_32f[0], sizeof(float)*num_samples * 4); + float output_32f[num_samples * 4]; + memcpy(&output_32f[0], &input_32f[0], sizeof(float)*num_samples * 4); - const float expected_32f[num_samples*4] = { - 0.21779590f, 0.03959925f, 0.07919850f, 0.5f, - 0.80029451f, 0.57485944f, 0.91301214f, 1.0f, - 0.46350446f, 0.88389223f, 0.76532131f, 0.0f, - -7.58577776f, -0.00758577f, 9.10293388f, 0.0f, - }; + const float expected_32f[num_samples*4] = { + 0.000637205163f, 0.000182058618f, 0.001062008605f, 0.1f, + 0.21779590f, 0.03959925f, 0.07919850f, 0.5f, + 0.80029451f, 0.57485944f, 0.91301214f, 1.0f, + 0.46350446f, 0.88389223f, 0.76532131f, 0.0f, + -1.43735918f, -0.00143735918f, 1.72483102f, 0.0f + }; - OCIO::FixedFunctionOpData::Params params = { 0.78 }; - OCIO::ConstFixedFunctionOpDataRcPtr funcData - = std::make_shared(OCIO::FixedFunctionOpData::REC2100_SURROUND_FWD, - params); + // Forward transform -- input to expected. + OCIO::ConstFixedFunctionOpDataRcPtr funcData + = std::make_shared(OCIO::FixedFunctionOpData::REC2100_SURROUND_FWD, + params); - ApplyFixedFunction(&input_32f[0], &expected_32f[0], num_samples, - funcData, - 1e-7f, - __LINE__); + ApplyFixedFunction(&output_32f[0], &expected_32f[0], num_samples, + funcData, + 4e-7f, + __LINE__); - OCIO::FixedFunctionOpData::Params params_inv = { 1 / 0.78 }; - OCIO::ConstFixedFunctionOpDataRcPtr funcData2 - = std::make_shared(OCIO::FixedFunctionOpData::REC2100_SURROUND_INV, - params_inv); + // Inverse transform -- output back to original. + OCIO::ConstFixedFunctionOpDataRcPtr funcDataInv + = std::make_shared(OCIO::FixedFunctionOpData::REC2100_SURROUND_INV, + params); - ApplyFixedFunction(&input2_32f[0], &expected_32f[0], num_samples, - funcData2, - 1e-7f, - __LINE__); + ApplyFixedFunction(&output_32f[0], &input_32f[0], num_samples, + funcDataInv, + 3e-7f, + __LINE__); + } + { + OCIO::FixedFunctionOpData::Params params = { 1.2 }; + + float output_32f[num_samples * 4]; + memcpy(&output_32f[0], &input_32f[0], sizeof(float)*num_samples * 4); + + const float expected_32f[num_samples*4] = { + 1.331310281667e-05f, 3.803743661907e-06f, 2.218850469446e-05f, 0.1f, + 0.059115925805f, 0.010748350146f, 0.021496700293f, 0.5f, + 0.636785774786f, 0.457409500198f, 0.726473912080f, 1.0f, + 0.401647721515f, 0.765932864285f, 0.663185772735f, 0.0f, + -7.190495367684e-01f, -7.190495367684e-04f, 8.628594441221e-01f, 0.0f + }; + + // Forward transform -- input to expected. + OCIO::ConstFixedFunctionOpDataRcPtr funcData + = std::make_shared(OCIO::FixedFunctionOpData::REC2100_SURROUND_FWD, + params); + + ApplyFixedFunction(&output_32f[0], &expected_32f[0], num_samples, + funcData, + 2e-7f, + __LINE__); + + // Inverse transform -- output back to original. + OCIO::ConstFixedFunctionOpDataRcPtr funcDataInv + = std::make_shared(OCIO::FixedFunctionOpData::REC2100_SURROUND_INV, + params); + + ApplyFixedFunction(&output_32f[0], &input_32f[0], num_samples, + funcDataInv, + 2e-7f, + __LINE__); + } } OCIO_ADD_TEST(FixedFunctionOpCPU, RGB_TO_HSV) diff --git a/tests/cpu/transforms/BuiltinTransform_tests.cpp b/tests/cpu/transforms/BuiltinTransform_tests.cpp index 52304d2a3..47a7b44da 100644 --- a/tests/cpu/transforms/BuiltinTransform_tests.cpp +++ b/tests/cpu/transforms/BuiltinTransform_tests.cpp @@ -538,6 +538,8 @@ AllValues UnitTestValues { { 0.5f, 0.4f, 0.3f }, { 0.896805202281f, 0.627254277624f, 0.608228132100f } } }, { "DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D60-BFD", { { 0.5f, 0.4f, 0.3f }, { 0.892433142142f, 0.627011653770f, 0.608093643982f } } }, + { "DISPLAY - CIE-XYZ-D65_to_DCDM-D65", + { { 0.5f, 0.4f, 0.3f }, { 0.740738422348f, 0.679816639411f, 0.608609083713f } } }, { "DISPLAY - CIE-XYZ-D65_to_DisplayP3", { { 0.5f, 0.4f, 0.3f }, { 0.882580907776f, 0.581526360743f, 0.5606367050000f } } }, @@ -549,6 +551,8 @@ AllValues UnitTestValues { { 0.5f, 0.4f, 0.3f }, { 0.464008302136f, 0.398157119110f, 0.384828370950f } } }, { "DISPLAY - CIE-XYZ-D65_to_ST2084-P3-D65", { { 0.5f, 0.4f, 0.3f }, { 0.479939091128f, 0.392091860770f, 0.384886051856f } } }, + { "DISPLAY - CIE-XYZ-D65_to_ST2084-DCDM-D65", + { { 0.5f, 0.4f, 0.3f }, { 0.440281573420f, 0.419284117712f, 0.392876186489f } } }, { "DISPLAY - CIE-XYZ-D65_to_REC.2100-HLG-1000nit", { { 0.5f, 0.4f, 0.3f }, { 0.5649694f, 0.4038837f, 0.3751478f } } } }; @@ -589,4 +593,8 @@ OCIO_ADD_TEST(Builtins, validate) ValidateBuiltinTransform(name, values.first, values.second, __LINE__); } } + + // The above checks if a test values is missing, but not if there are test values + // that don't have an associated built-in. + OCIO_CHECK_EQUAL(UnitTestValues.size(), reg->getNumBuiltins()); }