From b8f0d0986b70b341fe0b9d64da9497038da1063f Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Wed, 27 Aug 2025 22:11:57 -0400 Subject: [PATCH] Add mirrored displays Signed-off-by: Doug Walker --- .../transforms/builtins/Displays.cpp | 119 ++++++++++++++---- .../cpu/transforms/BuiltinTransform_tests.cpp | 35 +++++- 2 files changed, 126 insertions(+), 28 deletions(-) diff --git a/src/OpenColorIO/transforms/builtins/Displays.cpp b/src/OpenColorIO/transforms/builtins/Displays.cpp index bd6f0c5e7c..a3e7689b13 100644 --- a/src/OpenColorIO/transforms/builtins/Displays.cpp +++ b/src/OpenColorIO/transforms/builtins/Displays.cpp @@ -176,27 +176,44 @@ void GenerateLinearToHLGOps(OpRcPtrVec& ops) void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept { + using ConversionFunctor = std::function; + { - auto CIE_XYZ_D65_to_REC1886_REC709_Functor = [](OpRcPtrVec & ops) + auto CIE_XYZ_D65_to_REC1886_REC709 = [](OpRcPtrVec & ops, GammaOpData::Style gammaStyle) { - MatrixOpData::MatrixArrayPtr matrix + MatrixOpData::MatrixArrayPtr matrix = build_conversion_matrix_from_XYZ_D65(REC709::primaries, ADAPTATION_NONE); CreateMatrixOp(ops, matrix, TRANSFORM_DIR_FORWARD); const GammaOpData::Params rgbParams = { 2.4 }; const GammaOpData::Params alphaParams = { 1.0 }; - auto gammaData = std::make_shared(GammaOpData::BASIC_REV, - rgbParams, rgbParams, rgbParams, alphaParams); + + auto gammaData = std::make_shared(gammaStyle, + rgbParams, rgbParams, rgbParams, alphaParams); CreateGammaOp(ops, gammaData, TRANSFORM_DIR_FORWARD); }; + ConversionFunctor CIE_XYZ_D65_to_REC1886_REC709_BASIC = + [CIE_XYZ_D65_to_REC1886_REC709](OpRcPtrVec& ops) + { + CIE_XYZ_D65_to_REC1886_REC709(ops, GammaOpData::BASIC_REV); + }; registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.709", - "Convert CIE XYZ (D65 white) to Rec.1886/Rec.709 (HD video)", - CIE_XYZ_D65_to_REC1886_REC709_Functor); + "Convert CIE XYZ (D65 white) to Rec.1886/Rec.709, clamp neg. values", + CIE_XYZ_D65_to_REC1886_REC709_BASIC); + + ConversionFunctor CIE_XYZ_D65_to_REC1886_REC709_MIRROR = + [CIE_XYZ_D65_to_REC1886_REC709](OpRcPtrVec& ops) + { + CIE_XYZ_D65_to_REC1886_REC709(ops, GammaOpData::BASIC_MIRROR_REV); + }; + registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.709 - MIRROR NEGS", + "Convert CIE XYZ (D65 white) to Rec.1886/Rec.709, mirror neg. values", + CIE_XYZ_D65_to_REC1886_REC709_MIRROR); } { - auto CIE_XYZ_D65_to_REC1886_REC2020_Functor = [](OpRcPtrVec & ops) + auto CIE_XYZ_D65_to_REC1886_REC2020 = [](OpRcPtrVec & ops, GammaOpData::Style gammaStyle) { MatrixOpData::MatrixArrayPtr matrix = build_conversion_matrix_from_XYZ_D65(REC2020::primaries, ADAPTATION_NONE); @@ -204,18 +221,32 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept const GammaOpData::Params rgbParams = { 2.4 }; const GammaOpData::Params alphaParams = { 1.0 }; - auto gammaData = std::make_shared(GammaOpData::BASIC_REV, + auto gammaData = std::make_shared(gammaStyle, rgbParams, rgbParams, rgbParams, alphaParams); CreateGammaOp(ops, gammaData, TRANSFORM_DIR_FORWARD); }; + ConversionFunctor CIE_XYZ_D65_to_REC1886_REC2020_BASIC = + [CIE_XYZ_D65_to_REC1886_REC2020](OpRcPtrVec& ops) + { + CIE_XYZ_D65_to_REC1886_REC2020(ops, GammaOpData::BASIC_REV); + }; registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.2020", - "Convert CIE XYZ (D65 white) to Rec.1886/Rec.2020 (UHD video)", - CIE_XYZ_D65_to_REC1886_REC2020_Functor); + "Convert CIE XYZ (D65 white) to Rec.1886/Rec.2020, clamp neg. values", + CIE_XYZ_D65_to_REC1886_REC2020_BASIC); + + ConversionFunctor CIE_XYZ_D65_to_REC1886_REC2020_MIRROR = + [CIE_XYZ_D65_to_REC1886_REC2020](OpRcPtrVec& ops) + { + CIE_XYZ_D65_to_REC1886_REC2020(ops, GammaOpData::BASIC_MIRROR_REV); + }; + registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.2020 - MIRROR NEGS", + "Convert CIE XYZ (D65 white) to Rec.1886/Rec.2020, mirror neg. values", + CIE_XYZ_D65_to_REC1886_REC2020_MIRROR); } { - auto CIE_XYZ_D65_to_G22_REC709_Functor = [](OpRcPtrVec & ops) + auto CIE_XYZ_D65_to_G22_REC709 = [](OpRcPtrVec & ops, GammaOpData::Style gammaStyle) { MatrixOpData::MatrixArrayPtr matrix = build_conversion_matrix_from_XYZ_D65(REC709::primaries, ADAPTATION_NONE); @@ -223,18 +254,32 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept const GammaOpData::Params rgbParams = { 2.2 }; const GammaOpData::Params alphaParams = { 1.0 }; - auto gammaData = std::make_shared(GammaOpData::BASIC_REV, + auto gammaData = std::make_shared(gammaStyle, rgbParams, rgbParams, rgbParams, alphaParams); CreateGammaOp(ops, gammaData, TRANSFORM_DIR_FORWARD); }; + ConversionFunctor CIE_XYZ_D65_to_G22_REC709_BASIC = + [CIE_XYZ_D65_to_G22_REC709](OpRcPtrVec& ops) + { + CIE_XYZ_D65_to_G22_REC709(ops, GammaOpData::BASIC_REV); + }; registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_G2.2-REC.709", - "Convert CIE XYZ (D65 white) to Gamma2.2, Rec.709", - CIE_XYZ_D65_to_G22_REC709_Functor); + "Convert CIE XYZ (D65 white) to Gamma2.2, Rec.709, clamp neg. values", + CIE_XYZ_D65_to_G22_REC709_BASIC); + + ConversionFunctor CIE_XYZ_D65_to_G22_REC709_MIRROR = + [CIE_XYZ_D65_to_G22_REC709](OpRcPtrVec& ops) + { + CIE_XYZ_D65_to_G22_REC709(ops, GammaOpData::BASIC_MIRROR_REV); + }; + registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_G2.2-REC.709 - MIRROR NEGS", + "Convert CIE XYZ (D65 white) to Gamma2.2, Rec.709, mirror neg. values", + CIE_XYZ_D65_to_G22_REC709_MIRROR); } { - auto CIE_XYZ_D65_to_SRGB_Functor = [](OpRcPtrVec & ops) + auto CIE_XYZ_D65_to_SRGB = [](OpRcPtrVec & ops, GammaOpData::Style gammaStyle) { MatrixOpData::MatrixArrayPtr matrix = build_conversion_matrix_from_XYZ_D65(REC709::primaries, ADAPTATION_NONE); @@ -242,14 +287,28 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept const GammaOpData::Params rgbParams = { 2.4, 0.055 }; const GammaOpData::Params alphaParams = { 1.0, 0.0 }; - auto gammaData = std::make_shared(GammaOpData::MONCURVE_REV, + auto gammaData = std::make_shared(gammaStyle, rgbParams, rgbParams, rgbParams, alphaParams); CreateGammaOp(ops, gammaData, TRANSFORM_DIR_FORWARD); }; + ConversionFunctor CIE_XYZ_D65_to_SRGB_MONCURVE = + [CIE_XYZ_D65_to_SRGB](OpRcPtrVec& ops) + { + CIE_XYZ_D65_to_SRGB(ops, GammaOpData::MONCURVE_REV); + }; registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_sRGB", "Convert CIE XYZ (D65 white) to sRGB (piecewise EOTF)", - CIE_XYZ_D65_to_SRGB_Functor); + CIE_XYZ_D65_to_SRGB_MONCURVE); + + ConversionFunctor CIE_XYZ_D65_to_SRGB_MIRROR = + [CIE_XYZ_D65_to_SRGB](OpRcPtrVec& ops) + { + CIE_XYZ_D65_to_SRGB(ops, GammaOpData::MONCURVE_MIRROR_REV); + }; + registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_sRGB - MIRROR NEGS", + "Convert CIE XYZ (D65 white) to sRGB (piecewise EOTF), mirror neg. values", + CIE_XYZ_D65_to_SRGB_MIRROR); } { @@ -272,7 +331,7 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept } { - auto CIE_XYZ_D65_to_P3_D65_Functor = [](OpRcPtrVec & ops) + auto CIE_XYZ_D65_to_P3_D65 = [](OpRcPtrVec & ops, GammaOpData::Style gammaStyle) { MatrixOpData::MatrixArrayPtr matrix = build_conversion_matrix_from_XYZ_D65(P3_D65::primaries, ADAPTATION_NONE); @@ -280,14 +339,28 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept const GammaOpData::Params rgbParams = { 2.6 }; const GammaOpData::Params alphaParams = { 1.0 }; - auto gammaData = std::make_shared(GammaOpData::BASIC_REV, + auto gammaData = std::make_shared(gammaStyle, rgbParams, rgbParams, rgbParams, alphaParams); CreateGammaOp(ops, gammaData, TRANSFORM_DIR_FORWARD); }; + ConversionFunctor CIE_XYZ_D65_to_P3_D65_BASIC = + [CIE_XYZ_D65_to_P3_D65](OpRcPtrVec& ops) + { + CIE_XYZ_D65_to_P3_D65(ops, GammaOpData::BASIC_REV); + }; registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D65", - "Convert CIE XYZ (D65 white) to Gamma 2.6, P3-D65", - CIE_XYZ_D65_to_P3_D65_Functor); + "Convert CIE XYZ (D65 white) to Gamma 2.6, P3-D65, clamp neg. values", + CIE_XYZ_D65_to_P3_D65_BASIC); + + ConversionFunctor CIE_XYZ_D65_to_P3_D65_MIRROR = + [CIE_XYZ_D65_to_P3_D65](OpRcPtrVec& ops) + { + CIE_XYZ_D65_to_P3_D65(ops, GammaOpData::BASIC_MIRROR_REV); + }; + registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D65 - MIRROR NEGS", + "Convert CIE XYZ (D65 white) to Gamma 2.6, P3-D65, mirror neg. values", + CIE_XYZ_D65_to_P3_D65_MIRROR); } { @@ -356,13 +429,13 @@ void RegisterAll(BuiltinTransformRegistryImpl & registry) noexcept }; registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_DisplayP3", - "Convert CIE XYZ (D65 white) to Apple Display P3", + "Convert CIE XYZ (D65 white) to Apple Display P3, mirror neg. values", CIE_XYZ_D65_to_DisplayP3_Functor); // NOTE: This builtin is defined to be able to partition SDR and HDR view transforms under two separate // displays rather than a single one. registry.addBuiltin("DISPLAY - CIE-XYZ-D65_to_DisplayP3-HDR", - "Convert CIE XYZ (D65 white) to Apple Display P3 (HDR)", + "Convert CIE XYZ (D65 white) to Apple Display P3 (HDR), mirror neg. values", CIE_XYZ_D65_to_DisplayP3_Functor); } diff --git a/tests/cpu/transforms/BuiltinTransform_tests.cpp b/tests/cpu/transforms/BuiltinTransform_tests.cpp index abb7ae8bb5..cc57edf412 100644 --- a/tests/cpu/transforms/BuiltinTransform_tests.cpp +++ b/tests/cpu/transforms/BuiltinTransform_tests.cpp @@ -636,22 +636,47 @@ AllValues UnitTestValues { "DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.709", { 1.0e-6f, - { 0.5f, 0.4f, 0.3f }, { 0.937245093108f, 0.586817090358f, 0.573498106368f } } }, + { 0.5f, 0.4f, 0.3f, -0.05f, 0.05f, 1.25f }, + { 0.937245093108f, 0.586817090358f, 0.573498106368f, 0.f, 0.505174310421f, 1.118456082347f } } }, + { "DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.709 - MIRROR NEGS", + { 1.0e-6f, + { 0.5f, 0.4f, 0.3f, -0.05f, 0.05f, 1.25f }, + { 0.937245093108f, 0.586817090358f, 0.573498106368f, -0.940082660458f, 0.505174310421f, 1.118456082347f } } }, { "DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.2020", { 1.0e-6f, - { 0.5f, 0.4f, 0.3f }, { 0.830338272693f, 0.620393283803f, 0.583385370254f } } }, + { 0.5f, 0.4f, 0.3f, -0.05f, 0.05f, 1.25f }, + { 0.830338272693f, 0.620393283803f, 0.583385370254f, 0.f, 0.432629991358f, 1.069355537167f } } }, + { "DISPLAY - CIE-XYZ-D65_to_REC.1886-REC.2020 - MIRROR NEGS", + { 1.0e-6f, + { 0.5f, 0.4f, 0.3f, -0.05f, 0.05f, 1.25f }, + { 0.830338272693f, 0.620393283803f, 0.583385370254f, -0.696883299726f, 0.432629991358f, 1.069355537167f } } }, { "DISPLAY - CIE-XYZ-D65_to_G2.2-REC.709", { 1.0e-6f, - { 0.5f, 0.4f, 0.3f }, { 0.931739212204f, 0.559058879141f, 0.545230761999f } } }, + { 0.5f, 0.4f, 0.3f, -0.05f, 0.05f, 1.25f }, + { 0.931739212204f, 0.559058879141f, 0.545230761999f, 0.f, 0.474767926071f, 1.129896956592f } } }, + { "DISPLAY - CIE-XYZ-D65_to_G2.2-REC.709 - MIRROR NEGS", + { 1.0e-6f, + { 0.5f, 0.4f, 0.3f, -0.05f, 0.05f, 1.25f }, + { 0.931739212204f, 0.559058879141f, 0.545230761999f, -0.934816978533f, 0.474767926071f, 1.129896956592f } } }, { "DISPLAY - CIE-XYZ-D65_to_sRGB", { 1.0e-6f, - { 0.5f, 0.4f, 0.3f }, { 0.933793573229f, 0.564092030327f, 0.550040502218f } } }, + { 0.5f, 0.4f, 0.3f, -0.05f, 0.05f, 1.25f }, + { 0.933793573229f, 0.564092030327f, 0.550040502218f, -11.142147651136028f, 0.477958897494f, 1.124971166876f } } }, + { "DISPLAY - CIE-XYZ-D65_to_sRGB - MIRROR NEGS", + { 1.0e-6f, + { 0.5f, 0.4f, 0.3f, -0.05f, 0.05f, 1.25f }, + { 0.933793573229f, 0.564092030327f, 0.550040502218f, -0.936787206783f, 0.477958897494f, 1.124971166876f } } }, { "DISPLAY - CIE-XYZ-D65_to_G2.6-P3-DCI-BFD", { 1.0e-6f, { 0.5f, 0.4f, 0.3f }, { 0.908856342287f, 0.627840575107f, 0.608053675805f } } }, { "DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D65", { 1.0e-6f, - { 0.5f, 0.4f, 0.3f }, { 0.896805202281f, 0.627254277624f, 0.608228132100f } } }, + { 0.5f, 0.4f, 0.3f, -0.05f, 0.05f, 1.25f }, + { 0.896805202281f, 0.627254277624f, 0.608228132100f, 0.f, 0.493163009212f, 1.069368427937f } } }, + { "DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D65 - MIRROR NEGS", + { 1.0e-6f, + { 0.5f, 0.4f, 0.3f, -0.05f, 0.05f, 1.25f }, + { 0.896805202281f, 0.627254277624f, 0.608228132100f, -0.859521292874f, 0.493163009212f, 1.069368427937f } } }, { "DISPLAY - CIE-XYZ-D65_to_G2.6-P3-D60-BFD", { 1.0e-6f, { 0.5f, 0.4f, 0.3f }, { 0.892433142142f, 0.627011653770f, 0.608093643982f } } },