diff --git a/docs/api/apphelpers.rst b/docs/api/apphelpers.rst index a31935708d..9d8deca153 100644 --- a/docs/api/apphelpers.rst +++ b/docs/api/apphelpers.rst @@ -46,6 +46,65 @@ ColorSpaceMenuHelpers .. doxygentypedef:: ${OCIO_NAMESPACE}::ConstColorSpaceMenuHelperRcPtr .. doxygentypedef:: ${OCIO_NAMESPACE}::ColorSpaceMenuHelperRcPtr +ConfigCompatibilityHelpers +************************** + +.. tabs:: + + .. group-tab:: Python + + .. autofunction:: PyOpenColorIO.ConfigCompatibilityHelpers.CheckCompatibility + + .. group-tab:: C++ + + .. doxygennamespace:: ${OCIO_NAMESPACE}::ConfigCompatibilityHelpers + :members: + :undoc-members: + +ConfigMergingHelpers +******************** + +.. tabs:: + + .. group-tab:: Python + + .. autofunction:: PyOpenColorIO.ConfigMergingHelpers.MergeConfigs + .. autofunction:: PyOpenColorIO.ConfigMergingHelpers.MergeColorSpace + + .. autoclass:: PyOpenColorIO.ConfigMergingParameters + :members: + :undoc-members: + :special-members: __init__, __str__ + :exclude-members: MergeStrategies + + .. autoclass:: PyOpenColorIO.ConfigMergingParameters.MergeStrategies + :members: + :undoc-members: + :exclude-members: name + + .. autoclass:: PyOpenColorIO.ConfigMerger + :members: + :undoc-members: + :special-members: __init__, __str__ + + .. group-tab:: C++ + + .. doxygenclass:: ${OCIO_NAMESPACE}::ConfigMergingParameters + :members: + :undoc-members: + + .. doxygenfunction:: ${OCIO_NAMESPACE}::operator<<(std::ostream&, const ConfigMergingParameters&) + + .. doxygenclass:: ${OCIO_NAMESPACE}::ConfigMerger + :members: + :undoc-members: + + .. doxygenfunction:: ${OCIO_NAMESPACE}::operator<<(std::ostream&, const ConfigMerger&) + + .. doxygennamespace:: ${OCIO_NAMESPACE}::ConfigMergingHelpers + :members: + :undoc-members: + DisplayViewHelpers ****************** diff --git a/docs/api/enums.rst b/docs/api/enums.rst index 11646686c2..5c7e951bf2 100644 --- a/docs/api/enums.rst +++ b/docs/api/enums.rst @@ -482,3 +482,19 @@ ProcessorCacheFlags .. group-tab:: C++ .. doxygenenum:: ${OCIO_NAMESPACE}::ProcessorCacheFlags + +ConfigCompatibility +******************** + +.. tabs:: + + .. group-tab:: Python + + .. autoclass:: PyOpenColorIO.ConfigCompatibility + :members: + :undoc-members: + :exclude-members: name + + .. group-tab:: C++ + + .. doxygenenum:: ${OCIO_NAMESPACE}::ConfigCompatibility diff --git a/include/OpenColorIO/OpenColorAppHelpers.h b/include/OpenColorIO/OpenColorAppHelpers.h index adb7fe5bb8..b7f7f9efe9 100644 --- a/include/OpenColorIO/OpenColorAppHelpers.h +++ b/include/OpenColorIO/OpenColorAppHelpers.h @@ -275,6 +275,19 @@ class OCIOEXPORT ColorSpaceMenuHelper extern OCIOEXPORT std::ostream & operator<<(std::ostream &, const ColorSpaceMenuHelper &); +namespace ConfigCompatibilityHelpers +{ +/** + * \brief Check the config for compatibility with a specific requirement. + * + * Returns false if the config is not compatible with the requirement identified by the given + * ConfigCompatibility value. + */ +extern OCIOEXPORT bool CheckCompatibility(const ConstConfigRcPtr & config, + ConfigCompatibility compatibility); + +} // namespace ConfigCompatibilityHelpers + namespace ColorSpaceHelpers { /** diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 39a181c467..0341744204 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -742,6 +742,13 @@ enum ProcessorCacheFlags : unsigned int PROCESSOR_CACHE_DEFAULT = (PROCESSOR_CACHE_ENABLED | PROCESSOR_CACHE_SHARE_DYN_PROPERTIES) }; +//!cpp:type:: Enum identifying a specific compatibility requirement that a Config may be checked +// against via :cpp:func:`ConfigCompatibilityHelpers::CheckCompatibility`. +enum ConfigCompatibility +{ + CONFIG_HDR_DISPLAY_SUPPORT_26 = 0 // Config meets HDR display compatibility reqmts as of OCIO 2.6. +}; + // Conversion extern OCIOEXPORT const char * BoolToString(bool val); diff --git a/src/OpenColorIO/CMakeLists.txt b/src/OpenColorIO/CMakeLists.txt index ff20c1a6c7..646423e97e 100755 --- a/src/OpenColorIO/CMakeLists.txt +++ b/src/OpenColorIO/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCES apphelpers/CategoryHelpers.cpp apphelpers/ColorSpaceHelpers.cpp + apphelpers/ConfigCompatibility.cpp apphelpers/DisplayViewHelpers.cpp apphelpers/LegacyViewingPipeline.cpp apphelpers/mergeconfigs/MergeConfigsHelpers.cpp diff --git a/src/OpenColorIO/apphelpers/ConfigCompatibility.cpp b/src/OpenColorIO/apphelpers/ConfigCompatibility.cpp new file mode 100644 index 0000000000..c3876456c3 --- /dev/null +++ b/src/OpenColorIO/apphelpers/ConfigCompatibility.cpp @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#include + +#include + +#include "ConfigCompatibility.h" +#include "Logging.h" + + +namespace OCIO_NAMESPACE +{ + +namespace ConfigCompatibilityHelpers +{ + +bool ActiveDisplayColorSpacesHaveAttributes(const ConstConfigRcPtr & config) +{ + const int numCS = config->getNumColorSpaces(SEARCH_REFERENCE_SPACE_DISPLAY, COLORSPACE_ACTIVE); + if (numCS == 0) + { + LogDebug("HDR Display Support (2.6): No active display-referred color spaces found."); + return false; + } + + bool allHaveInteropID = true; + + for (int i = 0; i < numCS; ++i) + { + const char * csName = config->getColorSpaceNameByIndex(SEARCH_REFERENCE_SPACE_DISPLAY, + COLORSPACE_ACTIVE, i); + ConstColorSpaceRcPtr cs = config->getColorSpace(csName); + if (!cs || cs->getReferenceSpaceType() != REFERENCE_SPACE_DISPLAY || + !cs->getInteropID() || !*cs->getInteropID()) + { + LogDebug(std::string("HDR Display Support (2.6): Active display color space '") + csName + + "' has no interop ID."); + allHaveInteropID = false; + } + + if (!cs || !cs->getEncoding() || !*cs->getEncoding()) + { + LogDebug(std::string("HDR Display Support (2.6): Active display color space '") + csName + + "' has no encoding."); + allHaveInteropID = false; + } + } + + return allHaveInteropID; +} + +bool ActiveDisplaysHaveColorSpace(const ConstConfigRcPtr & config) +{ + // Only check active displays. + const int numDisplays = config->getNumDisplays(); + if (numDisplays == 0) + { + LogDebug("HDR Display Support (2.6): No active displays."); + return false; + } + + bool allHaveColorSpace = true; + + for (int d = 0; d < numDisplays; ++d) + { + const char * display = config->getDisplay(d); + ConstColorSpaceRcPtr cs = config->getColorSpace(display); + if (!cs) + { + LogDebug(std::string("HDR Display Support (2.6): Display '") + display + + "' has no matching color space."); + allHaveColorSpace = false; + } + } + + return allHaveColorSpace; +} + +bool ActiveViewsHaveViewTransform(const ConstConfigRcPtr & config) +{ + // Only check active displays. + const int numDisplays = config->getNumDisplays(); + + int numViewsTotal = 0; + bool allHaveViewTransform = true; + bool hasViewTransform = false; + + for (int d = 0; d < numDisplays; ++d) + { + const char * display = config->getDisplay(d); + // Only check active views. + const int numViews = config->getNumViews(display); + for (int v = 0; v < numViews; ++v) + { + ++numViewsTotal; + + const char * view = config->getView(display, v); + const char * transformName = config->getDisplayViewTransformName(display, view); + if (transformName && *transformName) + { + hasViewTransform = true; + continue; + } + + const char * csName = config->getDisplayViewColorSpaceName(display, view); + ConstColorSpaceRcPtr cs = csName && *csName ? config->getColorSpace(csName) : + ConstColorSpaceRcPtr(); + if (cs && cs->isData()) + { + continue; + } + + LogDebug(std::string("HDR Display Support (2.6): Active (display, view) pair ('") + display + + "', '" + view + "') has no view transform and is not a data color space."); + allHaveViewTransform = false; + } + } + + if (numViewsTotal == 0) + { + LogDebug("HDR Display Support (2.6): No active (display, view) pairs."); + return false; + } + + if (!hasViewTransform) + { + LogDebug("HDR Display Support (2.6): No active (display, view) pair references a view " + "transform."); + } + + return allHaveViewTransform && hasViewTransform; +} + +bool CheckHDRDisplaySupport26(const ConstConfigRcPtr & config) +{ + bool compatible = true; + + if (!config->hasRole(ROLE_INTERCHANGE_DISPLAY)) + { + LogDebug("HDR Display Support (2.6): Missing the 'cie_xyz_d65_interchange' role."); + compatible = false; + } + + if (!ActiveDisplayColorSpacesHaveAttributes(config)) + { + compatible = false; + } + + if (!ActiveDisplaysHaveColorSpace(config)) + { + compatible = false; + } + + if (!ActiveViewsHaveViewTransform(config)) + { + compatible = false; + } + + return compatible; +} + +bool CheckCompatibility(const ConstConfigRcPtr & config, ConfigCompatibility compatibility) +{ + switch (compatibility) + { + case CONFIG_HDR_DISPLAY_SUPPORT_26: + return CheckHDRDisplaySupport26(config); + } + + return false; +} + +} // namespace ConfigCompatibilityHelpers + +} // namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/apphelpers/ConfigCompatibility.h b/src/OpenColorIO/apphelpers/ConfigCompatibility.h new file mode 100644 index 0000000000..1a9012e5f8 --- /dev/null +++ b/src/OpenColorIO/apphelpers/ConfigCompatibility.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#ifndef INCLUDED_OCIO_CONFIG_COMPATIBILITY_H +#define INCLUDED_OCIO_CONFIG_COMPATIBILITY_H + +#include + + +namespace OCIO_NAMESPACE +{ + +namespace ConfigCompatibilityHelpers +{ + +// Returns true if the config has at least one active display color space and all of them have +// an interop ID and an encoding set. +bool ActiveDisplayColorSpacesHaveAttributes(const ConstConfigRcPtr & config); + +// Returns true if the config has at least one active display and each active display has a +// color space matching its name. +bool ActiveDisplaysHaveColorSpace(const ConstConfigRcPtr & config); + +// Returns true if the config has at least one active (display, view) pair using a view transform, +// and all views reference a non-empty view transform, except that a view with no view transform is +// allowed if the color space it refers to has isData set to true. +bool ActiveViewsHaveViewTransform(const ConstConfigRcPtr & config); + +bool CheckHDRDisplaySupport26(const ConstConfigRcPtr & config); + +} // namespace ConfigCompatibilityHelpers + +} // namespace OCIO_NAMESPACE + +#endif // INCLUDED_OCIO_CONFIG_COMPATIBILITY_H diff --git a/src/apps/ociocheck/main.cpp b/src/apps/ociocheck/main.cpp index 5a0c24524a..56520fec97 100644 --- a/src/apps/ociocheck/main.cpp +++ b/src/apps/ociocheck/main.cpp @@ -25,7 +25,7 @@ const char * DESC_STRING = "\n\n" "All display/view pairs, color spaces, and named transforms are checked,\n" "regardless of whether they are active or inactive.\n\n" "Ociocheck can also be used to clean up formatting on an existing profile\n" -"that has been manually edited, using the '-o' option.\n"; +"that has been manually edited, using the '--oconfig' option.\n"; // Returns true if the interopID is valid. @@ -107,6 +107,7 @@ bool isValidInteropID(const std::string& id) int main(int argc, const char **argv) { bool help = false; + bool verbose = false; int errorcount = 0; int warningcount = 0; std::string inputconfig; @@ -118,6 +119,8 @@ int main(int argc, const char **argv) "--help", &help, "Print help message", "--iconfig %s", &inputconfig, "Input .ocio configuration file (default: $OCIO)", "--oconfig %s", &outputconfig, "Output .ocio file", + "-i %s", &inputconfig, "Same as --iconfig", + "-v", &verbose, "Print detailed reasons for failed compatibility checks", NULL); if (ap.parse(argc, argv) < 0) @@ -657,6 +660,18 @@ int main(int argc, const char **argv) std::cout << "Validation: failed" << std::endl; } + std::cout << std::endl; + std::cout << "** Compatibility **" << std::endl; + + LogGuard compatLogGuard; + const bool hdrDisplaySupport26 = OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26); + std::cout << "HDR Display Support (2.6): " << (hdrDisplaySupport26 ? "yes" : "no") << std::endl; + if (verbose && !compatLogGuard.empty()) + { + std::cout << compatLogGuard.output(); + } + std::cout << std::endl; std::cout << "** Miscellaneous **" << std::endl; std::cout << "CacheID: " << cacheID << std::endl; diff --git a/src/bindings/python/CMakeLists.txt b/src/bindings/python/CMakeLists.txt index b3a343635f..ad04e46f8a 100644 --- a/src/bindings/python/CMakeLists.txt +++ b/src/bindings/python/CMakeLists.txt @@ -53,6 +53,7 @@ endif() set(SOURCES apphelpers/PyColorSpaceHelpers.cpp + apphelpers/PyConfigCompatibilityHelpers.cpp apphelpers/PyDisplayViewHelpers.cpp apphelpers/PyLegacyViewingPipeline.cpp apphelpers/PyMergeConfigs.cpp diff --git a/src/bindings/python/PyOpenColorIO.cpp b/src/bindings/python/PyOpenColorIO.cpp index f66e6d540a..7defd1b4fd 100644 --- a/src/bindings/python/PyOpenColorIO.cpp +++ b/src/bindings/python/PyOpenColorIO.cpp @@ -97,6 +97,7 @@ PYBIND11_MODULE(PyOpenColorIO, m) // OpenColorIOAppHelpers bindPyColorSpaceMenuHelpers(m); + bindPyConfigCompatibilityHelpers(m); bindPyConfigMergingHelpers(m); bindPyDisplayViewHelpers(m); bindPyLegacyViewingPipeline(m); diff --git a/src/bindings/python/PyOpenColorIO.h b/src/bindings/python/PyOpenColorIO.h index 54848057a3..4244f0d8e3 100644 --- a/src/bindings/python/PyOpenColorIO.h +++ b/src/bindings/python/PyOpenColorIO.h @@ -56,6 +56,7 @@ void bindPyTransform(py::module & m); // OpenColorIOAppHelpers void bindPyColorSpaceMenuHelpers(py::module & m); +void bindPyConfigCompatibilityHelpers(py::module & m); void bindPyConfigMergingHelpers(py::module & m); void bindPyDisplayViewHelpers(py::module & m); void bindPyLegacyViewingPipeline(py::module & m); diff --git a/src/bindings/python/PyTypes.cpp b/src/bindings/python/PyTypes.cpp index e109708c35..a1c26a1302 100644 --- a/src/bindings/python/PyTypes.cpp +++ b/src/bindings/python/PyTypes.cpp @@ -859,6 +859,14 @@ void bindPyTypes(py::module & m) DOC(PyOpenColorIO, ProcessorCacheFlags, PROCESSOR_CACHE_DEFAULT)) .export_values(); + py::enum_( + m, "ConfigCompatibility", + DOC(PyOpenColorIO, ConfigCompatibility)) + + .value("CONFIG_HDR_DISPLAY_SUPPORT_26", CONFIG_HDR_DISPLAY_SUPPORT_26, + DOC(PyOpenColorIO, ConfigCompatibility, CONFIG_HDR_DISPLAY_SUPPORT_26)) + .export_values(); + // Conversion m.def("BoolToString", &BoolToString, "value"_a, DOC(PyOpenColorIO, BoolToString)); diff --git a/src/bindings/python/apphelpers/PyConfigCompatibilityHelpers.cpp b/src/bindings/python/apphelpers/PyConfigCompatibilityHelpers.cpp new file mode 100644 index 0000000000..f0753957c0 --- /dev/null +++ b/src/bindings/python/apphelpers/PyConfigCompatibilityHelpers.cpp @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#include "PyOpenColorIO.h" + +namespace OCIO_NAMESPACE +{ + +void bindPyConfigCompatibilityHelpers(py::module & m) +{ + auto mConfigCompatibilityHelpers = m.def_submodule("ConfigCompatibilityHelpers") + .def("CheckCompatibility", &ConfigCompatibilityHelpers::CheckCompatibility, + "config"_a.none(false), + "compatibility"_a.none(false), + DOC(ConfigCompatibilityHelpers, CheckCompatibility)); +} + +} // namespace OCIO_NAMESPACE diff --git a/tests/cpu/CMakeLists.txt b/tests/cpu/CMakeLists.txt index f7e7468ab4..55a25588b0 100755 --- a/tests/cpu/CMakeLists.txt +++ b/tests/cpu/CMakeLists.txt @@ -211,6 +211,7 @@ set(SOURCES set(TESTS apphelpers/CategoryHelpers_tests.cpp apphelpers/ColorSpaceHelpers_tests.cpp + apphelpers/ConfigCompatibility_tests.cpp apphelpers/DisplayViewHelpers_tests.cpp apphelpers/LegacyViewingPipeline_tests.cpp apphelpers/MergeConfigsHelpers_tests.cpp diff --git a/tests/cpu/apphelpers/ConfigCompatibility_tests.cpp b/tests/cpu/apphelpers/ConfigCompatibility_tests.cpp new file mode 100644 index 0000000000..8bfc1feb9e --- /dev/null +++ b/tests/cpu/apphelpers/ConfigCompatibility_tests.cpp @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + + +#include "apphelpers/ConfigCompatibility.cpp" +#include "testutils/UnitTest.h" + +namespace OCIO = OCIO_NAMESPACE; + + +namespace +{ + +OCIO::ColorSpaceRcPtr CreateDisplayColorSpace(const char * name) +{ + auto cs = OCIO::ColorSpace::Create(OCIO::REFERENCE_SPACE_DISPLAY); + cs->setName(name); + return cs; +} + +} // namespace + +OCIO_ADD_TEST(ConfigCompatibility, hdr_display_support_26) +{ + OCIO::ConfigRcPtr config = OCIO::Config::Create(); + + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Add the role, but the referenced display color space has no interop ID or encoding. + + auto interchange = CreateDisplayColorSpace("CIE XYZ-D65"); + OCIO_CHECK_NO_THROW(config->addColorSpace(interchange)); + OCIO_CHECK_NO_THROW(config->setRole(OCIO::ROLE_INTERCHANGE_DISPLAY, + "CIE XYZ-D65")); + + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::ActiveDisplayColorSpacesHaveAttributes(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Give the interchange color space an interop ID, but it still has no encoding. + + interchange->setInteropID("ocio:lin_ciexyzd65_display"); + OCIO_CHECK_NO_THROW(config->addColorSpace(interchange)); + + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::ActiveDisplayColorSpacesHaveAttributes(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Give the interchange color space an encoding as well. It is now the only display color + // space in the config and it has both an interop ID and an encoding. + + interchange->setEncoding("display-linear"); + OCIO_CHECK_NO_THROW(config->addColorSpace(interchange)); + + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::ActiveDisplayColorSpacesHaveAttributes(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Add another display color space without an interop ID or encoding. Since not all + // display color spaces satisfy the requirement anymore, the check fails again. + + auto dcs = CreateDisplayColorSpace("display_cs"); + OCIO_CHECK_NO_THROW(config->addColorSpace(dcs)); + + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::ActiveDisplayColorSpacesHaveAttributes(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // It does not have the interop ID and encoding, but making it inactive allows it to pass. + + OCIO_CHECK_NO_THROW(config->setInactiveColorSpaces("display_cs")); + + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::ActiveDisplayColorSpacesHaveAttributes(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Add a view that does not use a view transform and whose color space is not a data + // color space. A view transform is required unless the color space is a data color space, + // so the check fails. + + auto scs = OCIO::ColorSpace::Create(); + scs->setName("scene_cs"); + OCIO_CHECK_NO_THROW(config->addColorSpace(scs)); + + OCIO_CHECK_NO_THROW(config->addDisplayView("display", "view_no_vt", "scene_cs", "")); + + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::ActiveViewsHaveViewTransform(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Mark scene_cs as a data color space. The view with no view transform is now exempt from + // the requirement, but the check still fails since no view uses a view transform at all. + + scs->setIsData(true); + OCIO_CHECK_NO_THROW(config->addColorSpace(scs)); + + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::ActiveViewsHaveViewTransform(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Add another view that does use a view transform. All views now satisfy the view + // transform requirement, but the config is not yet compatible since the active display + // "display" has no color space matching its name. + + auto vt = OCIO::ViewTransform::Create(OCIO::REFERENCE_SPACE_SCENE); + vt->setName("view_transform"); + auto cdl = OCIO::CDLTransform::Create(); + cdl->setSat(1.2); + vt->setTransform(cdl, OCIO::VIEWTRANSFORM_DIR_FROM_REFERENCE); + OCIO_CHECK_NO_THROW(config->addViewTransform(vt)); + + OCIO_CHECK_NO_THROW(config->addDisplayView("display", "view_with_vt", "view_transform", + "display_cs", "", "", "")); + + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::ActiveViewsHaveViewTransform(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::ActiveDisplaysHaveColorSpace(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Add a color space matching the display's name (with an interop ID and encoding, since + // it is active and so must also satisfy ActiveDisplayColorSpacesHaveAttributes). The config + // is now compatible. + + auto displayCS = CreateDisplayColorSpace("display"); + displayCS->setInteropID("foo:pq_rec6000_display"); + displayCS->setEncoding("hdr-video"); + OCIO_CHECK_NO_THROW(config->addColorSpace(displayCS)); + + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::ActiveDisplayColorSpacesHaveAttributes(config)); + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::ActiveDisplaysHaveColorSpace(config)); + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Add a second display that has no color space matching its name. Since it is active, + // the check fails. Deactivating it via setActiveDisplays makes the config compatible + // again, showing that an inactive display does not need to pass the check. + + OCIO_CHECK_NO_THROW(config->addDisplayView("display2", "view", "view_transform", + "display_cs", "", "", "")); + + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::ActiveDisplaysHaveColorSpace(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + OCIO_CHECK_NO_THROW(config->setActiveDisplays("display")); + + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::ActiveDisplaysHaveColorSpace(config)); + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Add a view that uses a named transform as its view_transform, the config is still compatible. + + auto nt = OCIO::NamedTransform::Create(); + nt->setTransform(OCIO::MatrixTransform::Create(), OCIO::TRANSFORM_DIR_FORWARD); + nt->setName("namedtransform"); + OCIO_CHECK_NO_THROW(config->addDisplayView("display2", "nt view", "namedtransform", + "display_cs", "", "", "")); + + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::ActiveViewsHaveViewTransform(config)); + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + // Add a view that does not qualify (no view transform, non-data color space) to the + // active display. Since it is active, the check fails. Deactivating it via setActiveViews + // makes the config compatible again, showing that an inactive view does not need to pass + // the check. + + OCIO_CHECK_NO_THROW(config->addDisplayView("display", "bad_view", "display_cs", "")); + + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::ActiveViewsHaveViewTransform(config)); + OCIO_CHECK_ASSERT(!OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); + + OCIO_CHECK_NO_THROW(config->setActiveViews("view_with_vt")); + + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::ActiveViewsHaveViewTransform(config)); + OCIO_CHECK_ASSERT(OCIO::ConfigCompatibilityHelpers::CheckCompatibility( + config, OCIO::CONFIG_HDR_DISPLAY_SUPPORT_26)); +} diff --git a/tests/python/ConfigCompatibilityHelpersTest.py b/tests/python/ConfigCompatibilityHelpersTest.py new file mode 100644 index 0000000000..654364659f --- /dev/null +++ b/tests/python/ConfigCompatibilityHelpersTest.py @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright Contributors to the OpenColorIO Project. + +import unittest + +import PyOpenColorIO as OCIO + + +class ConfigCompatibilityHelpersTest(unittest.TestCase): + def test_check_compatibility(self): + """ + Test the ConfigCompatibilityHelpers.CheckCompatibility() function. + """ + cfg = OCIO.Config() + + # An empty config does not meet the HDR display support requirements. + self.assertFalse( + OCIO.ConfigCompatibilityHelpers.CheckCompatibility( + cfg, OCIO.CONFIG_HDR_DISPLAY_SUPPORT_26 + ) + ) + + # Built-in configs version 4.0 and higher meet the requirements. + cfg = OCIO.Config.CreateFromBuiltinConfig("ocio://default") + + self.assertTrue( + OCIO.ConfigCompatibilityHelpers.CheckCompatibility( + cfg, OCIO.CONFIG_HDR_DISPLAY_SUPPORT_26 + ) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/python/OpenColorIOTestSuite.py b/tests/python/OpenColorIOTestSuite.py index 3a0530bc55..ad42d0dc64 100755 --- a/tests/python/OpenColorIOTestSuite.py +++ b/tests/python/OpenColorIOTestSuite.py @@ -57,6 +57,7 @@ import ColorSpaceHelpersTest import ColorSpaceTest import ColorSpaceTransformTest +import ConfigCompatibilityHelpersTest import ConfigTest import ConstantsTest import ContextTest @@ -108,6 +109,7 @@ def get_test_modules(): ("ColorSpaceHelpersTest", ColorSpaceHelpersTest), ("ColorSpaceTest", ColorSpaceTest), ("ColorSpaceTransformTest", ColorSpaceTransformTest), + ("ConfigCompatibilityHelpersTest", ConfigCompatibilityHelpersTest), ("ConfigTest", ConfigTest), ("ConstantsTest", ConstantsTest), ("ContextTest", ContextTest),