From ae6539e53796b03cbfc3271f62cf47d2457c5843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Renaud-Houde?= Date: Mon, 28 Aug 2023 21:06:51 -0400 Subject: [PATCH] Added GetProcessorFromConfigs variants for displays and views. (#1808) * Added GetProcessorFromConfigs variants for displays and views. Signed-off-by: Eric Renaud-Houde * Added display-view processor from two configs tests, moved direction argument last. Signed-off-by: Eric Renaud-Houde --------- Signed-off-by: Eric Renaud-Houde Co-authored-by: Michael Dolan Signed-off-by: Doug Walker --- include/OpenColorIO/OpenColorIO.h | 44 +++++++++ src/OpenColorIO/Config.cpp | 143 +++++++++++++++++++++++++++++- src/bindings/python/PyConfig.cpp | 73 +++++++++++++++ tests/cpu/Config_tests.cpp | 74 +++++++++++++++- 4 files changed, 330 insertions(+), 4 deletions(-) diff --git a/include/OpenColorIO/OpenColorIO.h b/include/OpenColorIO/OpenColorIO.h index 29f1243ac..2680407ae 100644 --- a/include/OpenColorIO/OpenColorIO.h +++ b/include/OpenColorIO/OpenColorIO.h @@ -1412,6 +1412,50 @@ class OCIOEXPORT Config const char * dstColorSpaceName, const char * dstInterchangeName); + /** + * \brief Get a processor to convert from a color space to a display and view in + * two separate configs. + */ + static ConstProcessorRcPtr GetProcessorFromConfigs(const ConstConfigRcPtr & srcConfig, + const char * srcColorSpaceName, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + TransformDirection direction); + + static ConstProcessorRcPtr GetProcessorFromConfigs(const ConstContextRcPtr & srcContext, + const ConstConfigRcPtr & srcConfig, + const char * srcColorSpaceName, + const ConstContextRcPtr & dstContext, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + TransformDirection direction); + + /** + * The srcInterchangeName and dstInterchangeName must refer to a pair of + * color spaces in the two configs that are the same. A role name may also be used. + */ + static ConstProcessorRcPtr GetProcessorFromConfigs(const ConstConfigRcPtr & srcConfig, + const char * srcColorSpaceName, + const char * srcInterchangeName, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + const char * dstInterchangeName, + TransformDirection direction); + + static ConstProcessorRcPtr GetProcessorFromConfigs(const ConstContextRcPtr & srcContext, + const ConstConfigRcPtr & srcConfig, + const char * srcColorSpaceName, + const char * srcInterchangeName, + const ConstContextRcPtr & dstContext, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + const char * dstInterchangeName, + TransformDirection direction); + /// Get the Processor Cache flags. ProcessorCacheFlags getProcessorCacheFlags() const noexcept; diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index b59e05c44..4003410c2 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -4572,7 +4572,7 @@ ConstProcessorRcPtr Config::GetProcessorFromConfigs(const ConstContextRcPtr & sr } auto p2 = dstConfig->getProcessor(dstContext, dstExCs, dstColorSpace); - if (!p1) + if (!p2) { throw Exception("Can't create the processor for the destination config " "and the destination color space."); @@ -4591,6 +4591,147 @@ ConstProcessorRcPtr Config::GetProcessorFromConfigs(const ConstContextRcPtr & sr return processor; } +ConstProcessorRcPtr Config::GetProcessorFromConfigs(const ConstConfigRcPtr & srcConfig, + const char * srcName, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + TransformDirection direction) +{ + return GetProcessorFromConfigs(srcConfig->getCurrentContext(), srcConfig, srcName, + dstConfig->getCurrentContext(), dstConfig, dstDisplay, dstView, direction); +} + +ConstProcessorRcPtr Config::GetProcessorFromConfigs(const ConstContextRcPtr & srcContext, + const ConstConfigRcPtr & srcConfig, + const char * srcName, + const ConstContextRcPtr & dstContext, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + TransformDirection direction) +{ + ConstColorSpaceRcPtr srcColorSpace = srcConfig->getColorSpace(srcName); + if (!srcColorSpace) + { + std::ostringstream os; + os << "Could not find source color space '" << srcName << "'."; + throw Exception(os.str().c_str()); + } + + const bool sceneReferred = (srcColorSpace->getReferenceSpaceType() == REFERENCE_SPACE_SCENE); + const char* exchangeRoleName = sceneReferred ? ROLE_INTERCHANGE_SCENE : ROLE_INTERCHANGE_DISPLAY; + const char* srcExName = LookupRole(srcConfig->getImpl()->m_roles, exchangeRoleName); + if (!srcExName || !*srcExName) + { + std::ostringstream os; + os << "The role '" << exchangeRoleName << "' is missing in the source config."; + throw Exception(os.str().c_str()); + } + ConstColorSpaceRcPtr srcExCs = srcConfig->getColorSpace(srcExName); + if (!srcExCs) + { + std::ostringstream os; + os << "The role '" << exchangeRoleName << "' refers to color space '" << srcExName; + os << "' that is missing in the source config."; + throw Exception(os.str().c_str()); + } + + const char* dstExName = LookupRole(dstConfig->getImpl()->m_roles, exchangeRoleName); + if (!dstExName || !*dstExName) + { + std::ostringstream os; + os << "The role '" << exchangeRoleName << "' is missing in the destination config."; + throw Exception(os.str().c_str()); + } + ConstColorSpaceRcPtr dstExCs = dstConfig->getColorSpace(dstExName); + if (!dstExCs) + { + std::ostringstream os; + os << "The role '" << exchangeRoleName << "' refers to color space '" << dstExName; + os << "' that is missing in the destination config."; + throw Exception(os.str().c_str()); + } + + return GetProcessorFromConfigs(srcContext, srcConfig, srcName, srcExName, + dstContext, dstConfig, dstDisplay, dstView, dstExName, direction); +} + +ConstProcessorRcPtr Config::GetProcessorFromConfigs(const ConstConfigRcPtr& srcConfig, + const char * srcName, + const char * srcInterchangeName, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + const char* dstInterchangeName, + TransformDirection direction) +{ + return GetProcessorFromConfigs(srcConfig->getCurrentContext(), srcConfig, srcName, srcInterchangeName, + dstConfig->getCurrentContext(), dstConfig, dstDisplay, dstView, dstInterchangeName, direction); +} + +ConstProcessorRcPtr Config::GetProcessorFromConfigs(const ConstContextRcPtr & srcContext, + const ConstConfigRcPtr & srcConfig, + const char * srcName, + const char * srcInterchangeName, + const ConstContextRcPtr & dstContext, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + const char * dstInterchangeName, + TransformDirection direction) +{ + ConstColorSpaceRcPtr srcColorSpace = srcConfig->getColorSpace(srcName); + if (!srcColorSpace) + { + std::ostringstream os; + os << "Could not find source color space '" << srcName << "'."; + throw Exception(os.str().c_str()); + } + + ConstColorSpaceRcPtr srcExCs = srcConfig->getColorSpace(srcInterchangeName); + if (!srcExCs) + { + std::ostringstream os; + os << "Could not find source interchange color space '" << srcInterchangeName << "'."; + throw Exception(os.str().c_str()); + } + + if (direction == TRANSFORM_DIR_INVERSE) + { + std::swap(srcColorSpace, srcExCs); + } + auto p1 = srcConfig->getProcessor(srcContext, srcColorSpace, srcExCs); + if (!p1) + { + throw Exception("Can't create the processor for the source config and " + "the source color space."); + } + + auto p2 = dstConfig->getProcessor(dstContext, dstInterchangeName, dstDisplay, dstView, direction); + if (!p2) + { + throw Exception("Can't create the processor for the destination config " + "and the destination color space."); + } + + ProcessorRcPtr processor = Processor::Create(); + processor->getImpl()->setProcessorCacheFlags(srcConfig->getImpl()->m_cacheFlags); + + // If the source color spaces is a data space, its corresponding processor + // will be empty, but need to make sure the entire result is also empty to + // better match the semantics of how data spaces are handled. + if (!srcColorSpace->isData()) + { + if (direction == TRANSFORM_DIR_INVERSE) + { + std::swap(p1, p2); + } + processor->getImpl()->concatenate(p1, p2); + } + return processor; +} + static ConstProcessorRcPtr GetProcessorToBuiltinCS(ConstConfigRcPtr srcConfig, const char * srcColorSpaceName, const char * builtinColorSpaceName, diff --git a/src/bindings/python/PyConfig.cpp b/src/bindings/python/PyConfig.cpp index 6708c81e3..675f7155c 100644 --- a/src/bindings/python/PyConfig.cpp +++ b/src/bindings/python/PyConfig.cpp @@ -811,6 +811,79 @@ void bindPyConfig(py::module & m) "srcContext"_a, "srcConfig"_a, "srcColorSpaceName"_a, "srcInterchangeName"_a, "dstContext"_a, "dstConfig"_a, "dstColorSpaceName"_a, "dstInterchangeName"_a, DOC(Config, GetProcessorFromConfigs, 4)) + .def_static("GetProcessorFromConfigs", [](const ConstConfigRcPtr & srcConfig, + const char * srcColorSpaceName, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + TransformDirection direction) + { + return Config::GetProcessorFromConfigs(srcConfig, srcColorSpaceName, + dstConfig, dstDisplay, dstView, direction); + }, + "srcConfig"_a, "srcColorSpaceName"_a, "dstConfig"_a, "dstDisplay"_a, "dstView"_a, "direction"_a, + DOC(Config, GetProcessorFromConfigs, 5)) + .def_static("GetProcessorFromConfigs", [](const ConstContextRcPtr & srcContext, + const ConstConfigRcPtr & srcConfig, + const char * srcColorSpaceName, + const ConstContextRcPtr & dstContext, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + TransformDirection direction) + { + return Config::GetProcessorFromConfigs(srcContext, srcConfig, srcColorSpaceName, + dstContext, dstConfig, dstDisplay, dstView, direction); + }, + "srcContext"_a, "srcConfig"_a, "srcColorSpaceName"_a, + "dstContext"_a, "dstConfig"_a, "dstView"_a, "dstDisplay"_a, "direction"_a, + DOC(Config, GetProcessorFromConfigs, 6)) + .def_static("GetProcessorFromConfigs", [](const ConstConfigRcPtr & srcConfig, + const char * srcColorSpaceName, + const char * srcInterchangeName, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + const char * dstInterchangeName, + TransformDirection direction) + { + return Config::GetProcessorFromConfigs(srcConfig, + srcColorSpaceName, + srcInterchangeName, + dstConfig, + dstDisplay, + dstView, + dstInterchangeName, + direction); + }, + "srcConfig"_a, "srcColorSpaceName"_a, "srcInterchangeName"_a, + "dstConfig"_a, "dstDisplay"_a, "dstView"_a, "dstInterchangeName"_a, "direction"_a, + DOC(Config, GetProcessorFromConfigs, 7)) + .def_static("GetProcessorFromConfigs", [](const ConstContextRcPtr & srcContext, + const ConstConfigRcPtr & srcConfig, + const char * srcColorSpaceName, + const char * srcInterchangeName, + const ConstContextRcPtr & dstContext, + const ConstConfigRcPtr & dstConfig, + const char * dstDisplay, + const char * dstView, + const char * dstInterchangeName, + TransformDirection direction) + { + return Config::GetProcessorFromConfigs(srcContext, + srcConfig, + srcColorSpaceName, + srcInterchangeName, + dstContext, + dstConfig, + dstDisplay, + dstView, + dstInterchangeName, + direction); + }, + "srcContext"_a, "srcConfig"_a, "srcColorSpaceName"_a, "srcInterchangeName"_a, + "dstContext"_a, "dstConfig"_a, "dstDisplay"_a, "dstView"_a, "dstInterchangeName"_a, "direction"_a, + DOC(Config, GetProcessorFromConfigs, 8)) .def("setProcessorCacheFlags", &Config::setProcessorCacheFlags, "flags"_a, DOC(Config, setProcessorCacheFlags)) .def("clearProcessorCache", &Config::clearProcessorCache, diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index 56f812ae9..c46e443d8 100644 --- a/tests/cpu/Config_tests.cpp +++ b/tests/cpu/Config_tests.cpp @@ -5913,6 +5913,11 @@ ocio_profile_version: 2 aces_interchange: aces1 cie_xyz_d65_interchange: display1 +displays: + displayname: + - ! {name: view1, colorspace: displaytest1} + - ! {name: view2, view_transform: vt1, display_colorspace: display2} + view_transforms: - ! name: vt1 @@ -5928,6 +5933,11 @@ ocio_profile_version: 2 allocation: uniform to_scene_reference: ! {offset: [0.01, 0.02, 0.03, 0]} + - ! + name: displaytest1 + allocation: uniform + to_scene_reference: ! {base: 2} + - ! name: aces1 allocation: uniform @@ -6020,7 +6030,6 @@ ocio_profile_version: 2 OCIO_CHECK_NO_THROW(p = OCIO::Config::GetProcessorFromConfigs( config1, "test1", "aces1", config2, "test2", "aces2")); OCIO_REQUIRE_ASSERT(p); - OCIO_REQUIRE_ASSERT(p); group = p->createGroupTransform(); OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 4); @@ -6028,7 +6037,6 @@ ocio_profile_version: 2 OCIO_CHECK_NO_THROW(p = OCIO::Config::GetProcessorFromConfigs( config1, "test1", OCIO::ROLE_INTERCHANGE_SCENE, config2, "test2", "aces2")); OCIO_REQUIRE_ASSERT(p); - OCIO_REQUIRE_ASSERT(p); group = p->createGroupTransform(); OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 4); @@ -6036,7 +6044,6 @@ ocio_profile_version: 2 OCIO_CHECK_NO_THROW(p = OCIO::Config::GetProcessorFromConfigs( config1, "test1", OCIO::ROLE_INTERCHANGE_SCENE, config2, "test_role", "aces2")); OCIO_REQUIRE_ASSERT(p); - OCIO_REQUIRE_ASSERT(p); group = p->createGroupTransform(); OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 4); @@ -6091,6 +6098,67 @@ ocio_profile_version: 2 "There is no view transform between the main scene-referred space " "and the display-referred space"); + + // Using the display-view getters + OCIO_CHECK_NO_THROW(p = OCIO::Config::GetProcessorFromConfigs( + config2, "test2", "aces2", config1, "displayname", "view1", "aces1", OCIO::TRANSFORM_DIR_FORWARD)); + OCIO_REQUIRE_ASSERT(p); + group = p->createGroupTransform(); + OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 4); + t0 = group->getTransform(0); + m0 = OCIO_DYNAMIC_POINTER_CAST(t0); + OCIO_CHECK_ASSERT(m0); + t1 = group->getTransform(1); + r1 = OCIO_DYNAMIC_POINTER_CAST(t1); + OCIO_CHECK_ASSERT(r1); + t2 = group->getTransform(2); + e2 = OCIO_DYNAMIC_POINTER_CAST(t2); + OCIO_CHECK_ASSERT(e2); + t3 = group->getTransform(3); + l3 = OCIO_DYNAMIC_POINTER_CAST(t3); + OCIO_CHECK_ASSERT(l3); + + // Inverse direction reverses the entire chain + OCIO_CHECK_NO_THROW(p = OCIO::Config::GetProcessorFromConfigs( + config2, "test2", "aces2", config1, "displayname", "view1", "aces1", OCIO::TRANSFORM_DIR_INVERSE)); + OCIO_REQUIRE_ASSERT(p); + group = p->createGroupTransform(); + OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 4); + t0 = group->getTransform(0); + auto l0 = OCIO_DYNAMIC_POINTER_CAST(t0); + OCIO_CHECK_ASSERT(l0); + t1 = group->getTransform(1); + e1 = OCIO_DYNAMIC_POINTER_CAST(t1); + OCIO_CHECK_ASSERT(e1); + t2 = group->getTransform(2); + r2 = OCIO_DYNAMIC_POINTER_CAST(t2); + OCIO_CHECK_ASSERT(r2); + t3 = group->getTransform(3); + m3 = OCIO_DYNAMIC_POINTER_CAST(t3); + OCIO_CHECK_ASSERT(m3); + + // Implicit interchange spaces, using the view transform + OCIO_CHECK_NO_THROW(p = OCIO::Config::GetProcessorFromConfigs( + config2, "test2", config1, "displayname", "view2", OCIO::TRANSFORM_DIR_FORWARD)); + OCIO_REQUIRE_ASSERT(p); + group = p->createGroupTransform(); + OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 5); + t0 = group->getTransform(0); + m0 = OCIO_DYNAMIC_POINTER_CAST(t0); + OCIO_CHECK_ASSERT(m0); + t1 = group->getTransform(1); + r1 = OCIO_DYNAMIC_POINTER_CAST(t1); + OCIO_CHECK_ASSERT(r1); + t2 = group->getTransform(2); + e2 = OCIO_DYNAMIC_POINTER_CAST(t2); + OCIO_CHECK_ASSERT(e2); + t3 = group->getTransform(3); + r3 = OCIO_DYNAMIC_POINTER_CAST(t3); + OCIO_CHECK_ASSERT(r3); + t4 = group->getTransform(4); + auto ff4 = OCIO_DYNAMIC_POINTER_CAST(t4); + OCIO_CHECK_ASSERT(ff4); + constexpr const char * SIMPLE_CONFIG3{ R"( ocio_profile_version: 2