diff --git a/include/OpenColorIO/OpenColorIO.h b/include/OpenColorIO/OpenColorIO.h index d97a376365..138f210c1e 100644 --- a/include/OpenColorIO/OpenColorIO.h +++ b/include/OpenColorIO/OpenColorIO.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "OpenColorABI.h" #include "OpenColorTypes.h" @@ -1979,6 +1980,42 @@ class OCIOEXPORT ColorSpace const char * getDescription() const noexcept; void setDescription(const char * description); + /** + * Get/Set the interop ID for the color space. The interop ID is a + * structured string defined by the Color Interop Forum. It is intended to + * identify color spaces in a way that is portable across different configs, + * making it suitable for use in various file formats. The Color Interop + * Forum publishes ID strings for common color spaces. If you create your + * own IDs, they must be preceded by a namespace string. The setter will + * throw if the string does not follow certain rules (run ociocheck for a + * more complete validation). For more details, please review the interop ID + * white paper available from: + * https://github.com/AcademySoftwareFoundation/ColorInterop. + */ + const char * getInteropID() const noexcept; + void setInteropID(const char * interopID); + + /** + * Get/Set the interchange attributes. + * + * Currently supported attribute names are "amf_transform_ids" and + * "icc_profile_name". Using any other name will throw. If the attribute is + * not defined, it'll return an empty string. Setting the value to an empty + * string will effectively delete the attribute. + * + * The AMF transform IDs are used to identify specific transforms in the + * ACES Metadata File. Multiple transform IDs can be specified in a + * newline-separated string. + * + * The ICC profile name identifies the ICC color profile associated with + * this color space. This can be used to link OCIO color spaces with + * corresponding ICC profiles for applications that need to work with both + * color management systems. + */ + const char *getInterchangeAttribute(const char *attrName) const; + void setInterchangeAttribute(const char* attrName, const char *value); + std::map getInterchangeAttributes() const noexcept; + BitDepth getBitDepth() const noexcept; void setBitDepth(BitDepth bitDepth); diff --git a/src/OpenColorIO/Baker.cpp b/src/OpenColorIO/Baker.cpp index b8a78a958c..e92a4f3d31 100755 --- a/src/OpenColorIO/Baker.cpp +++ b/src/OpenColorIO/Baker.cpp @@ -126,7 +126,7 @@ void Baker::setFormat(const char * formatName) { if (formatInfoVec[i].capabilities & FORMAT_CAPABILITY_BAKE) { - getImpl()->m_formatName = formatName; + getImpl()->m_formatName = formatName ? formatName : ""; return; } } @@ -155,7 +155,7 @@ FormatMetadata & Baker::getFormatMetadata() void Baker::setInputSpace(const char * inputSpace) { - getImpl()->m_inputSpace = inputSpace; + getImpl()->m_inputSpace = inputSpace ? inputSpace : ""; } const char * Baker::getInputSpace() const @@ -165,7 +165,7 @@ const char * Baker::getInputSpace() const void Baker::setShaperSpace(const char * shaperSpace) { - getImpl()->m_shaperSpace = shaperSpace; + getImpl()->m_shaperSpace = shaperSpace ? shaperSpace : ""; } const char * Baker::getShaperSpace() const @@ -175,7 +175,7 @@ const char * Baker::getShaperSpace() const void Baker::setLooks(const char * looks) { - getImpl()->m_looks = looks; + getImpl()->m_looks = looks ? looks : ""; } const char * Baker::getLooks() const @@ -185,7 +185,7 @@ const char * Baker::getLooks() const void Baker::setTargetSpace(const char * targetSpace) { - getImpl()->m_targetSpace = targetSpace; + getImpl()->m_targetSpace = targetSpace ? targetSpace : ""; } const char * Baker::getTargetSpace() const @@ -205,7 +205,7 @@ const char * Baker::getView() const void Baker::setDisplayView(const char * display, const char * view) { - if (!display ^ !view) + if (!display || !view) { throw Exception("Both display and view must be set."); } diff --git a/src/OpenColorIO/ColorSpace.cpp b/src/OpenColorIO/ColorSpace.cpp index 4b7ecb25d0..98f776fe18 100644 --- a/src/OpenColorIO/ColorSpace.cpp +++ b/src/OpenColorIO/ColorSpace.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -13,6 +14,13 @@ #include "utils/StringUtils.h" +namespace +{ +const std::array knownInterchangeNames = { + "amf_transform_ids", + "icc_profile_name" }; +} + namespace OCIO_NAMESPACE { @@ -24,7 +32,9 @@ class ColorSpace::Impl std::string m_equalityGroup; std::string m_description; std::string m_encoding; + std::string m_interopID; StringUtils::StringVec m_aliases; + std::map m_interchangeAttribs; BitDepth m_bitDepth{ BIT_DEPTH_UNKNOWN }; bool m_isData{ false }; @@ -62,6 +72,8 @@ class ColorSpace::Impl m_equalityGroup = rhs.m_equalityGroup; m_description = rhs.m_description; m_encoding = rhs.m_encoding; + m_interopID = rhs.m_interopID; + m_interchangeAttribs= rhs.m_interchangeAttribs; m_bitDepth = rhs.m_bitDepth; m_isData = rhs.m_isData; m_referenceSpaceType = rhs.m_referenceSpaceType; @@ -195,7 +207,7 @@ const char * ColorSpace::getFamily() const noexcept void ColorSpace::setFamily(const char * family) { - getImpl()->m_family = family; + getImpl()->m_family = family ? family : ""; } const char * ColorSpace::getEqualityGroup() const noexcept @@ -205,7 +217,7 @@ const char * ColorSpace::getEqualityGroup() const noexcept void ColorSpace::setEqualityGroup(const char * equalityGroup) { - getImpl()->m_equalityGroup = equalityGroup; + getImpl()->m_equalityGroup = equalityGroup ? equalityGroup : ""; } const char * ColorSpace::getDescription() const noexcept @@ -215,7 +227,125 @@ const char * ColorSpace::getDescription() const noexcept void ColorSpace::setDescription(const char * description) { - getImpl()->m_description = description; + getImpl()->m_description = description ? description : ""; +} + +const char * ColorSpace::getInteropID() const noexcept +{ + return getImpl()->m_interopID.c_str(); +} + +void ColorSpace::setInteropID(const char * interopID) +{ + std::string id = interopID ? interopID : ""; + + if (!id.empty()) + { + // check if it only uses ASCII characters: 0-9, a-z, and the following characters (no spaces): + // . - _ ~ / * # % ^ + ( ) [ ] | + auto allowed = [](char c) + { + return (c >= '0' && c <= '9')|| + (c >= 'a' && c <= 'z')|| + c=='.'||c=='-'||c=='_'||c=='~'||c=='/'||c=='*'||c=='#'||c=='%'|| + c=='^'||c=='+'||c=='('||c==')'||c=='['||c==']'||c=='|'||c==':'; + }; + + if (!std::all_of(id.begin(), id.end(), allowed)) + { + std::ostringstream oss; + oss << "InteropID '" << id << "' contains invalid characters. " + "Only lowercase a-z, 0-9 and . - _ ~ / * # % ^ + ( ) [ ] | are allowed." << + std::endl; + throw Exception(oss.str().c_str()); + } + + // Check if has a namespace. + size_t pos = id.find(':'); + if (pos != std::string::npos) + { + // Namespace found, split into namespace and color space. + std::string ns = id.substr(0, pos); + std::string cs = id.substr(pos+1); + + // both should be non-empty + if (ns.empty() || cs.empty()) + { + std::ostringstream oss; + oss << "InteropID '" << id << "' is not valid. " + "If ':' is used, both the namespace and the color space parts must be non-empty." << + std::endl; + throw Exception(oss.str().c_str()); + } + + // More than one ':' is an error. + if (cs.find(':') != std::string::npos) + { + std::ostringstream oss; + oss << "ERROR: InteropID '" << id << "' is not valid. " + "Only one ':' is allowed to separate the namespace and the color space." << + std::endl; + throw Exception(oss.str().c_str()); + } + } + } + + getImpl()->m_interopID = id; +} + +const char * ColorSpace::getInterchangeAttribute(const char* attrName) const +{ + std::string name = attrName ? attrName : ""; + + for (auto& key : knownInterchangeNames) + { + // do case-insensitive comparison. + if (StringUtils::Compare(key, name)) + { + auto it = m_impl->m_interchangeAttribs.find(key); + if (it != m_impl->m_interchangeAttribs.end()) + { + return it->second.c_str(); + } + return ""; + } + } + + std::ostringstream oss; + oss << "Unknown attribute name '" << name << "'."; + throw Exception(oss.str().c_str()); +} + +void ColorSpace::setInterchangeAttribute(const char* attrName, const char* value) +{ + std::string name = attrName ? attrName : ""; + + for (auto& key : knownInterchangeNames) + { + // Do case-insensitive comparison. + if (StringUtils::Compare(key, name)) + { + // use key instead of name for storing in correct capitalization. + if (!value || !*value) + { + m_impl->m_interchangeAttribs.erase(key); + } + else + { + m_impl->m_interchangeAttribs[key] = value; + } + return; + } + } + + std::ostringstream oss; + oss << "Unknown attribute name '" << name << "'."; + throw Exception(oss.str().c_str()); +} + +std::map ColorSpace::getInterchangeAttributes() const noexcept +{ + return m_impl->m_interchangeAttribs; } BitDepth ColorSpace::getBitDepth() const noexcept @@ -265,7 +395,7 @@ const char * ColorSpace::getEncoding() const noexcept void ColorSpace::setEncoding(const char * encoding) { - getImpl()->m_encoding = encoding; + getImpl()->m_encoding = encoding ? encoding : ""; } bool ColorSpace::isData() const noexcept @@ -371,7 +501,6 @@ std::ostream & operator<< (std::ostream & os, const ColorSpace & cs) break; } os << "name=" << cs.getName() << ", "; - std::string str{ cs.getFamily() }; const auto numAliases = cs.getNumAliases(); if (numAliases == 1) { @@ -386,6 +515,15 @@ std::ostream & operator<< (std::ostream & os, const ColorSpace & cs) } os << "], "; } + + std::string str; + + str = cs.getInteropID(); + if (!str.empty()) + { + os << "interop_id=" << str << ", "; + } + str = cs.getFamily(); if (!str.empty()) { os << "family=" << str << ", "; @@ -429,6 +567,10 @@ std::ostream & operator<< (std::ostream & os, const ColorSpace & cs) { os << ", description=" << str; } + for (const auto& attr : cs.getInterchangeAttributes()) + { + os << ", " << attr.first << "=" << attr.second; + } if(cs.getTransform(COLORSPACE_DIR_TO_REFERENCE)) { os << ",\n " << cs.getName() << " --> Reference"; diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index 727d434e9f..67a699a553 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -247,7 +247,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 - 4 // Version 2 + 5 // Version 2 }; } // namespace @@ -1185,7 +1185,7 @@ ConstConfigRcPtr Config::CreateFromFile(const char * filename) // Check if it is an OCIOZ archive. if (magicNumber[0] == 'P' && magicNumber[1] == 'K') { - // Closing ifstream even though it should be close by ifstream deconstructor (RAII). + // Closing ifstream even though it should be closed by ifstream destructor (RAII). ifstream.close(); // The file should be an OCIOZ archive file. @@ -1499,7 +1499,8 @@ void Config::validate() const // Check for interchange roles requirements - scene-referred and display-referred. - if (getMajorVersion() >= 2 && getMinorVersion() >= 2) + unsigned int versionHex = (getMajorVersion() << 24) | (getMinorVersion() << 16); + if (versionHex >= 0x02020000u) // v2.2 or higher { bool hasRoleSceneLinear = false; bool hasRoleCompositingLog = false; @@ -5589,6 +5590,8 @@ void Config::Impl::checkVersionConsistency(ConstTransformRcPtr & transform) cons void Config::Impl::checkVersionConsistency() const { + unsigned int hexVersion = (m_majorVersion << 24) | (m_minorVersion << 16); + // Check for the Transforms. ConstTransformVec transforms; @@ -5658,18 +5661,44 @@ void Config::Impl::checkVersionConsistency() const } } - // Check for the DisplayColorSpaces. + // Check ColorSpace properties. - if (m_majorVersion < 2) + const int nbCS = m_allColorSpaces->getNumColorSpaces(); + for (int i = 0; i < nbCS; ++i) { - const int nbCS = m_allColorSpaces->getNumColorSpaces(); - for (int i = 0; i < nbCS; ++i) + // Check for display color spaces. + + const auto & cs = m_allColorSpaces->getColorSpaceByIndex(i); + if (m_majorVersion < 2) { - const auto & cs = m_allColorSpaces->getColorSpaceByIndex(i); - if (MatchReferenceType(SEARCH_REFERENCE_SPACE_DISPLAY, cs->getReferenceSpaceType())) + if (MatchReferenceType(SEARCH_REFERENCE_SPACE_DISPLAY, cs->getReferenceSpaceType())) { throw Exception("Only version 2 (or higher) can have DisplayColorSpaces."); } + } + + // Check for new color space attributes. + + if (m_majorVersion < 2) + { + if (*cs->getInteropID()) + { + std::ostringstream os; + os << "Config failed validation. The color space '" << cs->getName() << "' "; + os << "has non-empty InteropID and config version is less than 2.0."; + throw Exception(os.str().c_str()); + } + } + + if (hexVersion < 0x02050000) + { + if (cs->getInterchangeAttributes().size()>0) + { + std::ostringstream os; + os << "Config failed validation. The color space '" << cs->getName() << "' "; + os << "has non-empty interchange attributes and config version is less than 2.5."; + throw Exception(os.str().c_str()); + } } } diff --git a/src/OpenColorIO/Context.cpp b/src/OpenColorIO/Context.cpp index 3a1294be58..b7e71ae6e7 100644 --- a/src/OpenColorIO/Context.cpp +++ b/src/OpenColorIO/Context.cpp @@ -253,7 +253,7 @@ void Context::setWorkingDir(const char * dirname) { AutoMutex lock(getImpl()->m_resultsCacheMutex); - getImpl()->m_workingDir = dirname; + getImpl()->m_workingDir = dirname ? dirname : ""; getImpl()->clearCaches(); } diff --git a/src/OpenColorIO/Look.cpp b/src/OpenColorIO/Look.cpp index c0e7cbef13..9830d0a460 100644 --- a/src/OpenColorIO/Look.cpp +++ b/src/OpenColorIO/Look.cpp @@ -86,7 +86,7 @@ const char * Look::getName() const void Look::setName(const char * name) { - getImpl()->m_name = name; + getImpl()->m_name = name ? name : ""; } const char * Look::getProcessSpace() const @@ -96,7 +96,7 @@ const char * Look::getProcessSpace() const void Look::setProcessSpace(const char * processSpace) { - getImpl()->m_processSpace = processSpace; + getImpl()->m_processSpace = processSpace ? processSpace : ""; } ConstTransformRcPtr Look::getTransform() const @@ -126,7 +126,7 @@ const char * Look::getDescription() const void Look::setDescription(const char * description) { - getImpl()->m_description = description; + getImpl()->m_description = description ? description : ""; } bool CollectContextVariables(const Config & config, diff --git a/src/OpenColorIO/OCIOYaml.cpp b/src/OpenColorIO/OCIOYaml.cpp index b1cee18982..eea8eef99a 100644 --- a/src/OpenColorIO/OCIOYaml.cpp +++ b/src/OpenColorIO/OCIOYaml.cpp @@ -33,6 +33,29 @@ namespace { typedef YAML::const_iterator Iterator; +void SanitizeLoadedNewlines(std::string &str) +{ + if (!str.empty()) + { + // YAML is changing the trailing newlines when reading them: + // - Written as YAML::Literal (starts with a "|"), descriptions will be read back with a + // single newline. One is added if there was none, only one is kept if there were + // several. + // - Written as YAML::Value string (does not start with "|"), all trailing newlines ('\n') + // are preserved. + // Trailing newlines are inconsistently preserved, lets remove them in all cases. + auto last = str.back(); + while (last == '\n' && str.length()) + { + str.pop_back(); + last = str.back(); + } + } + // Also, note that a \n is only interpreted as a newline if it is used in a string that is + // within double quotes. E.g., "A string \n with embedded \n newlines." Indeed, without the + // double quotes the backslash is generally not interpreted as an escape character in YAML. +} + // Basic types inline void load(const YAML::Node& node, bool& x) @@ -185,25 +208,7 @@ inline void save(YAML::Emitter& out, Interpolation interp) inline void loadDescription(const YAML::Node& node, std::string& x) { load(node, x); - if (!x.empty()) - { - // YAML is changing the trailing newlines when reading them: - // - Written as YAML::Literal (starts with a "|"), descriptions will be read back with a - // single newline. One is added if there was none, only one is kept if there were - // several. - // - Written as YAML::Value string (does not start with "|"), all trailing newlines ('\n') - // are preserved. - // Trailing newlines are inconsistently preserved, lets remove them in all cases. - auto last = x.back(); - while (last == '\n' && x.length()) - { - x.pop_back(); - last = x.back(); - } - } - // Also, note that a \n is only interpreted as a newline if it is used in a string that is - // within double quotes. E.g., "A string \n with embedded \n newlines." Indeed, without the - // double quotes the backslash is generally not interpreted as an escape character in YAML. + SanitizeLoadedNewlines(x); } inline void saveDescription(YAML::Emitter & out, const char * desc) @@ -319,6 +324,31 @@ inline void CheckDuplicates(const YAML::Node & node) } } +// Custom Key Loader + +struct CustomKeysLoader +{ + using Type = std::vector>; + Type m_keyVals; +}; + +inline void loadCustomKeys(const YAML::Node& node, CustomKeysLoader & ck, const char* sectionName) +{ + if (node.Type() == YAML::NodeType::Map) + { + for (Iterator iter = node.begin(); iter != node.end(); ++iter) + { + ck.m_keyVals.push_back(std::make_pair(iter->first, iter->second)); + } + } + else + { + std::ostringstream ss; + ss << "Expected a YAML map in the " << sectionName << " section."; + throwError(node, ss.str().c_str()); + } +} + // View inline void load(const YAML::Node& node, View& v) @@ -3202,11 +3232,38 @@ inline void load(const YAML::Node& node, ColorSpaceRcPtr& cs, unsigned int major cs->addAlias(alias.c_str()); } } + else if (key == "interop_id") + { + load(iter->second, stringval); + cs->setInteropID(stringval.c_str()); + } else if(key == "description") { loadDescription(iter->second, stringval); cs->setDescription(stringval.c_str()); } + else if (key == "interchange") + { + CustomKeysLoader kv; + loadCustomKeys(iter->second, kv, "ColorSpace interchange"); + + for (const auto& keyval : kv.m_keyVals) + { + std::string keystr = keyval.first.as(); + std::string valstr = keyval.second.as(); + SanitizeLoadedNewlines(valstr); + + // OCIO exception means the key is not recognized. Convert that to a warning. + try + { + cs->setInterchangeAttribute(keystr.c_str(), valstr.c_str()); + } + catch (Exception &) + { + LogUnknownKeyWarning(key, keyval.first); + } + } + } else if(key == "family") { load(iter->second, stringval); @@ -3323,11 +3380,23 @@ inline void save(YAML::Emitter& out, ConstColorSpaceRcPtr cs, unsigned int major } out << YAML::Flow << YAML::Value << aliases; } + + const std::string interopID{ cs->getInteropID() }; + if (!interopID.empty()) + { + out << YAML::Key << "interop_id"; + out << YAML::Value << interopID; + } + out << YAML::Key << "family" << YAML::Value << cs->getFamily(); + out << YAML::Key << "equalitygroup" << YAML::Value << cs->getEqualityGroup(); + out << YAML::Key << "bitdepth" << YAML::Value; save(out, cs->getBitDepth()); + saveDescription(out, cs->getDescription()); + out << YAML::Key << "isdata" << YAML::Value << cs->isData(); if(cs->getNumCategories() > 0) @@ -3348,6 +3417,40 @@ inline void save(YAML::Emitter& out, ConstColorSpaceRcPtr cs, unsigned int major out << YAML::Value << is; } + auto interchangemap = cs->getInterchangeAttributes(); + if (interchangemap.size()) + { + out << YAML::Key << "interchange"; + out << YAML::Value; + out << YAML::BeginMap; + for (const auto& keyval : interchangemap) + { + //out << YAML::Key << keyval.first; + //<< YAML::Value << keyval.second; + + // Remove trailing newlines so that only one is saved because they won't + // be read back. + std::string valStr{keyval.second}; + { + auto last = valStr.back(); + while (last == '\n' && valStr.length()) + { + valStr.pop_back(); + last = valStr.back(); + } + } + + out << YAML::Key << keyval.first << YAML::Value; + if (valStr.find_first_of('\n') != std::string::npos) + { + out << YAML::Literal; + } + out << valStr; + } + + out << YAML::EndMap; + } + out << YAML::Key << "allocation" << YAML::Value; save(out, cs->getAllocation()); if(cs->getAllocationNumVars() > 0) @@ -3778,30 +3881,6 @@ inline void save(YAML::Emitter & out, ConstNamedTransformRcPtr & nt, unsigned in // File rules -struct CustomKeysLoader -{ - StringUtils::StringVec m_keyVals; -}; - -inline void loadCustomKeys(const YAML::Node& node, CustomKeysLoader & ck) -{ - if (node.Type() == YAML::NodeType::Map) - { - for (Iterator iter = node.begin(); iter != node.end(); ++iter) - { - const std::string & key = iter->first.as(); - const std::string & val = iter->second.as(); - - ck.m_keyVals.push_back(key); - ck.m_keyVals.push_back(val); - } - } - else - { - throwError(node, "The 'file_rules' custom attributes need to be a YAML map."); - } -} - inline void load(const YAML::Node & node, FileRulesRcPtr & fr, bool & defaultRuleFound) { if (node.Tag() != "Rule") @@ -3811,7 +3890,7 @@ inline void load(const YAML::Node & node, FileRulesRcPtr & fr, bool & defaultRul std::string stringval; std::string name, colorspace, pattern, extension, regex; - StringUtils::StringVec keyVals; + CustomKeysLoader::Type keyVals; for (Iterator iter = node.begin(); iter != node.end(); ++iter) { @@ -3847,7 +3926,7 @@ inline void load(const YAML::Node & node, FileRulesRcPtr & fr, bool & defaultRul else if (key == FileRuleUtils::CustomKey) { CustomKeysLoader kv; - loadCustomKeys(iter->second, kv); + loadCustomKeys(iter->second, kv, "file_rules custom attribute"); keyVals = kv.m_keyVals; } else @@ -3918,11 +3997,15 @@ inline void load(const YAML::Node & node, FileRulesRcPtr & fr, bool & defaultRul fr->insertRule(pos, name.c_str(), colorspace.c_str(), regex.c_str()); } } - const auto numKeyVal = keyVals.size() / 2; - for (size_t i = 0; i < numKeyVal; ++i) + + for (const auto& keyval : keyVals) { - fr->setCustomKey(pos, keyVals[i * 2].c_str(), keyVals[i * 2 + 1].c_str()); + fr->setCustomKey( + pos, + keyval.first.as().c_str(), + keyval.second.as().c_str()); } + } catch (Exception & ex) { @@ -3985,7 +4068,7 @@ inline void load(const YAML::Node & node, ViewingRulesRcPtr & vr) std::string stringval; std::string name; StringUtils::StringVec colorspaces, encodings; - StringUtils::StringVec keyVals; + CustomKeysLoader::Type keyVals; for (Iterator iter = node.begin(); iter != node.end(); ++iter) { @@ -4027,7 +4110,7 @@ inline void load(const YAML::Node & node, ViewingRulesRcPtr & vr) else if (key == ViewingRuleUtils::CustomKey) { CustomKeysLoader kv; - loadCustomKeys(iter->second, kv); + loadCustomKeys(iter->second, kv, "viewing_rules custom attribute"); keyVals = kv.m_keyVals; } else @@ -4050,10 +4133,12 @@ inline void load(const YAML::Node & node, ViewingRulesRcPtr & vr) vr->addEncoding(pos, is.c_str()); } - const auto numKeyVal = keyVals.size() / 2; - for (size_t i = 0; i < numKeyVal; ++i) + for (const auto& keyval : keyVals) { - vr->setCustomKey(pos, keyVals[i * 2].c_str(), keyVals[i * 2 + 1].c_str()); + vr->setCustomKey( + pos, + keyval.first.as().c_str(), + keyval.second.as().c_str()); } } catch (Exception & ex) @@ -4754,10 +4839,12 @@ inline void save(YAML::Emitter & out, const Config & config) { std::stringstream ss; const unsigned configMajorVersion = config.getMajorVersion(); + const unsigned configMinorVersion = config.getMinorVersion(); + ss << configMajorVersion; - if(config.getMinorVersion()!=0) + if(configMinorVersion != 0) { - ss << "." << config.getMinorVersion(); + ss << "." << configMinorVersion; } out << YAML::Block; diff --git a/src/OpenColorIO/Platform.cpp b/src/OpenColorIO/Platform.cpp index 1dbd846f35..0e7fc68b2d 100644 --- a/src/OpenColorIO/Platform.cpp +++ b/src/OpenColorIO/Platform.cpp @@ -154,6 +154,11 @@ bool isEnvPresent(const char * name) int Strcasecmp(const char * str1, const char * str2) { + if (!str1 || !str2) + { + throw Exception("String pointer for comparison must not be null."); + } + #ifdef _WIN32 return ::_stricmp(str1, str2); #else @@ -163,6 +168,11 @@ int Strcasecmp(const char * str1, const char * str2) int Strncasecmp(const char * str1, const char * str2, size_t n) { + if (!str1 || !str2) + { + throw Exception("String pointer for comparison must not be null."); + } + #ifdef _WIN32 return ::_strnicmp(str1, str2, n); #else diff --git a/src/OpenColorIO/ViewTransform.cpp b/src/OpenColorIO/ViewTransform.cpp index 0a7ca21091..7fd8914a34 100644 --- a/src/OpenColorIO/ViewTransform.cpp +++ b/src/OpenColorIO/ViewTransform.cpp @@ -91,7 +91,7 @@ const char * ViewTransform::getName() const noexcept void ViewTransform::setName(const char * name) noexcept { - getImpl()->m_name = name; + getImpl()->m_name = name ? name : ""; } const char * ViewTransform::getFamily() const noexcept @@ -101,7 +101,7 @@ const char * ViewTransform::getFamily() const noexcept void ViewTransform::setFamily(const char * family) { - getImpl()->m_family = family; + getImpl()->m_family = family ? family : ""; } const char * ViewTransform::getDescription() const noexcept @@ -111,7 +111,7 @@ const char * ViewTransform::getDescription() const noexcept void ViewTransform::setDescription(const char * description) { - getImpl()->m_description = description; + getImpl()->m_description = description ? description : ""; } bool ViewTransform::hasCategory(const char * category) const diff --git a/src/OpenColorIO/ViewingRules.cpp b/src/OpenColorIO/ViewingRules.cpp index be780c94e1..bbe27172c1 100644 --- a/src/OpenColorIO/ViewingRules.cpp +++ b/src/OpenColorIO/ViewingRules.cpp @@ -38,8 +38,6 @@ class ViewingRule { public: - using CustomKeys = std::map; - ViewingRule() = delete; ViewingRule(const ViewingRule &) = delete; ViewingRule & operator=(const ViewingRule &) = delete; 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/src/apps/ociocheck/main.cpp b/src/apps/ociocheck/main.cpp index b3c8c33854..5c66a7eba0 100644 --- a/src/apps/ociocheck/main.cpp +++ b/src/apps/ociocheck/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace OCIO = OCIO_NAMESPACE; @@ -26,6 +27,83 @@ const char * DESC_STRING = "\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"; + +// returns true if the interopID is valid +bool isValidInteropID(const std::string& id) +{ + // See https://github.com/AcademySoftwareFoundation/ColorInterop for the details. + + static const std::set cifTextureIDs = { + "lin_ap1_scene", + "lin_ap0_scene", + "lin_rec709_scene", + "lin_p3d65_scene", + "lin_rec2020_scene", + "lin_adobergb_scene", + "lin_ciexyzd65_scene", + "srgb_rec709_scene", + "g22_rec709_scene", + "g18_rec709_scene", + "srgb_ap1_scene", + "g22_ap1_scene", + "srgb_p3d65_scene", + "g22_adobergb_scene", + "data", + "unknown" + }; + + static const std::set cifDisplayIDs = { + "srgb_rec709_display", + "g24_rec709_display", + "srgb_p3d65_display", + "srgbx_p3d65_display", + "pq_p3d65_display", + "pq_rec2020_display", + "hlg_rec2020_display", + "g22_rec709_display", + "g22_adobergb_display", + "g26_p3d65_display", + "g26_xyzd65_display", + "pq_xyzd65_display", + }; + + if (id.empty()) + return true; + + // Check if has a namespace. + size_t pos = id.find(':'); + if (pos == std::string::npos) + { + // No namespace, so id must be in the Color Interop Forum ID list. + if (cifTextureIDs.count(id) == 0 && cifDisplayIDs.count(id)==0) + { + std::cout << "ERROR: InteropID '" << id << "' is not valid. " + "It should either be one of Color Interop Forum standard IDs or " + "it must contain a namespace followed by ':', e.g. 'mycompany:mycolorspace'." << + std::endl; + return false; + } + } + else + { + // Namespace found, split into namespace and id. + std::string ns = id.substr(0, pos); + std::string cs = id.substr(pos+1); + + // Id should not be in the Color Interop Forum ID list. + if (cifTextureIDs.count(cs) > 0 || cifDisplayIDs.count(cs)> 0) + { + std::cout << "ERROR: InteropID '" << id << "' is not valid. " + "The ID part must not be one of the Color Interop Forum standard IDs when a namespace is used." << + std::endl; + return false; + } + } + + // all clear. + return true; +} + int main(int argc, const char **argv) { bool help = false; @@ -298,6 +376,15 @@ int main(int argc, const char **argv) OCIO::COLORSPACE_ALL, i)); + std::string interopID = cs->getInteropID(); + if (!interopID.empty()) + { + if (!isValidInteropID(interopID)) + { + errorcount += 1; + } + } + // Try to load the transform for the to_ref direction -- this will load any LUTs. bool toRefOK = true; std::string toRefErrorText; diff --git a/src/apps/ocioconvert/main.cpp b/src/apps/ocioconvert/main.cpp index e8071c76fa..31a5ed3542 100644 --- a/src/apps/ocioconvert/main.cpp +++ b/src/apps/ocioconvert/main.cpp @@ -658,6 +658,14 @@ int main(int argc, const char **argv) if (outputcolorspace) { imgOutput->attribute("oiio:ColorSpace", outputcolorspace); + + // Set the color space interopID if available. + auto cs = config->getColorSpace(outputcolorspace); + const char* interopID = cs ? cs->getInteropID() : nullptr; + if(interopID && *interopID) + { + imgOutput->attribute("colorInteropID", interopID); + } } imgOutput->write(outputimage, userOutputBitDepth); diff --git a/src/bindings/python/PyColorSpace.cpp b/src/bindings/python/PyColorSpace.cpp index 6827943bc0..a115b20bd9 100644 --- a/src/bindings/python/PyColorSpace.cpp +++ b/src/bindings/python/PyColorSpace.cpp @@ -90,7 +90,9 @@ void bindPyColorSpace(py::module & m) const std::vector & allocationVars, const TransformRcPtr & toReference, const TransformRcPtr & fromReference, - const std::vector & categories) + const std::vector & categories, + const std::string & interopID + ) { ColorSpaceRcPtr p = ColorSpace::Create(referenceSpace); if (!aliases.empty()) @@ -102,11 +104,12 @@ void bindPyColorSpace(py::module & m) } } // Setting the name will remove alias named the same, so set name after. - if (!name.empty()) { p->setName(name.c_str()); } - if (!family.empty()) { p->setFamily(family.c_str()); } - if (!encoding.empty()) { p->setEncoding(encoding.c_str()); } - if (!equalityGroup.empty()) { p->setEqualityGroup(equalityGroup.c_str()); } - if (!description.empty()) { p->setDescription(description.c_str()); } + if (!name.empty()) { p->setName(name.c_str()); } + if (!family.empty()) { p->setFamily(family.c_str()); } + if (!encoding.empty()) { p->setEncoding(encoding.c_str()); } + if (!equalityGroup.empty()) { p->setEqualityGroup(equalityGroup.c_str()); } + if (!description.empty()) { p->setDescription(description.c_str()); } + if (!interopID.empty()) { p->setInteropID(interopID.c_str()); } p->setBitDepth(bitDepth); p->setIsData(isData); p->setAllocation(allocation); @@ -150,6 +153,7 @@ void bindPyColorSpace(py::module & m) "toReference"_a = DEFAULT->getTransform(COLORSPACE_DIR_TO_REFERENCE), "fromReference"_a = DEFAULT->getTransform(COLORSPACE_DIR_FROM_REFERENCE), "categories"_a = getCategoriesStdVec(DEFAULT), + "interopID"_a = DEFAULT->getInteropID(), DOC(ColorSpace, Create, 2)) .def("__deepcopy__", [](const ConstColorSpaceRcPtr & self, py::dict) @@ -193,6 +197,14 @@ void bindPyColorSpace(py::module & m) DOC(ColorSpace, getDescription)) .def("setDescription", &ColorSpace::setDescription, "description"_a, DOC(ColorSpace, setDescription)) + .def("getInteropID", &ColorSpace::getInteropID, + DOC(ColorSpace, getInteropID)) + .def("setInteropID", &ColorSpace::setInteropID, "interopID"_a, + DOC(ColorSpace, setInteropID)) + .def("getInterchangeAttribute", &ColorSpace::getInterchangeAttribute, "attrName"_a, + DOC(ColorSpace, getInterchangeAttribute)) + .def("setInterchangeAttribute", &ColorSpace::setInterchangeAttribute, "attrName"_a, "attrValue"_a, + DOC(ColorSpace, setInterchangeAttribute)) .def("getBitDepth", &ColorSpace::getBitDepth, DOC(ColorSpace, getBitDepth)) .def("setBitDepth", &ColorSpace::setBitDepth, "bitDepth"_a, diff --git a/src/libutils/imageioapphelpers/imageio.cpp b/src/libutils/imageioapphelpers/imageio.cpp index b1701f8909..cda93b3e3a 100644 --- a/src/libutils/imageioapphelpers/imageio.cpp +++ b/src/libutils/imageioapphelpers/imageio.cpp @@ -192,6 +192,9 @@ ptrdiff_t ImageIO::getImageBytes() const void ImageIO::init(const ImageIO & img, BitDepth bitDepth) { m_impl->init(*img.m_impl, bitDepth); + + // Do not propagate colorInteropID. + attribute("colorInteropID", "unknown"); } void ImageIO::init(long width, long height, ChannelOrdering chanOrder, BitDepth bitDepth) diff --git a/tests/cpu/CPUProcessor_tests.cpp b/tests/cpu/CPUProcessor_tests.cpp index c2fed39022..925a75397c 100644 --- a/tests/cpu/CPUProcessor_tests.cpp +++ b/tests/cpu/CPUProcessor_tests.cpp @@ -57,8 +57,9 @@ OCIO_ADD_TEST(CPUProcessor, flag_composition) // a missing/partial optimization. -template -OCIO::ConstCPUProcessorRcPtr ComputeValues(OCIO::ConstProcessorRcPtr processor, +template +OCIO::ConstCPUProcessorRcPtr ComputeValues(unsigned line, + OCIO::ConstProcessorRcPtr processor, const void * inImg, OCIO::ChannelOrdering inChans, const void * resImg, @@ -147,12 +148,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 1.5025f, 0.9050f, 1.5896f, 1.5000f, 2.4002f, 1.6505f, 2.0707f, 0.5000f }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS, + 1e-5f); } { @@ -161,12 +162,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 0.182999f, 0.9050f, 2.9091f, 1.5000f, 1.0807f, 1.6505f, 3.3902f, 0.5000f }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS, + 1e-5f); } { @@ -175,12 +176,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 0.602300f, 0.585199f, 1.909399f, 2.400200f, 1.500000f, 1.330700f, 2.390500f, 1.400200f }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_ABGR, - &resImg[0], OCIO::CHANNEL_ORDERING_ABGR, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_ABGR, + &resImg[0], OCIO::CHANNEL_ORDERING_ABGR, + NB_PIXELS, + 1e-5f); } { @@ -189,12 +190,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 1.5896f, 0.9050f, 1.5025f, 1.5000f, 2.0707f, 1.6505f, 2.4002f, 0.5000f }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS, + 1e-5f); } { @@ -203,12 +204,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 1.5000f, 1.5896f, 0.9050f, 1.5025f, 0.5000f, 2.0707f, 1.6505f, 2.4002f }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_ABGR, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_ABGR, + NB_PIXELS, + 1e-5f); } { @@ -222,12 +223,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 1.5025f, 0.9050f, 1.5896f, 2.4002f, 1.6505f, 2.0707f }; - ComputeValues(processor, - &inImg[0], OCIO::CHANNEL_ORDERING_RGB, - &resImg[0], OCIO::CHANNEL_ORDERING_RGB, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &inImg[0], OCIO::CHANNEL_ORDERING_RGB, + &resImg[0], OCIO::CHANNEL_ORDERING_RGB, + NB_PIXELS, + 1e-5f); } { @@ -241,12 +242,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 0.182999f, 0.905000f, 2.909100f, 1.080700f, 1.650500f, 3.390200f }; - ComputeValues(processor, - &inImg[0], OCIO::CHANNEL_ORDERING_BGR, - &resImg[0], OCIO::CHANNEL_ORDERING_BGR, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &inImg[0], OCIO::CHANNEL_ORDERING_BGR, + &resImg[0], OCIO::CHANNEL_ORDERING_BGR, + NB_PIXELS, + 1e-5f); } { @@ -260,12 +261,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 1.58960f, 0.9050f, 1.5025f, 2.070699f, 1.6505f, 2.4002f }; - ComputeValues(processor, - &inImg[0], OCIO::CHANNEL_ORDERING_RGB, - &resImg[0], OCIO::CHANNEL_ORDERING_BGR, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &inImg[0], OCIO::CHANNEL_ORDERING_RGB, + &resImg[0], OCIO::CHANNEL_ORDERING_BGR, + NB_PIXELS, + 1e-5f); } { @@ -279,12 +280,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 1.58960f, 0.9050f, 1.5025f, 0.5f, 2.070699f, 1.6505f, 2.4002f, 0.5f }; - ComputeValues(processor, - &inImg[0], OCIO::CHANNEL_ORDERING_RGB, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &inImg[0], OCIO::CHANNEL_ORDERING_RGB, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS, + 1e-5f); } { @@ -298,12 +299,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 1.58960f, 0.9050f, 1.5025f, 2.070699f, 1.6505f, 2.4002f }; - ComputeValues(processor, - &inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGR, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGR, + NB_PIXELS, + 1e-5f); } const std::vector ui16_inImg = @@ -317,12 +318,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 1.40117657f, 0.40245315f, 0.08460631f, 0.5f, 1.47832620f, 0.70781672f, 1.08070004f, 0.5f }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS, - 1e-5f); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS, + 1e-5f); } { @@ -331,11 +332,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 65535, 26375, 5545, 32768, 65535, 46387, 65535, 32768 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } { @@ -344,11 +345,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 5545, 26375, 65535, 32768, 65535, 46387, 65535, 32768 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS); } { @@ -357,11 +358,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 5545, 26375, 65535, 65535, 46387, 65535 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGR, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGR, + NB_PIXELS); } { @@ -370,11 +371,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 255, 103, 22, 128, 255, 180, 255, 128 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } { @@ -383,11 +384,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 22, 103, 255, 255, 180, 255 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGR, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGR, + NB_PIXELS); } { @@ -396,11 +397,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 128, 22, 103, 255, 128, 255, 180, 255 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_ABGR, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_ABGR, + NB_PIXELS); } // Test OCIO::BIT_DEPTH_UINT10. @@ -411,11 +412,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 1023, 412, 87, 512, 1023, 724, 1023, 512 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &ui10_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &ui10_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); const std::vector ui10_inImg = { 0, 8, 12, 256, @@ -427,11 +428,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 65535, 27272, 9389, 65535, 65535, 28297, 11439, 65535 }; - ComputeValues(processor, - &ui10_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &ui16_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui10_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &ui16_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } // Test OCIO::BIT_DEPTH_UINT12. @@ -442,11 +443,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 4095, 1648, 346, 2048, 4095, 2899, 4095, 2048 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &ui12_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &ui12_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); const std::vector ui12_inImg = { 0, 8, 12, 1024, @@ -458,11 +459,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_matrix) 65535, 26503, 6313, 65535, 65535, 26759, 6825, 65535 }; - ComputeValues(processor, - &ui12_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &ui16_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui12_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &ui16_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } } @@ -499,12 +500,12 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 0.29089212f, 0.50935059f, 1.91091322f, 1, 64, 64, 64, 0 }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS, - 1e-7f); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS, + 1e-7f); } { @@ -514,11 +515,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 19064, 33380, 65535, 65535, 65535, 65535, 65535, 0 }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } const std::vector ui16_inImg = @@ -534,11 +535,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 0.00601041f, 0.00912247f, 0.01456576f, 0.00097657f, 0.03030112f, 0.13105739f, 64, 0.00781261f }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS, 1e-7f); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS, 1e-7f); } { @@ -548,11 +549,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 394, 598, 955, 64, 1986, 8589, 65535, 512 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } { @@ -562,11 +563,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 955, 598, 394, 64, 65535, 8589, 1986, 512 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } { @@ -576,11 +577,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 955, 598, 394, 64, 65535, 8589, 1986, 512 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS); } { @@ -590,11 +591,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 955, 598, 394, 65535, 8589, 1986 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGR, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGR, + NB_PIXELS); } { @@ -604,11 +605,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 394, 598, 955, 64, 1986, 8589, 65535, 512 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS); } { @@ -624,11 +625,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 955, 598, 394, 0, 65535, 8589, 1986, 0 }; - ComputeValues(processor, - &my_i_inImg[0], OCIO::CHANNEL_ORDERING_RGB, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &my_i_inImg[0], OCIO::CHANNEL_ORDERING_RGB, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS); } // Test OCIO::BIT_DEPTH_UINT10. @@ -640,11 +641,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 6, 9, 15, 1, 31, 134, 1023, 8 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &ui10_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &ui10_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } { @@ -660,11 +661,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 36, 106, 252, 0, 48, 1023, 384, 0 }; - ComputeValues(processor, - &ui10_inImg[0], OCIO::CHANNEL_ORDERING_RGB, - &ui10_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui10_inImg[0], OCIO::CHANNEL_ORDERING_RGB, + &ui10_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); const std::vector ui16_resImg = { 0, 394, 955, 0, @@ -672,11 +673,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 2301, 6794, 16162, 0, 3092, 65535, 24593, 0 }; - ComputeValues(processor, - &ui10_inImg[0], OCIO::CHANNEL_ORDERING_RGB, - &ui16_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui10_inImg[0], OCIO::CHANNEL_ORDERING_RGB, + &ui16_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } // Test OCIO::BIT_DEPTH_UINT12. @@ -688,11 +689,11 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 25, 37, 60, 4, 124, 537, 4095, 32 }; - ComputeValues(processor, - &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &ui12_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui16_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &ui12_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } { @@ -708,22 +709,22 @@ OCIO_ADD_TEST(CPUProcessor, with_one_1d_lut) 49, 103, 193, 0, 424, 1009, 4095, 0 }; - ComputeValues(processor, - &ui12_inImg[0], OCIO::CHANNEL_ORDERING_RGB, - &ui12_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui12_inImg[0], OCIO::CHANNEL_ORDERING_RGB, + &ui12_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); const std::vector ui16_resImg = { 0, 178, 394, 0, 598, 955, 1655, 0, 779, 1655, 3089, 0, 6789, 16143, 65535, 0 }; - ComputeValues(processor, - &ui12_inImg[0], OCIO::CHANNEL_ORDERING_RGB, - &ui16_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &ui12_inImg[0], OCIO::CHANNEL_ORDERING_RGB, + &ui16_resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } } @@ -794,11 +795,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 0.15488569f, 1.69210147f, 1.90666747f, 1.0f, 0.81575858f, 64.0f, 64.0f, 0.0f }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS, 1e-7f); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS, 1e-7f); } { @@ -808,11 +809,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 10150, 65535, 65535, 65535, 53461, 65535, 65535, 0 }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } const std::vector i_inImg = @@ -828,12 +829,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 0.0f, 0.08474064f, 0.01450117f, 0.0f, 0.0f, 0.24826171f, 56.39490891f, 1.0f }; - auto cpuProcessor = ComputeValues(processor, - &i_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS, 1e-7f); + auto cpuProcessor = ComputeValues( + __LINE__, processor, + &i_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS, 1e-7f); // SSE2/AVX/AVX2 generate a slightly different LUT1D // floating error below the absErrorThreshold, but cacheID hash will be different @@ -874,11 +874,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 0, 5553, 950, 0, 0, 16270, 65535, 65535 }; - ComputeValues(processor, - &i_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &i_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } { @@ -888,11 +888,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 0, 5553, 388, 0, 53461, 16270, 1982, 65535 }; - ComputeValues(processor, - &i_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &i_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } { @@ -902,11 +902,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 950, 5553, 0, 0, 65535, 16270, 0, 65535 }; - ComputeValues(processor, - &i_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &i_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS); } { @@ -916,11 +916,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 388, 5553, 0, 0, 1982, 16270, 53461, 65535 }; - ComputeValues(processor, - &i_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &i_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS); } } @@ -960,11 +960,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 0.10089212f, 0.69935059f, 1.91072320f, 1.0f, 63.81000137f, 64.19000244f, 63.99980927f, 0.0f }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS, 1e-7f); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS, 1e-7f); } { @@ -974,11 +974,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 6612, 45832, 65535, 65535, 65535, 65535, 65535, 0 }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS); } const std::vector i_inImg = @@ -993,11 +993,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) -0.18398958f, 0.19912247f, 0.01437576f, 0.0f, -0.15969887f, 0.32105737f, 63.99980927f, 0.0f }; - ComputeValues(processor, - &i_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS, 1e-7f); + ComputeValues( + __LINE__, processor, + &i_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS, 1e-7f); } { @@ -1007,11 +1007,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 381, 13049, 0, 0, 1973, 21040, 65535, 0 }; - ComputeValues(processor, - &i_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &i_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS); } } @@ -1053,11 +1053,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) -0.23451784f, 0.92250210f, 3.26448941f, 1.0f, 3.43709063f, 3.43709063f, 3.43709063f, 0.0f }; - ComputeValues(processor, - &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, - &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, - NB_PIXELS, 1e-7f); + ComputeValues( + __LINE__, processor, + &f_inImg[0], OCIO::CHANNEL_ORDERING_RGBA, + &resImg[0], OCIO::CHANNEL_ORDERING_RGBA, + NB_PIXELS, 1e-7f); } const std::vector i_inImg = @@ -1073,11 +1073,11 @@ OCIO_ADD_TEST(CPUProcessor, with_several_ops) 0, 0, 0, 0, 0, 12526, 65535, 0 }; - ComputeValues(processor, - &i_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, - &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, - NB_PIXELS); + ComputeValues( + __LINE__, processor, + &i_inImg[0], OCIO::CHANNEL_ORDERING_BGRA, + &resImg[0], OCIO::CHANNEL_ORDERING_BGRA, + NB_PIXELS); } } } diff --git a/tests/cpu/ColorSpace_tests.cpp b/tests/cpu/ColorSpace_tests.cpp index 54521be171..abb3b25063 100644 --- a/tests/cpu/ColorSpace_tests.cpp +++ b/tests/cpu/ColorSpace_tests.cpp @@ -10,6 +10,7 @@ #include "testutils/UnitTest.h" #include "UnitTestUtils.h" +#include "UnitTestLogUtils.h" namespace OCIO = OCIO_NAMESPACE; @@ -39,7 +40,39 @@ OCIO_ADD_TEST(ColorSpace, basic) OCIO_CHECK_ASSERT(!cs->isData()); OCIO_CHECK_EQUAL(OCIO::ALLOCATION_UNIFORM, cs->getAllocation()); OCIO_CHECK_EQUAL(0, cs->getAllocationNumVars()); - + + // Check the nullptr assignment hardening. + // First set the fields to non-empty values. + cs->setName("NAME"); + cs->setDescription("DESC"); + cs->setFamily("FAMILY"); + cs->setEqualityGroup("EQGRP"); + cs->setEncoding("ENC"); + cs->setInteropID("interop"); + OCIO_CHECK_NO_THROW(cs->setInterchangeAttribute("amf_transform_ids", "AMF")); + OCIO_CHECK_NO_THROW(cs->setInterchangeAttribute("icc_profile_name", "ICC")); + + // Set to nullptr, this should erase the old values. + OCIO_CHECK_NO_THROW(cs->setName(nullptr)); + OCIO_CHECK_NO_THROW(cs->setDescription(nullptr)); + OCIO_CHECK_NO_THROW(cs->setFamily(nullptr)); + OCIO_CHECK_NO_THROW(cs->setEqualityGroup(nullptr)); + OCIO_CHECK_NO_THROW(cs->setEncoding(nullptr)); + OCIO_CHECK_NO_THROW(cs->setInteropID(nullptr)); + OCIO_CHECK_NO_THROW(cs->setInterchangeAttribute("amf_transform_ids", nullptr)); + OCIO_CHECK_NO_THROW(cs->setInterchangeAttribute("icc_profile_name", nullptr)); + + // Check that the values are empty now. + OCIO_CHECK_ASSERT(!*cs->getName()); + OCIO_CHECK_ASSERT(!*cs->getDescription()); + OCIO_CHECK_ASSERT(!*cs->getFamily()); + OCIO_CHECK_ASSERT(!*cs->getEqualityGroup()); + OCIO_CHECK_ASSERT(!*cs->getEncoding()); + OCIO_CHECK_ASSERT(!*cs->getInteropID()); + OCIO_CHECK_ASSERT(!*cs->getInterchangeAttribute("amf_transform_ids")); + OCIO_CHECK_ASSERT(!*cs->getInterchangeAttribute("icc_profile_name")); + + // Test set/get roundtrip. cs->setName("name"); OCIO_CHECK_EQUAL(std::string("name"), cs->getName()); cs->setFamily("family"); @@ -63,10 +96,17 @@ OCIO_ADD_TEST(ColorSpace, basic) cs->getAllocationVars(readVars); OCIO_CHECK_EQUAL(1.f, readVars[0]); OCIO_CHECK_EQUAL(2.f, readVars[1]); + cs->setInteropID("interop_id"); + OCIO_CHECK_EQUAL(std::string("interop_id"), cs->getInteropID()); + cs->setInterchangeAttribute("amf_transform_ids", "amf_transform_id1\namf_transform_id2"); + OCIO_CHECK_EQUAL(std::string("amf_transform_id1\namf_transform_id2"), + cs->getInterchangeAttribute("amf_transform_ids")); + cs->setInterchangeAttribute("icc_profile_name","icc_profile_name"); + OCIO_CHECK_EQUAL(std::string("icc_profile_name"), cs->getInterchangeAttribute("icc_profile_name")); std::ostringstream oss; oss << *cs; - OCIO_CHECK_EQUAL(oss.str().size(), 193); + OCIO_CHECK_EQUAL(oss.str().size(), 306); } OCIO_ADD_TEST(ColorSpace, alias) @@ -256,7 +296,7 @@ active_views: [] OCIO_CHECK_EQUAL(cfgString, os.str()); } - // Adding a color space that uses all parameters. + // Adding a color space that uses all parameters (as of 2.0). { constexpr char End[]{ R"(colorspaces: - ! @@ -619,6 +659,141 @@ active_views: [] OCIO_CHECK_EQUAL(cfgRes, os.str()); } + + // Test that the interop_id is valid in v2.0 config too. + { + constexpr char End[]{R"(colorspaces: + - ! + name: raw + interop_id: data + family: raw + equalitygroup: "" + bitdepth: 32f + description: Some text. + isdata: true + allocation: uniform +)"}; + std::string cfgString{Start}; + cfgString += End; + + std::istringstream is; + is.str(cfgString); + OCIO::ConstConfigRcPtr config; + OCIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is)); + auto cs = config->getColorSpace("raw"); + OCIO_CHECK_ASSERT(cs); + OCIO_CHECK_EQUAL(std::string(cs->getInteropID()), "data"); + } + + // Test that the interchange is NOT valid in v2.0 config. + { + constexpr char End[]{R"(colorspaces: + - ! + name: raw + interchange: + amf_transform_ids: should NOT be valid in 2.0 config + family: raw + equalitygroup: "" + bitdepth: 32f + description: Some text. + isdata: true + allocation: uniform +)"}; + std::string cfgString{Start}; + cfgString += End; + + std::istringstream is; + is.str(cfgString); + OCIO::ConstConfigRcPtr config; + OCIO_CHECK_THROW_WHAT( + config = OCIO::Config::CreateFromStream(is), + OCIO::Exception, + "Config failed validation. The color space 'raw' has non-empty " + "interchange attributes and config version is less than 2.5."); + } + + // Interchange tests in 2.5 + constexpr char Start2_5[]{R"(ocio_profile_version: 2.5 + +environment: + {} +search_path: "" +strictparsing: false +luma: [0.2126, 0.7152, 0.0722] + +roles: + default: raw + +file_rules: + - ! {name: ColorSpaceNamePathSearch} + - ! {name: Default, colorspace: default} + +displays: + sRGB: + - ! {name: Raw, colorspace: raw} + +active_displays: [] +active_views: [] + +)"}; + + // Test that the interchange is valid in v2.5 config. + { + constexpr char End_amf[]{R"( +colorspaces: + - ! + name: raw + interchange: + amf_transform_ids: This is valid in 2.5 config + family: raw + equalitygroup: "" + bitdepth: 32f + description: Some text. + isdata: true + allocation: uniform +)"}; + + std::string cfgString{Start2_5}; + cfgString += End_amf; + + std::istringstream is; + is.str(cfgString); + OCIO::ConstConfigRcPtr config; + OCIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is)); + OCIO_REQUIRE_EQUAL(!config, false); + auto attrMap = config->getColorSpace("raw")->getInterchangeAttributes(); + OCIO_CHECK_EQUAL(attrMap.size(), 1); + } + + // Test that the unknown interchange attrib will be ignored in 2.5. + { + constexpr char End_unkown[]{R"( +colorspaces: + - ! + name: raw + interchange: + my-attrib: will be ignored + family: raw + equalitygroup: "" + bitdepth: 32f + description: Some text. + isdata: true + allocation: uniform +)"}; + + std::string cfgString{Start2_5}; + cfgString += End_unkown; + + std::istringstream is; + is.str(cfgString); + OCIO::ConstConfigRcPtr config; + OCIO::LogGuard logGuard; + OCIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is)); + OCIO_CHECK_EQUAL(logGuard.output(), "[OpenColorIO Warning]: Unknown key in interchange: 'my-attrib'.\n"); + OCIO_REQUIRE_EQUAL(!config, false); + auto attrMap = config->getColorSpace("raw")->getInterchangeAttributes(); + OCIO_CHECK_EQUAL(attrMap.size(), 0); + } } OCIO_ADD_TEST(Config, use_alias) @@ -734,6 +909,320 @@ active_views: [] "colorspace"); } +OCIO_ADD_TEST(ColorSpace, interop_id) +{ + OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create(); + + // Test default value. + OCIO_CHECK_EQUAL(std::string(cs->getInteropID()), ""); + + // Test setting and getting single profile name. + const char * interop_id = "srgb_p3d65_scene"; + cs->setInteropID(interop_id); + OCIO_CHECK_EQUAL(std::string(cs->getInteropID()), interop_id); + + // Test setting empty string. + cs->setInteropID(""); + OCIO_CHECK_EQUAL(std::string(cs->getInteropID()), ""); + + // Test setting and getting another value. + const char * anotherID= "lin_rec2020_scene"; + cs->setInteropID(anotherID); + OCIO_CHECK_EQUAL(std::string(cs->getInteropID()), anotherID); + + // Test setting null pointer (should be safe). + cs->setInteropID("something"); + OCIO_CHECK_NO_THROW(cs->setInteropID(nullptr)); + OCIO_CHECK_EQUAL(std::string(cs->getInteropID()), ""); + + // Test copy constructor preserves InteropID. + cs->setInteropID(interop_id); + OCIO::ColorSpaceRcPtr copy = cs->createEditableCopy(); + OCIO_CHECK_EQUAL(std::string(copy->getInteropID()), interop_id); + + // Test valid InteropID with colon in the middle. + const char * validColonMiddle = "namespace:colorspace_name"; + OCIO_CHECK_NO_THROW(cs->setInteropID(validColonMiddle)); + OCIO_CHECK_EQUAL(std::string(cs->getInteropID()), validColonMiddle); + + // Test invalid InteropID with multiple colons. + OCIO_CHECK_THROW_WHAT(cs->setInteropID("name:space:cs_name"), OCIO::Exception, + "Only one ':' is allowed to separate the namespace and the color space."); + + // Test invalid InteropID with colon at the end. + OCIO_CHECK_THROW_WHAT(cs->setInteropID("namespace:"), OCIO::Exception, + " If ':' is used, both the namespace and the color space parts must be non-empty."); + + // Test invalid InteropID with empty namespace or color space. + OCIO_CHECK_THROW_WHAT(cs->setInteropID(":cs_name"), OCIO::Exception, + "If ':' is used, both the namespace and the color space parts must be non-empty."); + + // Test invalid InteropID with illegal characters. + OCIO_CHECK_THROW_WHAT(cs->setInteropID("café_scene"), OCIO::Exception, + "Only lowercase a-z, 0-9 and . - _ ~ / * # % ^ + ( ) [ ] | are allowed."); + + OCIO_CHECK_THROW_WHAT(cs->setInteropID("UPPERCASE"), OCIO::Exception, + "Only lowercase a-z, 0-9 and . - _ ~ / * # % ^ + ( ) [ ] | are allowed."); + + OCIO_CHECK_THROW_WHAT(cs->setInteropID("{curly_bracket}"), OCIO::Exception, + "Only lowercase a-z, 0-9 and . - _ ~ / * # % ^ + ( ) [ ] | are allowed."); + + OCIO_CHECK_THROW_WHAT(cs->setInteropID("\\backslash"), OCIO::Exception, + "Only lowercase a-z, 0-9 and . - _ ~ / * # % ^ + ( ) [ ] | are allowed."); + + OCIO_CHECK_THROW_WHAT(cs->setInteropID(" space "), OCIO::Exception, + "Only lowercase a-z, 0-9 and . - _ ~ / * # % ^ + ( ) [ ] | are allowed."); +} + +OCIO_ADD_TEST(ColorSpace, interop_id_serialization) +{ + // Test YAML serialization and deserialization of InteropID. + auto cfg = OCIO::Config::Create(); + auto cs = OCIO::ColorSpace::Create(); + cs->setName("test_colorspace"); + + const std::string interop_id = "lin_rec709_scene"; + + cs->setInteropID(interop_id.c_str()); + cfg->addColorSpace(cs); + + // Serialize the Config. + std::stringstream ss; + cfg->serialize(ss); + std::string yamlStr = ss.str(); + + // Verify interop_id appears in YAML. + OCIO_CHECK_NE(yamlStr.find("interop_id"), std::string::npos); + OCIO_CHECK_NE(yamlStr.find(interop_id), std::string::npos); + + // Deserialize and verify. + std::istringstream iss(yamlStr); + OCIO::ConstConfigRcPtr deserializedCfg; + OCIO_CHECK_NO_THROW(deserializedCfg = OCIO::Config::CreateFromStream(iss)); + + // Verify interop_id is preserved. + OCIO::ConstColorSpaceRcPtr deserializedCs = deserializedCfg->getColorSpace("test_colorspace"); + OCIO_CHECK_EQUAL(std::string(deserializedCs->getInteropID()), interop_id); + + // verify that that versions earlier than 2.0 reject interop_id. + OCIO::ConfigRcPtr cfgCopy = cfg->createEditableCopy(); + cfgCopy->setVersion(2, 0); + OCIO_CHECK_NO_THROW(cfgCopy->serialize(ss)); + + cfgCopy->setVersion(1, 0); + OCIO_CHECK_THROW_WHAT( + cfgCopy->serialize(ss), + OCIO::Exception, + "Config failed validation. The color space 'test_colorspace' has non-empty " + "InteropID and config version is less than 2.0."); + + // Test with empty interop_id (should not appear in YAML). + cs->setInteropID(nullptr); + cfg->addColorSpace(cs); // Replace the existing CS. + ss.str(""); + cfg->serialize(ss); + std::string yamlStr2 = ss.str(); + + // Verify empty imterop_id does not appear in YAML. + OCIO_CHECK_EQUAL(yamlStr2.find("interop_id"), std::string::npos); +} + +OCIO_ADD_TEST(ColorSpace, amf_transform_ids) +{ + OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create(); + + // Test default value. + OCIO_CHECK_EQUAL(std::string(cs->getInterchangeAttribute("amf_transform_ids")), ""); + + // Test setting and getting single ID. + const char * singleID = "urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ACEScc_to_ACES.a1.0.3"; + cs->setInterchangeAttribute("amf_transform_ids", singleID); + OCIO_CHECK_EQUAL(std::string(cs->getInterchangeAttribute("amf_transform_ids")), singleID); + + // Test setting to empty string. + cs->setInterchangeAttribute("amf_transform_ids", ""); + OCIO_CHECK_EQUAL(std::string(cs->getInterchangeAttribute("amf_transform_ids")), ""); + + // Test setting and getting multiple IDs. + const char * multipleIDs = + "urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ACEScc_to_ACES.a1.0.3\n" + "urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ACES_to_ACEScc.a1.0.3"; + cs->setInterchangeAttribute("amf_transform_ids", multipleIDs); + OCIO_CHECK_EQUAL(std::string(cs->getInterchangeAttribute("amf_transform_ids")), multipleIDs); + + // Test setting to null pointer (should be safe). + cs->setInterchangeAttribute("amf_transform_ids", "something"); + cs->setInterchangeAttribute("amf_transform_ids", nullptr); + OCIO_CHECK_EQUAL(std::string(cs->getInterchangeAttribute("amf_transform_ids")), ""); + + // Test copy constructor preserves AMF transform IDs. + cs->setInterchangeAttribute("amf_transform_ids", singleID); + OCIO::ColorSpaceRcPtr copy = cs->createEditableCopy(); + OCIO_CHECK_EQUAL(std::string(copy->getInterchangeAttribute("amf_transform_ids")), singleID); +} + +OCIO_ADD_TEST(ColorSpace, amf_transform_ids_serialization) +{ + // Test YAML serialization and deserialization of AmfTransformIDs. + auto cfg = OCIO::Config::Create(); + auto cs = OCIO::ColorSpace::Create(); + cs->setName("test_colorspace"); + + const std::string amfIDs = + "urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ACEScc_to_ACES.a1.0.3\n" + "urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ACES_to_ACEScc.a1.0.3"; + + cs->setInterchangeAttribute("amf_transform_ids", amfIDs.c_str()); + cfg->addColorSpace(cs); + + // Serialize the Config. + std::stringstream ss; + cfg->serialize(ss); + std::string yamlStr = ss.str(); + + // Verify AmfTransformIDs appears in YAML. + OCIO_CHECK_NE(yamlStr.find("amf_transform_ids"), std::string::npos); + OCIO_CHECK_NE(yamlStr.find("ACEScsc.Academy.ACEScc_to_ACES"), std::string::npos); + + // Deserialize and verify. + std::istringstream iss(yamlStr); + OCIO::ConstConfigRcPtr deserializedCfg; + OCIO_CHECK_NO_THROW(deserializedCfg = OCIO::Config::CreateFromStream(iss)); + + // Verify AmfTransformIDs is preserved. + OCIO::ConstColorSpaceRcPtr deserializedCs = deserializedCfg->getColorSpace("test_colorspace"); + auto deserializedIDs = deserializedCs->getInterchangeAttribute("amf_transform_ids"); + OCIO_CHECK_EQUAL(deserializedIDs, amfIDs); + + // Verify that that earlier versions reject amf_transform_ids. + OCIO::ConfigRcPtr cfgCopy = cfg->createEditableCopy(); + cfgCopy->setVersion(2,4); + OCIO_CHECK_THROW_WHAT(cfgCopy->serialize(ss), + OCIO::Exception, + "has non-empty interchange attributes and config version is less than 2.5."); + + // Test with empty AmfTransformIDs (should not appear in YAML). + cs->setInterchangeAttribute("amf_transform_ids", nullptr); + cfg->addColorSpace(cs); // Replace the existing CS. + ss.str(""); + cfg->serialize(ss); + std::string yamlStr2 = ss.str(); + + // Verify empty AmfTransformIDs does not appear in YAML. + OCIO_CHECK_EQUAL(yamlStr2.find("amf_transform_ids"), std::string::npos); +} + +OCIO_ADD_TEST(ColorSpace, icc_profile_name) +{ + OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create(); + + // Test default value. + OCIO_CHECK_EQUAL(std::string(cs->getInterchangeAttribute("icc_profile_name")), ""); + + // Test setting and getting single profile name. + const char * profileName = "sRGB IEC61966-2.1"; + cs->setInterchangeAttribute("icc_profile_name",profileName); + OCIO_CHECK_EQUAL(std::string(cs->getInterchangeAttribute("icc_profile_name")), profileName); + + // Test setting and getting another profile name. + const char * anotherProfile = "Adobe RGB (1998)"; + cs->setInterchangeAttribute("icc_profile_name", anotherProfile); + OCIO_CHECK_EQUAL(std::string(cs->getInterchangeAttribute("icc_profile_name")), anotherProfile); + + // Test setting empty string. + cs->setInterchangeAttribute("icc_profile_name", ""); + OCIO_CHECK_EQUAL(std::string(cs->getInterchangeAttribute("icc_profile_name")), ""); + + // Test setting null pointer (should be safe). + cs->setInterchangeAttribute("icc_profile_name", "something"); + OCIO_CHECK_NO_THROW(cs->setInterchangeAttribute("icc_profile_name", nullptr)); + OCIO_CHECK_EQUAL(std::string(cs->getInterchangeAttribute("icc_profile_name")), ""); + + // Test copy constructor preserves ICC profile name. + cs->setInterchangeAttribute("icc_profile_name", profileName); + OCIO::ColorSpaceRcPtr copy = cs->createEditableCopy(); + OCIO_CHECK_EQUAL(std::string(copy->getInterchangeAttribute("icc_profile_name")), profileName); +} + +OCIO_ADD_TEST(ColorSpace, icc_profile_name_serialization) +{ + // Test YAML serialization and deserialization of IccProfileName. + auto cfg = OCIO::Config::Create(); + auto cs = OCIO::ColorSpace::Create(); + cs->setName("test_colorspace"); + + const std::string profileName = "sRGB IEC61966-2.1"; + + cs->setInterchangeAttribute("icc_profile_name", profileName.c_str()); + cfg->addColorSpace(cs); + + // Serialize the Config. + std::stringstream ss; + cfg->serialize(ss); + std::string yamlStr = ss.str(); + + // Verify IccProfileName appears in YAML. + OCIO_CHECK_NE(yamlStr.find("icc_profile_name"), std::string::npos); + OCIO_CHECK_NE(yamlStr.find(profileName), std::string::npos); + + // Deserialize and verify. + std::istringstream iss(yamlStr); + OCIO::ConstConfigRcPtr deserializedCfg; + OCIO_CHECK_NO_THROW(deserializedCfg = OCIO::Config::CreateFromStream(iss)); + + // Verify IccProfileName is preserved. + OCIO::ConstColorSpaceRcPtr deserializedCs = deserializedCfg->getColorSpace("test_colorspace"); + OCIO_CHECK_EQUAL(std::string(deserializedCs->getInterchangeAttribute("icc_profile_name")), profileName); + + // verify that that earlier versions reject icc_profile_name. + OCIO::ConfigRcPtr cfgCopy = cfg->createEditableCopy(); + cfgCopy->setVersion(2, 4); + OCIO_CHECK_THROW_WHAT(cfgCopy->serialize(ss), + OCIO::Exception, + "has non-empty interchange attributes and config version is less than 2.5."); + + // Test with empty IccProfileName (should not appear in YAML, and so won't invalidate a 2.4 config). + cs->setInterchangeAttribute("icc_profile_name", nullptr); + cfg->addColorSpace(cs); // replace the existing CS + ss.str(""); + cfg->serialize(ss); + std::string yamlStr2 = ss.str(); + + // Verify empty IccProfileName does not appear in YAML. + OCIO_CHECK_EQUAL(yamlStr2.find("icc_profile_name"), std::string::npos); +} + +OCIO_ADD_TEST(ColorSpace, unknown_interchange_attrib) +{ + OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create(); + + // Getting should throw. + OCIO_CHECK_THROW_WHAT(std::string(cs->getInterchangeAttribute("unknown_attrib")), + OCIO::Exception, + "Unknown attribute name"); + + // Empty name is not legal. + OCIO_CHECK_THROW_WHAT(std::string(cs->getInterchangeAttribute("")), OCIO::Exception, + "Unknown attribute name"); + OCIO_CHECK_THROW_WHAT(std::string(cs->getInterchangeAttribute(nullptr)), OCIO::Exception, + "Unknown attribute name"); + + // Setting should throw too. + OCIO_CHECK_THROW_WHAT(cs->setInterchangeAttribute("unknown_attribute1", "unknown"), + OCIO::Exception, + "Unknown attribute name"); + OCIO_CHECK_THROW_WHAT(cs->setInterchangeAttribute("unknown_attribute2", ""), + OCIO::Exception, + "Unknown attribute name"); + OCIO_CHECK_THROW_WHAT(cs->setInterchangeAttribute("unknown_attribute3", nullptr), + OCIO::Exception, + "Unknown attribute name"); + + // Make sure none of the above was stored. + auto attrMap = cs->getInterchangeAttributes(); + OCIO_CHECK_EQUAL(attrMap.size(), 0); +} + OCIO_ADD_TEST(Config, is_colorspace_linear) { @@ -1702,3 +2191,4 @@ inactive_colorspaces: [scene-linear Rec.709-sRGB, ACES2065-1] ); } } + diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index 6a310aae04..144d1a5816 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 4"); + "Maximum minor version is 5"); 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 4"); + "Maximum minor version is 5"); } { diff --git a/tests/cpu/fileformats/FileFormatCTF_tests.cpp b/tests/cpu/fileformats/FileFormatCTF_tests.cpp index e4906fd40b..b9b2662165 100644 --- a/tests/cpu/fileformats/FileFormatCTF_tests.cpp +++ b/tests/cpu/fileformats/FileFormatCTF_tests.cpp @@ -5124,7 +5124,7 @@ OCIO_ADD_TEST(CTFTransform, save_lut1d_halfdomain) } else { - OCIO_CHECK_EQUAL(loaded, expected); + OCIO_CHECK_EQUAL(loaded.bits(), expected.bits()); OCIO_CHECK_EQUAL(loadedVal, lut->getArray()[3 * i + 1]); OCIO_CHECK_EQUAL(loadedVal, lut->getArray()[3 * i + 2]); } diff --git a/tests/cpu/ops/lut1d/Lut1DOpCPU_tests.cpp b/tests/cpu/ops/lut1d/Lut1DOpCPU_tests.cpp index 94b45fb4c8..f4fa4aa9c9 100644 --- a/tests/cpu/ops/lut1d/Lut1DOpCPU_tests.cpp +++ b/tests/cpu/ops/lut1d/Lut1DOpCPU_tests.cpp @@ -643,25 +643,25 @@ OCIO_ADD_TEST(Lut1DRenderer, bit_depth_support) cpuOp->apply(&uint8_inImg[0], &outImg[0], NB_PIXELS); - OCIO_CHECK_EQUAL(outImg[ 0], 0.0f); - OCIO_CHECK_EQUAL(outImg[ 1], 0.0f); - OCIO_CHECK_EQUAL(outImg[ 2], 0.0f); - OCIO_CHECK_EQUAL(outImg[ 3], 0.0f); - - OCIO_CHECK_CLOSE(outImg[ 4], 0.066650390625f, 1e-6f); - OCIO_CHECK_CLOSE(outImg[ 5], 0.070617675781f, 1e-6f); - OCIO_CHECK_CLOSE(outImg[ 6], 0.070617675781f, 1e-6f); - OCIO_CHECK_EQUAL(outImg[ 7], 1.0f); - - OCIO_CHECK_CLOSE(outImg[ 8], 0.7138671875f, 1e-6f); - OCIO_CHECK_CLOSE(outImg[ 9], 0.7255859375f, 1e-6f); - OCIO_CHECK_CLOSE(outImg[10], 0.7373046875f, 1e-6f); - OCIO_CHECK_EQUAL(outImg[11], 0.0f); - - OCIO_CHECK_EQUAL(outImg[12], 1.0f); - OCIO_CHECK_EQUAL(outImg[13], 1.0f); - OCIO_CHECK_EQUAL(outImg[14], 1.0f); - OCIO_CHECK_EQUAL(outImg[15], 1.0f); + OCIO_CHECK_EQUAL((float)outImg[ 0], 0.0f); + OCIO_CHECK_EQUAL((float)outImg[ 1], 0.0f); + OCIO_CHECK_EQUAL((float)outImg[ 2], 0.0f); + OCIO_CHECK_EQUAL((float)outImg[ 3], 0.0f); + + OCIO_CHECK_CLOSE((float)outImg[ 4], 0.066650390625f, 1e-6f); + OCIO_CHECK_CLOSE((float)outImg[ 5], 0.070617675781f, 1e-6f); + OCIO_CHECK_CLOSE((float)outImg[ 6], 0.070617675781f, 1e-6f); + OCIO_CHECK_EQUAL((float)outImg[ 7], 1.0f); + + OCIO_CHECK_CLOSE((float)outImg[ 8], 0.7138671875f, 1e-6f); + OCIO_CHECK_CLOSE((float)outImg[ 9], 0.7255859375f, 1e-6f); + OCIO_CHECK_CLOSE((float)outImg[10], 0.7373046875f, 1e-6f); + OCIO_CHECK_EQUAL((float)outImg[11], 0.0f); + + OCIO_CHECK_EQUAL((float)outImg[12], 1.0f); + OCIO_CHECK_EQUAL((float)outImg[13], 1.0f); + OCIO_CHECK_EQUAL((float)outImg[14], 1.0f); + OCIO_CHECK_EQUAL((float)outImg[15], 1.0f); } // Processing from UINT8 to F32. @@ -878,15 +878,15 @@ OCIO_ADD_TEST(Lut1DRenderer, half) std::vector outImg(2 * 4, -1.f); cpuOp->apply(&inImg[0], &outImg[0], 2); - OCIO_CHECK_EQUAL(outImg[0], inImg[0]); - OCIO_CHECK_EQUAL(outImg[1], inImg[1]); - OCIO_CHECK_EQUAL(outImg[2], inImg[2]); - OCIO_CHECK_EQUAL(outImg[3], inImg[3]); + OCIO_CHECK_EQUAL(outImg[0], (float)inImg[0]); + OCIO_CHECK_EQUAL(outImg[1], (float)inImg[1]); + OCIO_CHECK_EQUAL(outImg[2], (float)inImg[2]); + OCIO_CHECK_EQUAL(outImg[3], (float)inImg[3]); - OCIO_CHECK_EQUAL(outImg[4], inImg[4]); - OCIO_CHECK_EQUAL(outImg[5], inImg[5]); + OCIO_CHECK_EQUAL(outImg[4], (float)inImg[4]); + OCIO_CHECK_EQUAL(outImg[5], (float)inImg[5]); OCIO_CHECK_CLOSE(outImg[6], arbitraryVal, 1e-5f); - OCIO_CHECK_EQUAL(outImg[7], inImg[7]); + OCIO_CHECK_EQUAL(outImg[7], (float)inImg[7]); } OCIO_ADD_TEST(Lut1DRenderer, nan) @@ -1594,9 +1594,9 @@ OCIO_ADD_TEST(Lut1DRenderer, lut_1d_identity_half) } else { - OCIO_CHECK_EQUAL(outImg[imgCntr + 0], hVal); - OCIO_CHECK_EQUAL(outImg[imgCntr + 1], hVal); - OCIO_CHECK_EQUAL(outImg[imgCntr + 2], hVal); + OCIO_CHECK_EQUAL(outImg[imgCntr + 0].bits(), hVal.bits()); + OCIO_CHECK_EQUAL(outImg[imgCntr + 1].bits(), hVal.bits()); + OCIO_CHECK_EQUAL(outImg[imgCntr + 2].bits(), hVal.bits()); OCIO_CHECK_EQUAL((float)outImg[imgCntr + 3], 1.f); } } 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 } } }, diff --git a/tests/python/ColorSpaceTest.py b/tests/python/ColorSpaceTest.py index 0860e7a3f0..7ad1edde9c 100644 --- a/tests/python/ColorSpaceTest.py +++ b/tests/python/ColorSpaceTest.py @@ -42,6 +42,9 @@ def test_copy(self): self.colorspace.setTransform(direction=OCIO.COLORSPACE_DIR_FROM_REFERENCE, transform=mat) self.colorspace.addAlias('alias') self.colorspace.addCategory('cat') + self.colorspace.setInteropID('data') + self.colorspace.setInterchangeAttribute('amf_transform_ids', 'urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.CG_to_ACES.a1.0.3') + self.colorspace.setInterchangeAttribute('icc_profile_name', 'sRGB IEC61966-2.1') other = copy.deepcopy(self.colorspace) self.assertFalse(other is self.colorspace) @@ -59,6 +62,9 @@ def test_copy(self): self.assertTrue(other.getTransform(OCIO.COLORSPACE_DIR_FROM_REFERENCE).equals(self.colorspace.getTransform(OCIO.COLORSPACE_DIR_FROM_REFERENCE))) self.assertEqual(list(other.getAliases()), list(self.colorspace.getAliases())) self.assertEqual(list(other.getCategories()), list(self.colorspace.getCategories())) + self.assertEqual(other.getInteropID(), self.colorspace.getInteropID()) + self.assertEqual(other.getInterchangeAttribute('amf_transform_ids'), self.colorspace.getInterchangeAttribute('amf_transform_ids')) + self.assertEqual(other.getInterchangeAttribute('icc_profile_name'), self.colorspace.getInterchangeAttribute('icc_profile_name')) def test_allocation(self): """ @@ -279,6 +285,9 @@ def test_constructor_without_parameter(self): self.assertFalse(cs.isData()) self.assertEqual(cs.getAllocation(), OCIO.ALLOCATION_UNIFORM) self.assertEqual(cs.getAllocationVars(), []) + self.assertEqual(cs.getInteropID(), '') + self.assertEqual(cs.getInterchangeAttribute("amf_transform_ids"), '') + self.assertEqual(cs.getInterchangeAttribute("icc_profile_name"), '') def test_data(self): """ @@ -421,6 +430,171 @@ def test_aliases(self): self.assertEqual(len(aliases), 0) self.assertFalse(cs.hasAlias('alias1')) + def test_interop_id(self): + """ + Test the setInteropID() and getInteropID() methods. + """ + + # Test default value (should be empty). + self.assertEqual(self.colorspace.getInteropID(), '') + + # Test setting and getting a simple interop ID. + test_id = 'lin_ap0_scene' + self.colorspace.setInteropID(test_id) + self.assertEqual(self.colorspace.getInteropID(), test_id) + + # Test setting and getting a different interop ID. + test_id2 = 'srgb_ap1_scene' + self.colorspace.setInteropID(test_id2) + self.assertEqual(self.colorspace.getInteropID(), test_id2) + + # Test setting empty string. + self.colorspace.setInteropID('') + self.assertEqual(self.colorspace.getInteropID(), '') + + # Test setting None (should convert to empty string). + self.colorspace.setInteropID('something') + self.colorspace.setInteropID(None) + self.assertEqual(self.colorspace.getInteropID(), '') + + # Test wrong type (should raise TypeError). + with self.assertRaises(TypeError): + self.colorspace.setInteropID(123) + + with self.assertRaises(TypeError): + self.colorspace.setInteropID(['list']) + + # Test valid InteropID with one colon (not at the end). + valid_with_colon = 'namespace:cs_name' + self.colorspace.setInteropID(valid_with_colon) + self.assertEqual(self.colorspace.getInteropID(), valid_with_colon) + + # Test invalid InteropID with multiple colons. + with self.assertRaises(Exception) as context: + self.colorspace.setInteropID('name:space:cs_name') + self.assertIn("Only one ':' is allowed to separate the namespace and the color space.", + str(context.exception)) + + # Test invalid InteropID with colon at the end. + with self.assertRaises(Exception) as context: + self.colorspace.setInteropID('namespace:') + self.assertIn("If ':' is used, both the namespace and the color space parts must be non-empty.", + str(context.exception)) + + # Test invalid InteropID with non-ASCII characters. + with self.assertRaises(Exception) as context: + self.colorspace.setInteropID('café_scene') # Contains é (UTF-8) + self.assertIn("contains invalid characters.", str(context.exception)) + + with self.assertRaises(Exception) as context: + self.colorspace.setInteropID('space±_name') # Contains ± (ANSI 0xB1) + self.assertIn("contains invalid characters.", str(context.exception)) + + def test_interchange_atttributes(self): + """ + Test the setInterchangeAttribute() and getInterchangeAttribute() methods. + """ + # amf_transform_ids + + # Test default value (should be empty). + self.assertEqual(self.colorspace.getInterchangeAttribute('amf_transform_ids'), '') + + # Test setting and getting a single amf transform ID. + single_id = 'urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.CG_to_ACES.a1.0.3' + self.colorspace.setInterchangeAttribute('amf_transform_ids', single_id) + self.assertEqual(self.colorspace.getInterchangeAttribute('amf_transform_ids'), single_id) + + # Test setting and getting multiple transform IDs (newline-separated). + multiple_ids = ('urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.CG_to_ACES.a1.0.3\n' + 'urn:ampas:aces:transformId:v1.5:ACEScsc.Academy.ACES_to_CG.a1.0.3\n' + 'urn:ampas:aces:transformId:v1.5:RRT.a1.0.3') + self.colorspace.setInterchangeAttribute('amf_transform_ids', multiple_ids) + self.assertEqual(self.colorspace.getInterchangeAttribute('amf_transform_ids'), multiple_ids) + + # Test setting empty string. + self.colorspace.setInterchangeAttribute('amf_transform_ids', '') + self.assertEqual(self.colorspace.getInterchangeAttribute('amf_transform_ids'), '') + + # Test setting None (should convert to empty string). + self.colorspace.setInterchangeAttribute('amf_transform_ids', 'something') + self.colorspace.setInterchangeAttribute('amf_transform_ids', None) + self.assertEqual(self.colorspace.getInterchangeAttribute('amf_transform_ids'), '') + + # Test with different line endings. + mixed_endings = 'id1\nid2\rid3\r\nid4' + self.colorspace.setInterchangeAttribute('amf_transform_ids', mixed_endings) + self.assertEqual(self.colorspace.getInterchangeAttribute('amf_transform_ids'), mixed_endings) + + # Test with leading/trailing whitespace. + whitespace_ids = ' \n id1 \n id2 \n ' + self.colorspace.setInterchangeAttribute('amf_transform_ids', whitespace_ids) + self.assertEqual(self.colorspace.getInterchangeAttribute('amf_transform_ids'), whitespace_ids) + + # Test wrong type (should raise TypeError). + with self.assertRaises(TypeError): + self.colorspace.setInterchangeAttribute('amf_transform_ids', 123) + + with self.assertRaises(TypeError): + self.colorspace.setInterchangeAttribute('amf_transform_ids', ['list', 'of', 'ids']) + + # icc_profile_name + + # Test default value (should be empty). + self.assertEqual(self.colorspace.getInterchangeAttribute('icc_profile_name'), '') + + # Test setting and getting a simple profile name. + profile_name = 'sRGB IEC61966-2.1' + self.colorspace.setInterchangeAttribute('icc_profile_name', profile_name) + self.assertEqual(self.colorspace.getInterchangeAttribute('icc_profile_name'), profile_name) + + # Test setting and getting a different profile name. + profile_name2 = 'Adobe RGB (1998)' + self.colorspace.setInterchangeAttribute('icc_profile_name', profile_name2) + self.assertEqual(self.colorspace.getInterchangeAttribute('icc_profile_name'), profile_name2) + + # Test with a more complex profile name. + complex_name = 'Display P3 - Apple Cinema Display (Calibrated 2023-01-15)' + self.colorspace.setInterchangeAttribute('icc_profile_name', complex_name) + self.assertEqual(self.colorspace.getInterchangeAttribute('icc_profile_name'), complex_name) + + # Test setting empty string. + self.colorspace.setInterchangeAttribute('icc_profile_name', '') + self.assertEqual(self.colorspace.getInterchangeAttribute('icc_profile_name'), '') + + # Test setting None (should convert to empty string). + self.colorspace.setInterchangeAttribute('icc_profile_name', 'something') + self.colorspace.setInterchangeAttribute('icc_profile_name', None) + self.assertEqual(self.colorspace.getInterchangeAttribute('icc_profile_name'), '') + + # Test with special characters and numbers. + special_name = 'ProPhoto RGB v2.0 (γ=1.8) [Custom Profile #123]' + self.colorspace.setInterchangeAttribute('icc_profile_name', special_name) + self.assertEqual(self.colorspace.getInterchangeAttribute('icc_profile_name'), special_name) + + # Test with Unicode characters. + unicode_name = 'Профиль RGB γ=2.2' + self.colorspace.setInterchangeAttribute('icc_profile_name', unicode_name) + self.assertEqual(self.colorspace.getInterchangeAttribute('icc_profile_name'), unicode_name) + + # Test wrong type (should raise TypeError). + with self.assertRaises(TypeError): + self.colorspace.setInterchangeAttribute('icc_profile_name', 123) + + with self.assertRaises(TypeError): + self.colorspace.setInterchangeAttribute('icc_profile_name', ['profile', 'name']) + + # unsupported interchange key + + # test that setting an unsupported key raises. + with self.assertRaises(Exception) as context: + self.colorspace.setInterchangeAttribute('this_should_fail', 'foo42') + self.assertIn("Unknown attribute name 'this_should_fail'", str(context.exception)) + + # test that getting an unsupported key raises. + with self.assertRaises(Exception) as context: + self.colorspace.getInterchangeAttribute('this_should_fail') + self.assertIn("Unknown attribute name 'this_should_fail'", str(context.exception)) + def test_is_colorspace_linear(self): """ Test isColorSpaceLinear. @@ -634,7 +808,7 @@ def test_display_referred(self, cfg, cs_name, expected_value): def test_processor_to_known_colorspace(self): - CONFIG = """ocio_profile_version: 2 + CONFIG = """ocio_profile_version: 2.0 roles: default: raw @@ -690,12 +864,14 @@ def test_processor_to_known_colorspace(self): - ! name: ACES cg description: An ACEScg space with an unusual spelling. + interop_id: lin_ap1_scene isdata: false to_scene_reference: ! {style: ACEScg_to_ACES2065-1} - ! name: Linear ITU-R BT.709 description: A linear Rec.709 space with an unusual spelling. + interop_id: "mycompany:led_wall_1" isdata: false from_scene_reference: ! name: AP0 to Linear Rec.709 (sRGB)