From 66e5d9e2915733247bca47d077414ec2594aedad Mon Sep 17 00:00:00 2001 From: "Matteo F. Vescovi" Date: Tue, 20 Aug 2013 09:53:19 +0100 Subject: opencolorio (1.0.8~dfsg0-2) unstable; urgency=low * debian/rules: get-orig-source stuff added * debian/rules: useless dh addon removed * debian/rules: License.txt duplicate removed * debian/rules: SSE optimization disabled (Closes: #719174) * debian/libopencolorio1.symbols: file removed (Closes: #719175) # imported from the archive --- src/core/Config.cpp | 2223 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2223 insertions(+) create mode 100644 src/core/Config.cpp (limited to 'src/core/Config.cpp') diff --git a/src/core/Config.cpp b/src/core/Config.cpp new file mode 100644 index 0000000..985990f --- /dev/null +++ b/src/core/Config.cpp @@ -0,0 +1,2223 @@ +/* +Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al. +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of Sony Pictures Imageworks nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "HashUtils.h" +#include "Logging.h" +#include "LookParse.h" +#include "MathUtils.h" +#include "Mutex.h" +#include "OpBuilders.h" +#include "PathUtils.h" +#include "ParseUtils.h" +#include "Processor.h" +#include "PrivateTypes.h" +#include "pystring/pystring.h" +#include "OCIOYaml.h" + +OCIO_NAMESPACE_ENTER +{ + namespace + { + const char * OCIO_CONFIG_ENVVAR = "OCIO"; + const char * OCIO_ACTIVE_DISPLAYS_ENVVAR = "OCIO_ACTIVE_DISPLAYS"; + const char * OCIO_ACTIVE_VIEWS_ENVVAR = "OCIO_ACTIVE_VIEWS"; + + enum Sanity + { + SANITY_UNKNOWN = 0, + SANITY_SANE, + SANITY_INSANE + }; + + // These are the 709 primaries specified by the ASC. + const float DEFAULT_LUMA_COEFF_R = 0.2126f; + const float DEFAULT_LUMA_COEFF_G = 0.7152f; + const float DEFAULT_LUMA_COEFF_B = 0.0722f; + + const char * INTERNAL_RAW_PROFILE = + "ocio_profile_version: 1\n" + "strictparsing: false\n" + "roles:\n" + " default: raw\n" + "displays:\n" + " sRGB:\n" + " - ! {name: Raw, colorspace: raw}\n" + "colorspaces:\n" + " - !\n" + " name: raw\n" + " family: raw\n" + " equalitygroup:\n" + " bitdepth: 32f\n" + " isdata: true\n" + " allocation: uniform\n" + " description: 'A raw color space. Conversions to and from this space are no-ops.'\n"; + } + + + /////////////////////////////////////////////////////////////////////////// + + const char * GetVersion() + { + return OCIO_VERSION; + } + + int GetVersionHex() + { + return OCIO_VERSION_HEX; + } + + namespace + { + ConstConfigRcPtr g_currentConfig; + Mutex g_currentConfigLock; + } + + ConstConfigRcPtr GetCurrentConfig() + { + AutoMutex lock(g_currentConfigLock); + + if(!g_currentConfig) + { + g_currentConfig = Config::CreateFromEnv(); + } + + return g_currentConfig; + } + + void SetCurrentConfig(const ConstConfigRcPtr & config) + { + AutoMutex lock(g_currentConfigLock); + + g_currentConfig = config->createEditableCopy(); + } + + namespace + { + + // Roles + // (lower case role name: colorspace name) + std::string LookupRole(const StringMap & roles, const std::string & rolename) + { + StringMap::const_iterator iter = roles.find(pystring::lower(rolename)); + if(iter == roles.end()) return ""; + return iter->second; + } + + + void GetFileReferences(std::set & files, + const ConstTransformRcPtr & transform) + { + if(!transform) return; + + if(ConstGroupTransformRcPtr groupTransform = \ + DynamicPtrCast(transform)) + { + for(int i=0; isize(); ++i) + { + GetFileReferences(files, groupTransform->getTransform(i)); + } + } + else if(ConstFileTransformRcPtr fileTransform = \ + DynamicPtrCast(transform)) + { + files.insert(fileTransform->getSrc()); + } + } + + void GetColorSpaceReferences(std::set & colorSpaceNames, + const ConstTransformRcPtr & transform) + { + if(!transform) return; + + if(ConstGroupTransformRcPtr groupTransform = \ + DynamicPtrCast(transform)) + { + for(int i=0; isize(); ++i) + { + GetColorSpaceReferences(colorSpaceNames, groupTransform->getTransform(i)); + } + } + else if(ConstColorSpaceTransformRcPtr colorSpaceTransform = \ + DynamicPtrCast(transform)) + { + colorSpaceNames.insert(colorSpaceTransform->getSrc()); + colorSpaceNames.insert(colorSpaceTransform->getDst()); + } + else if(ConstDisplayTransformRcPtr displayTransform = \ + DynamicPtrCast(transform)) + { + colorSpaceNames.insert(displayTransform->getInputColorSpaceName()); + } + else if(ConstLookTransformRcPtr lookTransform = \ + DynamicPtrCast(transform)) + { + colorSpaceNames.insert(colorSpaceTransform->getSrc()); + colorSpaceNames.insert(colorSpaceTransform->getDst()); + } + } + + + bool FindColorSpaceIndex(int * index, + const ColorSpaceVec & colorspaces, + const std::string & csname) + { + if(csname.empty()) return false; + + std::string csnamelower = pystring::lower(csname); + + for(unsigned int i = 0; i < colorspaces.size(); ++i) + { + if(csnamelower == pystring::lower(colorspaces[i]->getName())) + { + if(index) *index = i; + return true; + } + } + + return false; + } + + + // Displays + struct View + { + std::string name; + std::string colorspace; + std::string looks; + + View() { } + + View(const std::string & name_, + const std::string & colorspace_, + const std::string & looksList_) : + name(name_), + colorspace(colorspace_), + looks(looksList_) + { } + }; + + typedef std::vector ViewVec; + typedef std::map DisplayMap; // (display name : ViewVec) + + void operator >> (const YAML::Node& node, View& v) + { + if(node.Tag() != "View") + return; + + std::string key, stringval; + + for (YAML::Iterator iter = node.begin(); + iter != node.end(); + ++iter) + { + iter.first() >> key; + + if(key == "name") + { + if (iter.second().Type() != YAML::NodeType::Null && + iter.second().Read(stringval)) + v.name = stringval; + } + else if(key == "colorspace") + { + if (iter.second().Type() != YAML::NodeType::Null && + iter.second().Read(stringval)) + v.colorspace = stringval; + } + else if(key == "looks" || key == "look") + { + if (iter.second().Type() != YAML::NodeType::Null && + iter.second().Read(stringval)) + v.looks = stringval; + } + else + { + LogUnknownKeyWarning(node.Tag(), iter.first()); + } + } + + if(v.name.empty()) + { + throw Exception("View does not specify 'name'."); + } + if(v.colorspace.empty()) + { + std::ostringstream os; + os << "View '" << v.name << "' "; + os << "does not specify colorspace."; + throw Exception(os.str().c_str()); + } + } + + YAML::Emitter& operator << (YAML::Emitter& out, View view) + { + out << YAML::VerbatimTag("View"); + out << YAML::Flow; + out << YAML::BeginMap; + out << YAML::Key << "name" << YAML::Value << view.name; + out << YAML::Key << "colorspace" << YAML::Value << view.colorspace; + if(!view.looks.empty()) out << YAML::Key << "looks" << YAML::Value << view.looks; + out << YAML::EndMap; + return out; + } + + DisplayMap::iterator find_display(DisplayMap & displays, const std::string & display) + { + for(DisplayMap::iterator iter = displays.begin(); + iter != displays.end(); + ++iter) + { + if(StrEqualsCaseIgnore(display, iter->first)) return iter; + } + return displays.end(); + } + + DisplayMap::const_iterator find_display_const(const DisplayMap & displays, const std::string & display) + { + for(DisplayMap::const_iterator iter = displays.begin(); + iter != displays.end(); + ++iter) + { + if(StrEqualsCaseIgnore(display, iter->first)) return iter; + } + return displays.end(); + } + + int find_view(const ViewVec & vec, const std::string & name) + { + for(unsigned int i=0; isecond; + int index = find_view(views, view); + if(index<0) + { + views.push_back( View(view, colorspace, looks) ); + } + else + { + views[index].colorspace = colorspace; + views[index].looks = looks; + } + } + } + + void ComputeDisplays(StringVec & displayCache, + const DisplayMap & displays, + const StringVec & activeDisplays, + const StringVec & activeDisplaysEnvOverride) + { + displayCache.clear(); + + StringVec displayMasterList; + for(DisplayMap::const_iterator iter = displays.begin(); + iter != displays.end(); + ++iter) + { + displayMasterList.push_back(iter->first); + } + + // Apply the env override if it's not empty. + if(!activeDisplaysEnvOverride.empty()) + { + displayCache = IntersectStringVecsCaseIgnore(displayMasterList, activeDisplaysEnvOverride); + if(!displayCache.empty()) return; + } + // Otherwise, aApply the active displays if it's not empty. + else if(!activeDisplays.empty()) + { + displayCache = IntersectStringVecsCaseIgnore(displayMasterList, activeDisplays); + if(!displayCache.empty()) return; + } + + displayCache = displayMasterList; + } + + + + } // namespace + + class Config::Impl + { + public: + ContextRcPtr context_; + std::string description_; + ColorSpaceVec colorspaces_; + StringMap roles_; + LookVec looksList_; + + DisplayMap displays_; + StringVec activeDisplays_; + StringVec activeDisplaysEnvOverride_; + StringVec activeViews_; + StringVec activeViewsEnvOverride_; + + mutable std::string activeDisplaysStr_; + mutable std::string activeViewsStr_; + mutable StringVec displayCache_; + + // Misc + std::vector defaultLumaCoefs_; + bool strictParsing_; + + mutable Sanity sanity_; + mutable std::string sanitytext_; + + mutable Mutex cacheidMutex_; + mutable StringMap cacheids_; + mutable std::string cacheidnocontext_; + + Impl() : + context_(Context::Create()), + strictParsing_(true), + sanity_(SANITY_UNKNOWN) + { + context_->loadEnvironment(); + + char* activeDisplays = std::getenv(OCIO_ACTIVE_DISPLAYS_ENVVAR); + SplitStringEnvStyle(activeDisplaysEnvOverride_, activeDisplays); + + char * activeViews = std::getenv(OCIO_ACTIVE_VIEWS_ENVVAR); + SplitStringEnvStyle(activeViewsEnvOverride_, activeViews); + + defaultLumaCoefs_.resize(3); + defaultLumaCoefs_[0] = DEFAULT_LUMA_COEFF_R; + defaultLumaCoefs_[1] = DEFAULT_LUMA_COEFF_G; + defaultLumaCoefs_[2] = DEFAULT_LUMA_COEFF_B; + } + + ~Impl() + { + + } + + Impl& operator= (const Impl & rhs) + { + context_ = rhs.context_->createEditableCopy(); + description_ = rhs.description_; + + // Deep copy the colorspaces + colorspaces_.clear(); + colorspaces_.reserve(rhs.colorspaces_.size()); + for(unsigned int i=0; icreateEditableCopy()); + } + + // Deep copy the looks + looksList_.clear(); + looksList_.reserve(rhs.looksList_.size()); + for(unsigned int i=0; icreateEditableCopy()); + } + + // Assignment operator will suffice for these + roles_ = rhs.roles_; + + displays_ = rhs.displays_; + activeDisplays_ = rhs.activeDisplays_; + activeViews_ = rhs.activeViews_; + activeViewsEnvOverride_ = rhs.activeViewsEnvOverride_; + activeDisplaysEnvOverride_ = rhs.activeDisplaysEnvOverride_; + activeDisplaysStr_ = rhs.activeDisplaysStr_; + displayCache_ = rhs.displayCache_; + + defaultLumaCoefs_ = rhs.defaultLumaCoefs_; + strictParsing_ = rhs.strictParsing_; + + sanity_ = rhs.sanity_; + sanitytext_ = rhs.sanitytext_; + + cacheids_ = rhs.cacheids_; + cacheidnocontext_ = cacheidnocontext_; + return *this; + } + + void load(std::istream & istream, const char * name); + + // Any time you modify the state of the config, you must call this + // to reset internal cache states. You also should do this in a + // thread safe manner by acquiring the cacheidMutex_; + void resetCacheIDs(); + + // Get all internal transforms (to generate cacheIDs, validation, etc). + // This currently crawls colorspaces + looks + void getAllIntenalTransforms(ConstTransformVec & transformVec) const; + }; + + + /////////////////////////////////////////////////////////////////////////// + + ConfigRcPtr Config::Create() + { + return ConfigRcPtr(new Config(), &deleter); + } + + void Config::deleter(Config* c) + { + delete c; + } + + ConstConfigRcPtr Config::CreateFromEnv() + { + char* file = std::getenv(OCIO_CONFIG_ENVVAR); + if(file) return CreateFromFile(file); + + std::ostringstream os; + os << "Color management disabled. "; + os << "(Specify the $OCIO environment variable to enable.)"; + LogInfo(os.str()); + + std::istringstream istream; + istream.str(INTERNAL_RAW_PROFILE); + + ConfigRcPtr config = Config::Create(); + config->getImpl()->load(istream, ""); + return config; + } + + ConstConfigRcPtr Config::CreateFromFile(const char * filename) + { + std::ifstream istream(filename); + if(istream.fail()) { + std::ostringstream os; + os << "Error could not read '" << filename; + os << "' OCIO profile."; + throw Exception (os.str().c_str()); + } + + ConfigRcPtr config = Config::Create(); + config->getImpl()->load(istream, filename); + return config; + } + + ConstConfigRcPtr Config::CreateFromStream(std::istream & istream) + { + ConfigRcPtr config = Config::Create(); + config->getImpl()->load(istream, ""); + return config; + } + + /////////////////////////////////////////////////////////////////////////// + + + + Config::Config() + : m_impl(new Config::Impl) + { + } + + Config::~Config() + { + delete m_impl; + m_impl = NULL; + } + + ConfigRcPtr Config::createEditableCopy() const + { + ConfigRcPtr config = Config::Create(); + *config->m_impl = *m_impl; + return config; + } + + void Config::sanityCheck() const + { + if(getImpl()->sanity_ == SANITY_SANE) return; + if(getImpl()->sanity_ == SANITY_INSANE) + { + throw Exception(getImpl()->sanitytext_.c_str()); + } + + getImpl()->sanity_ = SANITY_INSANE; + getImpl()->sanitytext_ = ""; + + + ///// COLORSPACES + StringSet existingColorSpaces; + + // Confirm all ColorSpaces are valid + for(unsigned int i=0; icolorspaces_.size(); ++i) + { + if(!getImpl()->colorspaces_[i]) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "The colorspace at index " << i << " is null."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + + const char * name = getImpl()->colorspaces_[i]->getName(); + if(!name || strlen(name) == 0) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "The colorspace at index " << i << " is not named."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + + std::string namelower = pystring::lower(name); + StringSet::const_iterator it = existingColorSpaces.find(namelower); + if(it != existingColorSpaces.end()) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "Two colorspaces are defined with the same name, '"; + os << namelower << "'."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + + existingColorSpaces.insert(namelower); + } + + // Confirm all roles are valid + { + for(StringMap::const_iterator iter = getImpl()->roles_.begin(), + end = getImpl()->roles_.end(); iter!=end; ++iter) + { + int csindex = -1; + if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, iter->second)) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "The role '" << iter->first << "' "; + os << "refers to a colorspace, '" << iter->second << "', "; + os << "which is not defined."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + + // Confirm no name conflicts between colorspaces and roles + if(FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, iter->first)) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "The role '" << iter->first << "' "; + os << " is in conflict with a colorspace of the same name."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + } + } + + ///// DISPLAYS + + int numviews = 0; + + // Confirm all Displays transforms refer to colorspaces that exit + for(DisplayMap::const_iterator iter = getImpl()->displays_.begin(); + iter != getImpl()->displays_.end(); + ++iter) + { + std::string display = iter->first; + const ViewVec & views = iter->second; + if(views.empty()) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "The display '" << display << "' "; + os << "does not define any views."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + + for(unsigned int i=0; isanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + + int csindex = -1; + if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, views[i].colorspace)) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "The display '" << display << "' "; + os << "refers to a colorspace, '" << views[i].colorspace << "', "; + os << "which is not defined."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + + // Confirm looks references exist + LookParseResult looks; + const LookParseResult::Options & options = looks.parse(views[i].looks); + + for(unsigned int optionindex=0; + optionindexsanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + } + } + + ++numviews; + } + } + + // Confirm at least one display entry exists. + if(numviews == 0) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "No displays are specified."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + + // Confirm for all Transforms that reference internal colorspaces, + // the named space exists + { + ConstTransformVec allTransforms; + getImpl()->getAllIntenalTransforms(allTransforms); + + std::set colorSpaceNames; + for(unsigned int i=0; i::iterator iter = colorSpaceNames.begin(); + iter != colorSpaceNames.end(); ++iter) + { + int csindex = -1; + if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, *iter)) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "This config references a ColorSpace, '" << *iter << "', "; + os << "which is not defined."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + } + } + + ///// LOOKS + + // For all looks, confirm the process space exists and the look is named + for(unsigned int i=0; ilooksList_.size(); ++i) + { + std::string name = getImpl()->looksList_[i]->getName(); + if(name.empty()) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "The look at index '" << i << "' "; + os << "does not specify a name."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + + std::string processSpace = getImpl()->looksList_[i]->getProcessSpace(); + if(processSpace.empty()) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "The look '" << name << "' "; + os << "does not specify a process space."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + + int csindex=0; + if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, processSpace)) + { + std::ostringstream os; + os << "Config failed sanitycheck. "; + os << "The look '" << name << "' "; + os << "specifies a process color space, '"; + os << processSpace << "', which is not defined."; + getImpl()->sanitytext_ = os.str(); + throw Exception(getImpl()->sanitytext_.c_str()); + } + } + + + + // Everything is groovy. + getImpl()->sanity_ = SANITY_SANE; + } + + /////////////////////////////////////////////////////////////////////////// + + const char * Config::getDescription() const + { + return getImpl()->description_.c_str(); + } + + void Config::setDescription(const char * description) + { + getImpl()->description_ = description; + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + + // RESOURCES ////////////////////////////////////////////////////////////// + + ConstContextRcPtr Config::getCurrentContext() const + { + return getImpl()->context_; + } + + const char * Config::getSearchPath() const + { + return getImpl()->context_->getSearchPath(); + } + + void Config::setSearchPath(const char * path) + { + getImpl()->context_->setSearchPath(path); + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + const char * Config::getWorkingDir() const + { + return getImpl()->context_->getWorkingDir(); + } + + void Config::setWorkingDir(const char * dirname) + { + getImpl()->context_->setWorkingDir(dirname); + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + + /////////////////////////////////////////////////////////////////////////// + + int Config::getNumColorSpaces() const + { + return static_cast(getImpl()->colorspaces_.size()); + } + + const char * Config::getColorSpaceNameByIndex(int index) const + { + if(index<0 || index >= (int)getImpl()->colorspaces_.size()) + { + return ""; + } + + return getImpl()->colorspaces_[index]->getName(); + } + + ConstColorSpaceRcPtr Config::getColorSpace(const char * name) const + { + int index = getIndexForColorSpace(name); + if(index<0 || index >= (int)getImpl()->colorspaces_.size()) + { + return ColorSpaceRcPtr(); + } + + return getImpl()->colorspaces_[index]; + } + + int Config::getIndexForColorSpace(const char * name) const + { + int csindex = -1; + + // Check to see if the name is a color space + if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, name) ) + { + return csindex; + } + + // Check to see if the name is a role + std::string csname = LookupRole(getImpl()->roles_, name); + if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) ) + { + return csindex; + } + + // Is a default role defined? + // (And, are we allowed to use it) + if(!getImpl()->strictParsing_) + { + csname = LookupRole(getImpl()->roles_, ROLE_DEFAULT); + if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) ) + { + return csindex; + } + } + + return -1; + } + + void Config::addColorSpace(const ConstColorSpaceRcPtr & original) + { + ColorSpaceRcPtr cs = original->createEditableCopy(); + + std::string name = cs->getName(); + if(name.empty()) + throw Exception("Cannot addColorSpace with an empty name."); + + // Check to see if the colorspace already exists + int csindex = -1; + if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, name) ) + { + getImpl()->colorspaces_[csindex] = cs; + } + else + { + // Otherwise, add it + getImpl()->colorspaces_.push_back( cs ); + } + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + void Config::clearColorSpaces() + { + getImpl()->colorspaces_.clear(); + } + + + + + + + const char * Config::parseColorSpaceFromString(const char * str) const + { + if(!str) return ""; + + // Search the entire filePath, including directory name (if provided) + // convert the filename to lowercase. + std::string fullstr = pystring::lower(std::string(str)); + + // See if it matches a lut name. + // This is the position of the RIGHT end of the colorspace substring, not the left + int rightMostColorPos=-1; + std::string rightMostColorspace = ""; + int rightMostColorSpaceIndex = -1; + + // Find the right-most occcurance within the string for each colorspace. + for (unsigned int i=0; icolorspaces_.size(); ++i) + { + std::string csname = pystring::lower(getImpl()->colorspaces_[i]->getName()); + + // find right-most extension matched in filename + int colorspacePos = pystring::rfind(fullstr, csname); + if(colorspacePos < 0) + continue; + + // If we have found a match, move the pointer over to the right end of the substring + // This will allow us to find the longest name that matches the rightmost colorspace + colorspacePos += (int)csname.size(); + + if ( (colorspacePos > rightMostColorPos) || + ((colorspacePos == rightMostColorPos) && (csname.size() > rightMostColorspace.size())) + ) + { + rightMostColorPos = colorspacePos; + rightMostColorspace = csname; + rightMostColorSpaceIndex = i; + } + } + + if(rightMostColorSpaceIndex>=0) + { + return getImpl()->colorspaces_[rightMostColorSpaceIndex]->getName(); + } + + if(!getImpl()->strictParsing_) + { + // Is a default role defined? + std::string csname = LookupRole(getImpl()->roles_, ROLE_DEFAULT); + if(!csname.empty()) + { + int csindex = -1; + if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) ) + { + // This is necessary to not return a reference to + // a local variable. + return getImpl()->colorspaces_[csindex]->getName(); + } + } + } + + return ""; + } + + bool Config::isStrictParsingEnabled() const + { + return getImpl()->strictParsing_; + } + + void Config::setStrictParsingEnabled(bool enabled) + { + getImpl()->strictParsing_ = enabled; + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + // Roles + void Config::setRole(const char * role, const char * colorSpaceName) + { + // Set the role + if(colorSpaceName) + { + getImpl()->roles_[pystring::lower(role)] = std::string(colorSpaceName); + } + // Unset the role + else + { + StringMap::iterator iter = getImpl()->roles_.find(pystring::lower(role)); + if(iter != getImpl()->roles_.end()) + { + getImpl()->roles_.erase(iter); + } + } + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + int Config::getNumRoles() const + { + return static_cast(getImpl()->roles_.size()); + } + + bool Config::hasRole(const char * role) const + { + return LookupRole(getImpl()->roles_, role) == "" ? false : true; + } + + const char * Config::getRoleName(int index) const + { + if(index < 0 || index >= (int)getImpl()->roles_.size()) return ""; + StringMap::const_iterator iter = getImpl()->roles_.begin(); + for(int i = 0; i < index; ++i) ++iter; + return iter->first.c_str(); + } + + /////////////////////////////////////////////////////////////////////////// + // + // Display/View Registration + + + const char * Config::getDefaultDisplay() const + { + if(getImpl()->displayCache_.empty()) + { + ComputeDisplays(getImpl()->displayCache_, + getImpl()->displays_, + getImpl()->activeDisplays_, + getImpl()->activeDisplaysEnvOverride_); + } + + int index = -1; + + if(!getImpl()->activeDisplaysEnvOverride_.empty()) + { + StringVec orderedDisplays = IntersectStringVecsCaseIgnore(getImpl()->activeDisplaysEnvOverride_, + getImpl()->displayCache_); + if(!orderedDisplays.empty()) + { + index = FindInStringVecCaseIgnore(getImpl()->displayCache_, orderedDisplays[0]); + } + } + else if(!getImpl()->activeDisplays_.empty()) + { + StringVec orderedDisplays = IntersectStringVecsCaseIgnore(getImpl()->activeDisplays_, + getImpl()->displayCache_); + if(!orderedDisplays.empty()) + { + index = FindInStringVecCaseIgnore(getImpl()->displayCache_, orderedDisplays[0]); + } + } + + if(index >= 0) + { + return getImpl()->displayCache_[index].c_str(); + } + + if(!getImpl()->displayCache_.empty()) + { + return getImpl()->displayCache_[0].c_str(); + } + + return ""; + } + + + int Config::getNumDisplays() const + { + if(getImpl()->displayCache_.empty()) + { + ComputeDisplays(getImpl()->displayCache_, + getImpl()->displays_, + getImpl()->activeDisplays_, + getImpl()->activeDisplaysEnvOverride_); + } + + return static_cast(getImpl()->displayCache_.size()); + } + + const char * Config::getDisplay(int index) const + { + if(getImpl()->displayCache_.empty()) + { + ComputeDisplays(getImpl()->displayCache_, + getImpl()->displays_, + getImpl()->activeDisplays_, + getImpl()->activeDisplaysEnvOverride_); + } + + if(index>=0 || index < static_cast(getImpl()->displayCache_.size())) + { + return getImpl()->displayCache_[index].c_str(); + } + + return ""; + } + + const char * Config::getDefaultView(const char * display) const + { + if(getImpl()->displayCache_.empty()) + { + ComputeDisplays(getImpl()->displayCache_, + getImpl()->displays_, + getImpl()->activeDisplays_, + getImpl()->activeDisplaysEnvOverride_); + } + + if(!display) return ""; + + DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display); + if(iter == getImpl()->displays_.end()) return ""; + + const ViewVec & views = iter->second; + + StringVec masterViews; + for(unsigned int i=0; iactiveViewsEnvOverride_.empty()) + { + StringVec orderedViews = IntersectStringVecsCaseIgnore(getImpl()->activeViewsEnvOverride_, + masterViews); + if(!orderedViews.empty()) + { + index = FindInStringVecCaseIgnore(masterViews, orderedViews[0]); + } + } + else if(!getImpl()->activeViews_.empty()) + { + StringVec orderedViews = IntersectStringVecsCaseIgnore(getImpl()->activeViews_, + masterViews); + if(!orderedViews.empty()) + { + index = FindInStringVecCaseIgnore(masterViews, orderedViews[0]); + } + } + + if(index >= 0) + { + return views[index].name.c_str(); + } + + if(!views.empty()) + { + return views[0].name.c_str(); + } + + return ""; + } + + int Config::getNumViews(const char * display) const + { + if(getImpl()->displayCache_.empty()) + { + ComputeDisplays(getImpl()->displayCache_, + getImpl()->displays_, + getImpl()->activeDisplays_, + getImpl()->activeDisplaysEnvOverride_); + } + + if(!display) return 0; + + DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display); + if(iter == getImpl()->displays_.end()) return 0; + + const ViewVec & views = iter->second; + return static_cast(views.size()); + } + + const char * Config::getView(const char * display, int index) const + { + if(getImpl()->displayCache_.empty()) + { + ComputeDisplays(getImpl()->displayCache_, + getImpl()->displays_, + getImpl()->activeDisplays_, + getImpl()->activeDisplaysEnvOverride_); + } + + if(!display) return ""; + + DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display); + if(iter == getImpl()->displays_.end()) return ""; + + const ViewVec & views = iter->second; + return views[index].name.c_str(); + } + + const char * Config::getDisplayColorSpaceName(const char * display, const char * view) const + { + if(!display || !view) return ""; + + DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display); + if(iter == getImpl()->displays_.end()) return ""; + + const ViewVec & views = iter->second; + int index = find_view(views, view); + if(index<0) return ""; + + return views[index].colorspace.c_str(); + } + + const char * Config::getDisplayLooks(const char * display, const char * view) const + { + if(!display || !view) return ""; + + DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display); + if(iter == getImpl()->displays_.end()) return ""; + + const ViewVec & views = iter->second; + int index = find_view(views, view); + if(index<0) return ""; + + return views[index].looks.c_str(); + } + + void Config::addDisplay(const char * display, const char * view, + const char * colorSpaceName, const char * lookName) + { + + if(!display || !view || !colorSpaceName || !lookName) return; + + AddDisplay(getImpl()->displays_, + display, view, colorSpaceName, lookName); + getImpl()->displayCache_.clear(); + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + void Config::clearDisplays() + { + getImpl()->displays_.clear(); + getImpl()->displayCache_.clear(); + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + void Config::setActiveDisplays(const char * displays) + { + getImpl()->activeDisplays_.clear(); + SplitStringEnvStyle(getImpl()->activeDisplays_, displays); + + getImpl()->displayCache_.clear(); + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + const char * Config::getActiveDisplays() const + { + getImpl()->activeDisplaysStr_ = JoinStringEnvStyle(getImpl()->activeDisplays_); + return getImpl()->activeDisplaysStr_.c_str(); + } + + void Config::setActiveViews(const char * views) + { + getImpl()->activeViews_.clear(); + SplitStringEnvStyle(getImpl()->activeViews_, views); + + getImpl()->displayCache_.clear(); + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + const char * Config::getActiveViews() const + { + getImpl()->activeViewsStr_ = JoinStringEnvStyle(getImpl()->activeViews_); + return getImpl()->activeViewsStr_.c_str(); + } + + /////////////////////////////////////////////////////////////////////////// + + + void Config::getDefaultLumaCoefs(float * c3) const + { + memcpy(c3, &getImpl()->defaultLumaCoefs_[0], 3*sizeof(float)); + } + + void Config::setDefaultLumaCoefs(const float * c3) + { + memcpy(&getImpl()->defaultLumaCoefs_[0], c3, 3*sizeof(float)); + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + + + + /////////////////////////////////////////////////////////////////////////// + + + + + ConstLookRcPtr Config::getLook(const char * name) const + { + std::string namelower = pystring::lower(name); + + for(unsigned int i=0; ilooksList_.size(); ++i) + { + if(pystring::lower(getImpl()->looksList_[i]->getName()) == namelower) + { + return getImpl()->looksList_[i]; + } + } + + return ConstLookRcPtr(); + } + + int Config::getNumLooks() const + { + return static_cast(getImpl()->looksList_.size()); + } + + const char * Config::getLookNameByIndex(int index) const + { + if(index<0 || index>=static_cast(getImpl()->looksList_.size())) + { + return ""; + } + + return getImpl()->looksList_[index]->getName(); + } + + void Config::addLook(const ConstLookRcPtr & look) + { + std::string name = look->getName(); + if(name.empty()) + throw Exception("Cannot addLook with an empty name."); + + std::string namelower = pystring::lower(name); + + // If the look exists, replace it + for(unsigned int i=0; ilooksList_.size(); ++i) + { + if(pystring::lower(getImpl()->looksList_[i]->getName()) == namelower) + { + getImpl()->looksList_[i] = look->createEditableCopy(); + return; + } + } + + // Otherwise, add it + getImpl()->looksList_.push_back(look->createEditableCopy()); + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + void Config::clearLooks() + { + getImpl()->looksList_.clear(); + + AutoMutex lock(getImpl()->cacheidMutex_); + getImpl()->resetCacheIDs(); + } + + /////////////////////////////////////////////////////////////////////////// + + + + ConstProcessorRcPtr Config::getProcessor(const ConstColorSpaceRcPtr & src, + const ConstColorSpaceRcPtr & dst) const + { + ConstContextRcPtr context = getCurrentContext(); + return getProcessor(context, src, dst); + } + + ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context, + const ConstColorSpaceRcPtr & src, + const ConstColorSpaceRcPtr & dst) const + { + if(!src) + { + throw Exception("Config::GetProcessor failed. Source colorspace is null."); + } + if(!dst) + { + throw Exception("Config::GetProcessor failed. Destination colorspace is null."); + } + + ProcessorRcPtr processor = Processor::Create(); + processor->getImpl()->addColorSpaceConversion(*this, context, src, dst); + processor->getImpl()->finalize(); + return processor; + } + + ConstProcessorRcPtr Config::getProcessor(const char * srcName, + const char * dstName) const + { + ConstContextRcPtr context = getCurrentContext(); + return getProcessor(context, srcName, dstName); + } + + //! Names can be colorspace name or role name + ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context, + const char * srcName, + const char * dstName) const + { + ConstColorSpaceRcPtr src = getColorSpace(srcName); + if(!src) + { + std::ostringstream os; + os << "Could not find colorspace '" << srcName << "'."; + throw Exception(os.str().c_str()); + } + + ConstColorSpaceRcPtr dst = getColorSpace(dstName); + if(!dst) + { + std::ostringstream os; + os << "Could not find colorspace '" << dstName << "'."; + throw Exception(os.str().c_str()); + } + + return getProcessor(context, src, dst); + } + + + ConstProcessorRcPtr Config::getProcessor(const ConstTransformRcPtr& transform) const + { + return getProcessor(transform, TRANSFORM_DIR_FORWARD); + } + + + ConstProcessorRcPtr Config::getProcessor(const ConstTransformRcPtr& transform, + TransformDirection direction) const + { + ConstContextRcPtr context = getCurrentContext(); + return getProcessor(context, transform, direction); + } + + ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context, + const ConstTransformRcPtr& transform, + TransformDirection direction) const + { + ProcessorRcPtr processor = Processor::Create(); + processor->getImpl()->addTransform(*this, context, transform, direction); + processor->getImpl()->finalize(); + return processor; + } + + std::ostream& operator<< (std::ostream& os, const Config& config) + { + config.serialize(os); + return os; + } + + /////////////////////////////////////////////////////////////////////////// + // CacheID + + const char * Config::getCacheID() const + { + return getCacheID(getCurrentContext()); + } + + const char * Config::getCacheID(const ConstContextRcPtr & context) const + { + AutoMutex lock(getImpl()->cacheidMutex_); + + // A null context will use the empty cacheid + std::string contextcacheid = ""; + if(context) contextcacheid = context->getCacheID(); + + StringMap::const_iterator cacheiditer = getImpl()->cacheids_.find(contextcacheid); + if(cacheiditer != getImpl()->cacheids_.end()) + { + return cacheiditer->second.c_str(); + } + + // Include the hash of the yaml config serialization + if(getImpl()->cacheidnocontext_.empty()) + { + std::stringstream cacheid; + serialize(cacheid); + std::string fullstr = cacheid.str(); + getImpl()->cacheidnocontext_ = CacheIDHash(fullstr.c_str(), (int)fullstr.size()); + } + + // Also include all file references, using the context (if specified) + std::string fileReferencesFashHash = ""; + if(context) + { + std::ostringstream filehash; + + ConstTransformVec allTransforms; + getImpl()->getAllIntenalTransforms(allTransforms); + + std::set files; + for(unsigned int i=0; i::iterator iter = files.begin(); + iter != files.end(); ++iter) + { + if(iter->empty()) continue; + filehash << *iter << "="; + + try + { + std::string resolvedLocation = context->resolveFileLocation(iter->c_str()); + filehash << GetFastFileHash(resolvedLocation) << " "; + } + catch(...) + { + filehash << "? "; + continue; + } + } + + std::string fullstr = filehash.str(); + fileReferencesFashHash = CacheIDHash(fullstr.c_str(), (int)fullstr.size()); + } + + getImpl()->cacheids_[contextcacheid] = getImpl()->cacheidnocontext_ + ":" + fileReferencesFashHash; + return getImpl()->cacheids_[contextcacheid].c_str(); + } + + + /////////////////////////////////////////////////////////////////////////// + // Serialization + + void Config::serialize(std::ostream& os) const + { + try + { + YAML::Emitter out; + out << YAML::Block; + out << YAML::BeginMap; + out << YAML::Key << "ocio_profile_version" << YAML::Value << 1; + out << YAML::Newline; + + out << YAML::Key << "search_path" << YAML::Value << getImpl()->context_->getSearchPath(); + out << YAML::Key << "strictparsing" << YAML::Value << getImpl()->strictParsing_; + out << YAML::Key << "luma" << YAML::Value << YAML::Flow << getImpl()->defaultLumaCoefs_; + + if(getImpl()->description_ != "") + { + out << YAML::Newline; + out << YAML::Key << "description"; + out << YAML::Value << getImpl()->description_; + } + + // Roles + out << YAML::Newline; + out << YAML::Key << "roles"; + out << YAML::Value << getImpl()->roles_; + + // Displays + out << YAML::Newline; + out << YAML::Key << "displays"; + out << YAML::Value << getImpl()->displays_; + out << YAML::Newline; + out << YAML::Key << "active_displays"; + out << YAML::Value << YAML::Flow << getImpl()->activeDisplays_; + out << YAML::Key << "active_views"; + out << YAML::Value << YAML::Flow << getImpl()->activeViews_; + + // Looks + if(!getImpl()->looksList_.empty()) + { + out << YAML::Newline; + out << YAML::Key << "looks"; + out << YAML::Value << getImpl()->looksList_; + } + + // ColorSpaces + { + out << YAML::Newline; + out << YAML::Key << "colorspaces"; + out << YAML::Value << getImpl()->colorspaces_; + } + + out << YAML::EndMap; + + os << out.c_str(); + } + catch( const std::exception & e) + { + std::ostringstream error; + error << "Error building YAML: " << e.what(); + throw Exception(error.str().c_str()); + } + } + + void Config::Impl::load(std::istream & istream, const char * filename) + { + try + { + YAML::Parser parser(istream); + YAML::Node node; + parser.GetNextDocument(node); + + // check profile version + int profile_version = 0; + if(node.FindValue("ocio_profile_version") == NULL) + { + std::ostringstream os; + os << "The specified file "; + os << "does not appear to be an OCIO configuration."; + throw Exception (os.str().c_str()); + } + + node["ocio_profile_version"] >> profile_version; + if(profile_version > 1) + { + std::ostringstream os; + os << "This .ocio config "; + if(filename && *filename) + { + os << " '" << filename << "' "; + } + os << "is version " << profile_version << ". "; + os << "This version of the OpenColorIO library (" << OCIO_VERSION ") "; + os << "is not known to be able to load this profile. "; + os << "An attempt will be made, but there are no guarantees that the "; + os << "results will be accurate. Continue at your own risk."; + LogWarning(os.str()); + } + + + std::string key, stringval; + bool boolval = false; + + for (YAML::Iterator iter = node.begin(); + iter != node.end(); + ++iter) + { + iter.first() >> key; + + if(key == "ocio_profile_version") { } // Already handled above. + else if(key == "search_path" || key == "resource_path") + { + if (iter.second().Type() != YAML::NodeType::Null && + iter.second().Read(stringval)) + context_->setSearchPath(stringval.c_str()); + } + else if(key == "strictparsing") + { + if (iter.second().Type() != YAML::NodeType::Null && + iter.second().Read(boolval)) + strictParsing_ = boolval; + } + else if(key == "description") + { + if (iter.second().Type() != YAML::NodeType::Null && + iter.second().Read(stringval)) + description_ = stringval; + } + else if(key == "luma") + { + std::vector val; + if (iter.second().Type() != YAML::NodeType::Null) + { + iter.second() >> val; + if(val.size() != 3) + { + std::ostringstream os; + os << "'luma' field must be 3 "; + os << "floats. Found '" << val.size() << "'."; + throw Exception(os.str().c_str()); + } + defaultLumaCoefs_ = val; + } + } + else if(key == "roles") + { + const YAML::Node& roles = iter.second(); + if(roles.Type() != YAML::NodeType::Map) + { + std::ostringstream os; + os << "'roles' field needs to be a (name: key) map."; + throw Exception(os.str().c_str()); + } + for (YAML::Iterator it = roles.begin(); + it != roles.end(); ++it) + { + std::string k, v; + it.first() >> k; + it.second() >> v; + roles_[pystring::lower(k)] = v; + } + } + else if(key == "displays") + { + if (iter.second().Type() != YAML::NodeType::Null) + { + iter.second() >> displays_; + } + } + else if(key == "active_displays") + { + if (iter.second().Type() != YAML::NodeType::Null) + { + iter.second() >> activeDisplays_; + } + } + else if(key == "active_views") + { + if (iter.second().Type() != YAML::NodeType::Null) + { + iter.second() >> activeViews_; + } + } + else if(key == "colorspaces") + { + const YAML::Node& colorspaces = iter.second(); + + if(colorspaces.Type() != YAML::NodeType::Sequence) + { + std::ostringstream os; + os << "'colorspaces' field needs to be a (- !) list."; + throw Exception(os.str().c_str()); + } + + for(unsigned i = 0; i < colorspaces.size(); ++i) + { + if(colorspaces[i].Tag() == "ColorSpace") + { + ColorSpaceRcPtr cs = ColorSpace::Create(); + colorspaces[i] >> cs; + colorspaces_.push_back( cs ); + } + else + { + std::ostringstream os; + os << "Unknown element found in colorspaces:"; + os << colorspaces[i].Tag() << ". Only ColorSpace(s)"; + os << " currently handled."; + LogWarning(os.str()); + } + } + } + else if(key == "looks") + { + const YAML::Node& looks = iter.second(); + + if(looks.Type() != YAML::NodeType::Sequence) + { + std::ostringstream os; + os << "'looks' field needs to be a (- !) list."; + throw Exception(os.str().c_str()); + } + + for(unsigned i = 0; i < looks.size(); ++i) + { + if(looks[i].Tag() == "Look") + { + LookRcPtr look = Look::Create(); + looks[i] >> look; + looksList_.push_back( look ); + } + else + { + std::ostringstream os; + os << "Unknown element found in looks:"; + os << looks[i].Tag() << ". Only Look(s)"; + os << " currently handled."; + LogWarning(os.str()); + } + } + } + else + { + LogUnknownKeyWarning("profile", iter.first()); + } + } + + if(filename) + { + std::string realfilename = pystring::os::path::abspath(filename); + std::string configrootdir = pystring::os::path::dirname(realfilename); + context_->setWorkingDir(configrootdir.c_str()); + } + } + catch( const std::exception & e) + { + std::ostringstream os; + os << "Error: Loading the OCIO profile "; + if(filename) os << "'" << filename << "' "; + os << "failed. " << e.what(); + throw Exception(os.str().c_str()); + } + } + + void Config::Impl::resetCacheIDs() + { + cacheids_.clear(); + cacheidnocontext_ = ""; + sanity_ = SANITY_UNKNOWN; + sanitytext_ = ""; + } + + void Config::Impl::getAllIntenalTransforms(ConstTransformVec & transformVec) const + { + // Grab all transforms from the ColorSpaces + for(unsigned int i=0; igetTransform(COLORSPACE_DIR_TO_REFERENCE)) + transformVec.push_back(colorspaces_[i]->getTransform(COLORSPACE_DIR_TO_REFERENCE)); + if(colorspaces_[i]->getTransform(COLORSPACE_DIR_FROM_REFERENCE)) + transformVec.push_back(colorspaces_[i]->getTransform(COLORSPACE_DIR_FROM_REFERENCE)); + } + + // Grab all transforms from the Looks + for(unsigned int i=0; igetTransform()) + transformVec.push_back(looksList_[i]->getTransform()); + if(looksList_[i]->getInverseTransform()) + transformVec.push_back(looksList_[i]->getInverseTransform()); + } + + } +} +OCIO_NAMESPACE_EXIT + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef OCIO_UNIT_TEST + +namespace OCIO = OCIO_NAMESPACE; +#include "UnitTest.h" + +#include +#include "pystring/pystring.h" + +#if 0 +OIIO_ADD_TEST(Config, test_searchpath_filesystem) +{ + + OCIO::EnvMap env = OCIO::GetEnvMap(); + std::string OCIO_TEST_AREA("$OCIO_TEST_AREA"); + EnvExpand(&OCIO_TEST_AREA, &env); + + OCIO::ConfigRcPtr config = OCIO::Config::Create(); + + // basic get/set/expand + config->setSearchPath("." + ":$OCIO_TEST1" + ":/$OCIO_JOB/${OCIO_SEQ}/$OCIO_SHOT/ocio"); + + OIIO_CHECK_ASSERT(strcmp(config->getSearchPath(), + ".:$OCIO_TEST1:/$OCIO_JOB/${OCIO_SEQ}/$OCIO_SHOT/ocio") == 0); + OIIO_CHECK_ASSERT(strcmp(config->getSearchPath(true), + ".:foobar:/meatballs/cheesecake/mb-cc-001/ocio") == 0); + + // find some files + config->setSearchPath(".." + ":$OCIO_TEST1" + ":${OCIO_TEST_AREA}/test_search/one" + ":$OCIO_TEST_AREA/test_search/two"); + + // setup for search test + std::string base_dir("$OCIO_TEST_AREA/test_search/"); + EnvExpand(&base_dir, &env); + mkdir(base_dir.c_str(), 0777); + + std::string one_dir("$OCIO_TEST_AREA/test_search/one/"); + EnvExpand(&one_dir, &env); + mkdir(one_dir.c_str(), 0777); + + std::string two_dir("$OCIO_TEST_AREA/test_search/two/"); + EnvExpand(&two_dir, &env); + mkdir(two_dir.c_str(), 0777); + + std::string lut1(one_dir+"somelut1.lut"); + std::ofstream somelut1(lut1.c_str()); + somelut1.close(); + + std::string lut2(two_dir+"somelut2.lut"); + std::ofstream somelut2(lut2.c_str()); + somelut2.close(); + + std::string lut3(two_dir+"somelut3.lut"); + std::ofstream somelut3(lut3.c_str()); + somelut3.close(); + + std::string lutdotdot(OCIO_TEST_AREA+"/lutdotdot.lut"); + std::ofstream somelutdotdot(lutdotdot.c_str()); + somelutdotdot.close(); + + // basic search test + OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut1.lut"), + lut1.c_str()) == 0); + OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut2.lut"), + lut2.c_str()) == 0); + OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut3.lut"), + lut3.c_str()) == 0); + OIIO_CHECK_ASSERT(strcmp(config->findFile("lutdotdot.lut"), + lutdotdot.c_str()) == 0); + +} +#endif + +OIIO_ADD_TEST(Config, InternalRawProfile) +{ + std::istringstream is; + is.str(OCIO::INTERNAL_RAW_PROFILE); + OIIO_CHECK_NO_THOW(OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromStream(is)); +} + +OIIO_ADD_TEST(Config, SimpleConfig) +{ + + std::string SIMPLE_PROFILE = + "ocio_profile_version: 1\n" + "resource_path: luts\n" + "strictparsing: false\n" + "luma: [0.2126, 0.7152, 0.0722]\n" + "roles:\n" + " compositing_log: lgh\n" + " default: raw\n" + " scene_linear: lnh\n" + "displays:\n" + " sRGB:\n" + " - ! {name: Film1D, colorspace: vd8}\n" + " - ! {name: Log, colorspace: lg10}\n" + " - ! {name: Raw, colorspace: raw}\n" + "colorspaces:\n" + " - !\n" + " name: raw\n" + " family: raw\n" + " equalitygroup: \n" + " bitdepth: 32f\n" + " description: |\n" + " A raw color space. Conversions to and from this space are no-ops.\n" + " isdata: true\n" + " allocation: uniform\n" + " - !\n" + " name: lnh\n" + " family: ln\n" + " equalitygroup: \n" + " bitdepth: 16f\n" + " description: |\n" + " The show reference space. This is a sensor referred linear\n" + " representation of the scene with primaries that correspond to\n" + " scanned film. 0.18 in this space corresponds to a properly\n" + " exposed 18% grey card.\n" + " isdata: false\n" + " allocation: lg2\n" + " - !\n" + " name: loads_of_transforms\n" + " family: vd8\n" + " equalitygroup: \n" + " bitdepth: 8ui\n" + " description: 'how many transforms can we use?'\n" + " isdata: false\n" + " allocation: uniform\n" + " to_reference: !\n" + " direction: forward\n" + " children:\n" + " - !\n" + " src: diffusemult.spimtx\n" + " interpolation: unknown\n" + " - !\n" + " src: vd8\n" + " dst: lnh\n" + " - !\n" + " value: [2.2, 2.2, 2.2, 1]\n" + " - !\n" + " matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]\n" + " offset: [0, 0, 0, 0]\n" + " - !\n" + " slope: [1, 1, 1]\n" + " offset: [0, 0, 0]\n" + " power: [1, 1, 1]\n" + " saturation: 1\n" + "\n"; + + std::istringstream is; + is.str(SIMPLE_PROFILE); + OCIO::ConstConfigRcPtr config; + OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is)); +} + +OIIO_ADD_TEST(Config, Roles) +{ + + std::string SIMPLE_PROFILE = + "ocio_profile_version: 1\n" + "strictparsing: false\n" + "roles:\n" + " compositing_log: lgh\n" + " default: raw\n" + " scene_linear: lnh\n" + "colorspaces:\n" + " - !\n" + " name: raw\n" + " - !\n" + " name: lnh\n" + " - !\n" + " name: lgh\n" + "\n"; + + std::istringstream is; + is.str(SIMPLE_PROFILE); + OCIO::ConstConfigRcPtr config; + OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is)); + + OIIO_CHECK_EQUAL(config->getNumRoles(), 3); + + OIIO_CHECK_ASSERT(config->hasRole("compositing_log") == true); + OIIO_CHECK_ASSERT(config->hasRole("cheese") == false); + OIIO_CHECK_ASSERT(config->hasRole("") == false); + + OIIO_CHECK_ASSERT(strcmp(config->getRoleName(2), "scene_linear") == 0); + OIIO_CHECK_ASSERT(strcmp(config->getRoleName(0), "compositing_log") == 0); + OIIO_CHECK_ASSERT(strcmp(config->getRoleName(1), "default") == 0); + OIIO_CHECK_ASSERT(strcmp(config->getRoleName(10), "") == 0); + OIIO_CHECK_ASSERT(strcmp(config->getRoleName(-4), "") == 0); + +} + +OIIO_ADD_TEST(Config, Serialize) +{ + + OCIO::ConfigRcPtr config = OCIO::Config::Create(); + { + OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create(); + cs->setName("testing"); + cs->setFamily("test"); + OCIO::FileTransformRcPtr transform1 = \ + OCIO::FileTransform::Create(); + OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create(); + groupTransform->push_back(transform1); + cs->setTransform(groupTransform, OCIO::COLORSPACE_DIR_TO_REFERENCE); + config->addColorSpace(cs); + config->setRole( OCIO::ROLE_COMPOSITING_LOG, cs->getName() ); + } + { + OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create(); + cs->setName("testing2"); + cs->setFamily("test"); + OCIO::ExponentTransformRcPtr transform1 = \ + OCIO::ExponentTransform::Create(); + OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create(); + groupTransform->push_back(transform1); + cs->setTransform(groupTransform, OCIO::COLORSPACE_DIR_TO_REFERENCE); + config->addColorSpace(cs); + config->setRole( OCIO::ROLE_COMPOSITING_LOG, cs->getName() ); + } + + // for testing + //std::ofstream outfile("/tmp/test.ocio"); + //config->serialize(outfile); + //outfile.close(); + + std::ostringstream os; + config->serialize(os); + + std::string PROFILE_OUT = + "ocio_profile_version: 1\n" + "\n" + "search_path: \"\"\n" + "strictparsing: true\n" + "luma: [0.2126, 0.7152, 0.0722]\n" + "\n" + "roles:\n" + " compositing_log: testing2\n" + "\n" + "displays:\n" + " {}\n" + "\n" + "active_displays: []\n" + "active_views: []\n" + "\n" + "colorspaces:\n" + " - !\n" + " name: testing\n" + " family: test\n" + " equalitygroup: \"\"\n" + " bitdepth: unknown\n" + " isdata: false\n" + " allocation: uniform\n" + " to_reference: !\n" + " children:\n" + " - ! {src: \"\", interpolation: unknown}\n" + "\n" + " - !\n" + " name: testing2\n" + " family: test\n" + " equalitygroup: \"\"\n" + " bitdepth: unknown\n" + " isdata: false\n" + " allocation: uniform\n" + " to_reference: !\n" + " children:\n" + " - ! {value: [1, 1, 1, 1]}\n"; + + std::vector osvec; + OCIO::pystring::splitlines(os.str(), osvec); + std::vector PROFILE_OUTvec; + OCIO::pystring::splitlines(PROFILE_OUT, PROFILE_OUTvec); + + OIIO_CHECK_EQUAL(osvec.size(), PROFILE_OUTvec.size()); + for(unsigned int i = 0; i < PROFILE_OUTvec.size(); ++i) + OIIO_CHECK_EQUAL(osvec[i], PROFILE_OUTvec[i]); +} + + +OIIO_ADD_TEST(Config, SanityCheck) +{ + { + std::string SIMPLE_PROFILE = + "ocio_profile_version: 1\n" + "colorspaces:\n" + " - !\n" + " name: raw\n" + " - !\n" + " name: raw\n" + "strictparsing: false\n" + "roles:\n" + " default: raw\n" + "displays:\n" + " sRGB:\n" + " - ! {name: Raw, colorspace: raw}\n" + "\n"; + + std::istringstream is; + is.str(SIMPLE_PROFILE); + OCIO::ConstConfigRcPtr config; + OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is)); + + OIIO_CHECK_THOW(config->sanityCheck(), OCIO::Exception); + } + + { + std::string SIMPLE_PROFILE = + "ocio_profile_version: 1\n" + "colorspaces:\n" + " - !\n" + " name: raw\n" + "strictparsing: false\n" + "roles:\n" + " default: raw\n" + "displays:\n" + " sRGB:\n" + " - ! {name: Raw, colorspace: raw}\n" + "\n"; + + std::istringstream is; + is.str(SIMPLE_PROFILE); + OCIO::ConstConfigRcPtr config; + OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is)); + + OIIO_CHECK_NO_THOW(config->sanityCheck()); + } +} + + +#endif // OCIO_UNIT_TEST -- cgit v1.2.3