1 /*
2 Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
3 All Rights Reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8 * Redistributions of source code must retain the above copyright
9   notice, this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11   notice, this list of conditions and the following disclaimer in the
12   documentation and/or other materials provided with the distribution.
13 * Neither the name of Sony Pictures Imageworks nor the names of its
14   contributors may be used to endorse or promote products derived from
15   this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 
30 #include <cstdlib>
31 #include <cstring>
32 #include <set>
33 #include <sstream>
34 #include <fstream>
35 #include <utility>
36 #include <vector>
37 
38 #include <OpenColorIO/OpenColorIO.h>
39 
40 #include "HashUtils.h"
41 #include "Logging.h"
42 #include "LookParse.h"
43 #include "Display.h"
44 #include "MathUtils.h"
45 #include "Mutex.h"
46 #include "OpBuilders.h"
47 #include "PathUtils.h"
48 #include "ParseUtils.h"
49 #include "Processor.h"
50 #include "PrivateTypes.h"
51 #include "pystring/pystring.h"
52 #include "OCIOYaml.h"
53 #include "Platform.h"
54 
55 OCIO_NAMESPACE_ENTER
56 {
57     namespace
58     {
59         const char * OCIO_CONFIG_ENVVAR = "OCIO";
60         const char * OCIO_ACTIVE_DISPLAYS_ENVVAR = "OCIO_ACTIVE_DISPLAYS";
61         const char * OCIO_ACTIVE_VIEWS_ENVVAR = "OCIO_ACTIVE_VIEWS";
62 
63         enum Sanity
64         {
65             SANITY_UNKNOWN = 0,
66             SANITY_SANE,
67             SANITY_INSANE
68         };
69 
70         // These are the 709 primaries specified by the ASC.
71         const float DEFAULT_LUMA_COEFF_R = 0.2126f;
72         const float DEFAULT_LUMA_COEFF_G = 0.7152f;
73         const float DEFAULT_LUMA_COEFF_B = 0.0722f;
74 
75         const char * INTERNAL_RAW_PROFILE =
76         "ocio_profile_version: 1\n"
77         "strictparsing: false\n"
78         "roles:\n"
79         "  default: raw\n"
80         "displays:\n"
81         "  sRGB:\n"
82         "  - !<View> {name: Raw, colorspace: raw}\n"
83         "colorspaces:\n"
84         "  - !<ColorSpace>\n"
85         "      name: raw\n"
86         "      family: raw\n"
87         "      equalitygroup:\n"
88         "      bitdepth: 32f\n"
89         "      isdata: true\n"
90         "      allocation: uniform\n"
91         "      description: 'A raw color space. Conversions to and from this space are no-ops.'\n";
92     }
93 
94 
95     ///////////////////////////////////////////////////////////////////////////
96 
97     const char * GetVersion()
98     {
99         return OCIO_VERSION;
100     }
101 
102     int GetVersionHex()
103     {
104         return OCIO_VERSION_HEX;
105     }
106 
107     namespace
108     {
109         ConstConfigRcPtr g_currentConfig;
110         Mutex g_currentConfigLock;
111     }
112 
113     ConstConfigRcPtr GetCurrentConfig()
114     {
115         AutoMutex lock(g_currentConfigLock);
116 
117         if(!g_currentConfig)
118         {
119             g_currentConfig = Config::CreateFromEnv();
120         }
121 
122         return g_currentConfig;
123     }
124 
125     void SetCurrentConfig(const ConstConfigRcPtr & config)
126     {
127         AutoMutex lock(g_currentConfigLock);
128 
129         g_currentConfig = config->createEditableCopy();
130     }
131 
132     namespace
133     {
134 
135     // Environment
136     const char* LookupEnvironment(const StringMap & env,
137                                         const std::string & name)
138     {
139         StringMap::const_iterator iter = env.find(name);
140         if(iter == env.end()) return "";
141         return iter->second.c_str();
142     }
143 
144     // Roles
145     // (lower case role name: colorspace name)
146     const char* LookupRole(const StringMap & roles, const std::string & rolename)
147     {
148         StringMap::const_iterator iter = roles.find(pystring::lower(rolename));
149         if(iter == roles.end()) return "";
150         return iter->second.c_str();
151     }
152 
153 
154     void GetFileReferences(std::set<std::string> & files,
155                            const ConstTransformRcPtr & transform)
156     {
157         if(!transform) return;
158 
159         if(ConstGroupTransformRcPtr groupTransform = \
160             DynamicPtrCast<const GroupTransform>(transform))
161         {
162             for(int i=0; i<groupTransform->size(); ++i)
163             {
164                 GetFileReferences(files, groupTransform->getTransform(i));
165             }
166         }
167         else if(ConstFileTransformRcPtr fileTransform = \
168             DynamicPtrCast<const FileTransform>(transform))
169         {
170             files.insert(fileTransform->getSrc());
171         }
172     }
173 
174     void GetColorSpaceReferences(std::set<std::string> & colorSpaceNames,
175                                  const ConstTransformRcPtr & transform)
176     {
177         if(!transform) return;
178 
179         if(ConstGroupTransformRcPtr groupTransform = \
180             DynamicPtrCast<const GroupTransform>(transform))
181         {
182             for(int i=0; i<groupTransform->size(); ++i)
183             {
184                 GetColorSpaceReferences(colorSpaceNames, groupTransform->getTransform(i));
185             }
186         }
187         else if(ConstColorSpaceTransformRcPtr colorSpaceTransform = \
188             DynamicPtrCast<const ColorSpaceTransform>(transform))
189         {
190             colorSpaceNames.insert(colorSpaceTransform->getSrc());
191             colorSpaceNames.insert(colorSpaceTransform->getDst());
192         }
193         else if(ConstDisplayTransformRcPtr displayTransform = \
194             DynamicPtrCast<const DisplayTransform>(transform))
195         {
196             colorSpaceNames.insert(displayTransform->getInputColorSpaceName());
197         }
198         else if(ConstLookTransformRcPtr lookTransform = \
199             DynamicPtrCast<const LookTransform>(transform))
200         {
201             colorSpaceNames.insert(colorSpaceTransform->getSrc());
202             colorSpaceNames.insert(colorSpaceTransform->getDst());
203         }
204     }
205 
206 
207     bool FindColorSpaceIndex(int * index,
208                              const ColorSpaceVec & colorspaces,
209                              const std::string & csname)
210     {
211         if(csname.empty()) return false;
212 
213         std::string csnamelower = pystring::lower(csname);
214         for(unsigned int i = 0; i < colorspaces.size(); ++i)
215         {
216             if(csnamelower == pystring::lower(colorspaces[i]->getName()))
217             {
218                 if(index) *index = i;
219                 return true;
220             }
221         }
222 
223         return false;
224     }
225 
226     } // namespace
227 
228     class Config::Impl
229     {
230     public:
231         StringMap env_;
232         ContextRcPtr context_;
233         std::string description_;
234         ColorSpaceVec colorspaces_;
235         StringMap roles_;
236         LookVec looksList_;
237 
238         DisplayMap displays_;
239         StringVec activeDisplays_;
240         StringVec activeDisplaysEnvOverride_;
241         StringVec activeViews_;
242         StringVec activeViewsEnvOverride_;
243 
244         mutable std::string activeDisplaysStr_;
245         mutable std::string activeViewsStr_;
246         mutable StringVec displayCache_;
247 
248         // Misc
249         std::vector<float> defaultLumaCoefs_;
250         bool strictParsing_;
251 
252         mutable Sanity sanity_;
253         mutable std::string sanitytext_;
254 
255         mutable Mutex cacheidMutex_;
256         mutable StringMap cacheids_;
257         mutable std::string cacheidnocontext_;
258 
259         OCIOYaml io_;
260 
261         Impl() :
262             context_(Context::Create()),
263             strictParsing_(true),
264             sanity_(SANITY_UNKNOWN)
265         {
266             std::string activeDisplays;
267             Platform::getenv(OCIO_ACTIVE_DISPLAYS_ENVVAR, activeDisplays);
268             activeDisplays = pystring::strip(activeDisplays);
269             if (!activeDisplays.empty()) {
270                 SplitStringEnvStyle(activeDisplaysEnvOverride_, activeDisplays.c_str());
271             }
272 
273             std::string activeViews;
274             Platform::getenv(OCIO_ACTIVE_VIEWS_ENVVAR, activeViews);
275             activeViews = pystring::strip(activeViews);
276             if (!activeViews.empty()) {
277                 SplitStringEnvStyle(activeViewsEnvOverride_, activeViews.c_str());
278             }
279 
280             defaultLumaCoefs_.resize(3);
281             defaultLumaCoefs_[0] = DEFAULT_LUMA_COEFF_R;
282             defaultLumaCoefs_[1] = DEFAULT_LUMA_COEFF_G;
283             defaultLumaCoefs_[2] = DEFAULT_LUMA_COEFF_B;
284         }
285 
286         ~Impl()
287         {
288 
289         }
290 
291         Impl& operator= (const Impl & rhs)
292         {
293             if(this!=&rhs)
294             {
295                 env_ = rhs.env_;
296                 context_ = rhs.context_->createEditableCopy();
297                 description_ = rhs.description_;
298 
299                 // Deep copy the colorspaces
300                 colorspaces_.clear();
301                 colorspaces_.reserve(rhs.colorspaces_.size());
302                 for(unsigned int i=0; i<rhs.colorspaces_.size(); ++i)
303                 {
304                     colorspaces_.push_back(rhs.colorspaces_[i]->createEditableCopy());
305                 }
306 
307                 // Deep copy the looks
308                 looksList_.clear();
309                 looksList_.reserve(rhs.looksList_.size());
310                 for(unsigned int i=0; i<rhs.looksList_.size(); ++i)
311                 {
312                     looksList_.push_back(rhs.looksList_[i]->createEditableCopy());
313                 }
314 
315                 // Assignment operator will suffice for these
316                 roles_ = rhs.roles_;
317 
318                 displays_ = rhs.displays_;
319                 activeDisplays_ = rhs.activeDisplays_;
320                 activeViews_ = rhs.activeViews_;
321                 activeViewsEnvOverride_ = rhs.activeViewsEnvOverride_;
322                 activeDisplaysEnvOverride_ = rhs.activeDisplaysEnvOverride_;
323                 activeDisplaysStr_ = rhs.activeDisplaysStr_;
324                 displayCache_ = rhs.displayCache_;
325 
326                 defaultLumaCoefs_ = rhs.defaultLumaCoefs_;
327                 strictParsing_ = rhs.strictParsing_;
328 
329                 sanity_ = rhs.sanity_;
330                 sanitytext_ = rhs.sanitytext_;
331 
332                 cacheids_ = rhs.cacheids_;
333                 cacheidnocontext_ = rhs.cacheidnocontext_;
334             }
335             return *this;
336         }
337 
338         // Any time you modify the state of the config, you must call this
339         // to reset internal cache states.  You also should do this in a
340         // thread safe manner by acquiring the cacheidMutex_;
341         void resetCacheIDs();
342 
343         // Get all internal transforms (to generate cacheIDs, validation, etc).
344         // This currently crawls colorspaces + looks
345         void getAllIntenalTransforms(ConstTransformVec & transformVec) const;
346     };
347 
348 
349     ///////////////////////////////////////////////////////////////////////////
350 
351     ConfigRcPtr Config::Create()
352     {
353         return ConfigRcPtr(new Config(), &deleter);
354     }
355 
356     void Config::deleter(Config* c)
357     {
358         delete c;
359     }
360 
361     ConstConfigRcPtr Config::CreateFromEnv()
362     {
363         std::string file;
364         Platform::getenv(OCIO_CONFIG_ENVVAR, file);
365         if(!file.empty()) return CreateFromFile(file.c_str());
366 
367         std::ostringstream os;
368         os << "Color management disabled. ";
369         os << "(Specify the $OCIO environment variable to enable.)";
370         LogInfo(os.str());
371 
372         std::istringstream istream;
373         istream.str(INTERNAL_RAW_PROFILE);
374 
375         ConfigRcPtr config = Config::Create();
376         config->getImpl()->io_.open(istream, config);
377         return config;
378     }
379 
380     ConstConfigRcPtr Config::CreateFromFile(const char * filename)
381     {
382         std::ifstream istream(filename);
383         if(istream.fail()) {
384             std::ostringstream os;
385             os << "Error could not read '" << filename;
386             os << "' OCIO profile.";
387             throw Exception (os.str().c_str());
388         }
389 
390         ConfigRcPtr config = Config::Create();
391         config->getImpl()->io_.open(istream, config, filename);
392         return config;
393     }
394 
395     ConstConfigRcPtr Config::CreateFromStream(std::istream & istream)
396     {
397         ConfigRcPtr config = Config::Create();
398         config->getImpl()->io_.open(istream, config);
399         return config;
400     }
401 
402     ///////////////////////////////////////////////////////////////////////////
403 
404 
405 
406     Config::Config()
407     : m_impl(new Config::Impl)
408     {
409     }
410 
411     Config::~Config()
412     {
413         delete m_impl;
414         m_impl = NULL;
415     }
416 
417     ConfigRcPtr Config::createEditableCopy() const
418     {
419         ConfigRcPtr config = Config::Create();
420         *config->m_impl = *m_impl;
421         return config;
422     }
423 
424     void Config::sanityCheck() const
425     {
426         if(getImpl()->sanity_ == SANITY_SANE) return;
427         if(getImpl()->sanity_ == SANITY_INSANE)
428         {
429             throw Exception(getImpl()->sanitytext_.c_str());
430         }
431 
432         getImpl()->sanity_ = SANITY_INSANE;
433         getImpl()->sanitytext_ = "";
434 
435 
436         ///// COLORSPACES
437         StringSet existingColorSpaces;
438 
439         // Confirm all ColorSpaces are valid
440         for(unsigned int i=0; i<getImpl()->colorspaces_.size(); ++i)
441         {
442             if(!getImpl()->colorspaces_[i])
443             {
444                 std::ostringstream os;
445                 os << "Config failed sanitycheck. ";
446                 os << "The colorspace at index " << i << " is null.";
447                 getImpl()->sanitytext_ = os.str();
448                 throw Exception(getImpl()->sanitytext_.c_str());
449             }
450 
451             const char * name = getImpl()->colorspaces_[i]->getName();
452             if(!name || strlen(name) == 0)
453             {
454                 std::ostringstream os;
455                 os << "Config failed sanitycheck. ";
456                 os << "The colorspace at index " << i << " is not named.";
457                 getImpl()->sanitytext_ = os.str();
458                 throw Exception(getImpl()->sanitytext_.c_str());
459             }
460 
461             std::string namelower = pystring::lower(name);
462             StringSet::const_iterator it = existingColorSpaces.find(namelower);
463             if(it != existingColorSpaces.end())
464             {
465                 std::ostringstream os;
466                 os << "Config failed sanitycheck. ";
467                 os << "Two colorspaces are defined with the same name, '";
468                 os << namelower << "'.";
469                 getImpl()->sanitytext_ = os.str();
470                 throw Exception(getImpl()->sanitytext_.c_str());
471             }
472 
473             existingColorSpaces.insert(namelower);
474         }
475 
476         // Confirm all roles are valid
477         {
478             for(StringMap::const_iterator iter = getImpl()->roles_.begin(),
479                 end = getImpl()->roles_.end(); iter!=end; ++iter)
480             {
481                 int csindex = -1;
482                 if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, iter->second))
483                 {
484                     std::ostringstream os;
485                     os << "Config failed sanitycheck. ";
486                     os << "The role '" << iter->first << "' ";
487                     os << "refers to a colorspace, '" << iter->second << "', ";
488                     os << "which is not defined.";
489                     getImpl()->sanitytext_ = os.str();
490                     throw Exception(getImpl()->sanitytext_.c_str());
491                 }
492 
493                 // Confirm no name conflicts between colorspaces and roles
494                 if(FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, iter->first))
495                 {
496                     std::ostringstream os;
497                     os << "Config failed sanitycheck. ";
498                     os << "The role '" << iter->first << "' ";
499                     os << " is in conflict with a colorspace of the same name.";
500                     getImpl()->sanitytext_ = os.str();
501                     throw Exception(getImpl()->sanitytext_.c_str());
502                 }
503             }
504         }
505 
506         ///// DISPLAYS
507 
508         int numviews = 0;
509 
510         // Confirm all Displays transforms refer to colorspaces that exit
511         for(DisplayMap::const_iterator iter = getImpl()->displays_.begin();
512             iter != getImpl()->displays_.end();
513             ++iter)
514         {
515             std::string display = iter->first;
516             const ViewVec & views = iter->second;
517             if(views.empty())
518             {
519                 std::ostringstream os;
520                 os << "Config failed sanitycheck. ";
521                 os << "The display '" << display << "' ";
522                 os << "does not define any views.";
523                 getImpl()->sanitytext_ = os.str();
524                 throw Exception(getImpl()->sanitytext_.c_str());
525             }
526 
527             for(unsigned int i=0; i<views.size(); ++i)
528             {
529                 if(views[i].name.empty() || views[i].colorspace.empty())
530                 {
531                     std::ostringstream os;
532                     os << "Config failed sanitycheck. ";
533                     os << "The display '" << display << "' ";
534                     os << "defines a view with an empty name and/or colorspace.";
535                     getImpl()->sanitytext_ = os.str();
536                     throw Exception(getImpl()->sanitytext_.c_str());
537                 }
538 
539                 int csindex = -1;
540                 if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, views[i].colorspace))
541                 {
542                     std::ostringstream os;
543                     os << "Config failed sanitycheck. ";
544                     os << "The display '" << display << "' ";
545                     os << "refers to a colorspace, '" << views[i].colorspace << "', ";
546                     os << "which is not defined.";
547                     getImpl()->sanitytext_ = os.str();
548                     throw Exception(getImpl()->sanitytext_.c_str());
549                 }
550 
551                 // Confirm looks references exist
552                 LookParseResult looks;
553                 const LookParseResult::Options & options = looks.parse(views[i].looks);
554 
555                 for(unsigned int optionindex=0;
556                     optionindex<options.size();
557                     ++optionindex)
558                 {
559                     for(unsigned int tokenindex=0;
560                         tokenindex<options[optionindex].size();
561                         ++tokenindex)
562                     {
563                         std::string look = options[optionindex][tokenindex].name;
564 
565                         if(!look.empty() && !getLook(look.c_str()))
566                         {
567                             std::ostringstream os;
568                             os << "Config failed sanitycheck. ";
569                             os << "The display '" << display << "' ";
570                             os << "refers to a look, '" << look << "', ";
571                             os << "which is not defined.";
572                             getImpl()->sanitytext_ = os.str();
573                             throw Exception(getImpl()->sanitytext_.c_str());
574                         }
575                     }
576                 }
577 
578                 ++numviews;
579             }
580         }
581 
582         // Confirm at least one display entry exists.
583         if(numviews == 0)
584         {
585             std::ostringstream os;
586             os << "Config failed sanitycheck. ";
587             os << "No displays are specified.";
588             getImpl()->sanitytext_ = os.str();
589             throw Exception(getImpl()->sanitytext_.c_str());
590         }
591 
592         // Confirm for all Transforms that reference internal colorspaces,
593         // the named space exists
594         {
595             ConstTransformVec allTransforms;
596             getImpl()->getAllIntenalTransforms(allTransforms);
597 
598             std::set<std::string> colorSpaceNames;
599             for(unsigned int i=0; i<colorSpaceNames.size(); ++i)
600             {
601                 GetColorSpaceReferences(colorSpaceNames, allTransforms[i]);
602             }
603 
604             for(std::set<std::string>::iterator iter = colorSpaceNames.begin();
605                 iter != colorSpaceNames.end(); ++iter)
606             {
607                 int csindex = -1;
608                 if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, *iter))
609                 {
610                     std::ostringstream os;
611                     os << "Config failed sanitycheck. ";
612                     os << "This config references a ColorSpace, '" << *iter << "', ";
613                     os << "which is not defined.";
614                     getImpl()->sanitytext_ = os.str();
615                     throw Exception(getImpl()->sanitytext_.c_str());
616                 }
617             }
618         }
619 
620         ///// LOOKS
621 
622         // For all looks, confirm the process space exists and the look is named
623         for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
624         {
625             std::string name = getImpl()->looksList_[i]->getName();
626             if(name.empty())
627             {
628                 std::ostringstream os;
629                 os << "Config failed sanitycheck. ";
630                 os << "The look at index '" << i << "' ";
631                 os << "does not specify a name.";
632                 getImpl()->sanitytext_ = os.str();
633                 throw Exception(getImpl()->sanitytext_.c_str());
634             }
635 
636             std::string processSpace = getImpl()->looksList_[i]->getProcessSpace();
637             if(processSpace.empty())
638             {
639                 std::ostringstream os;
640                 os << "Config failed sanitycheck. ";
641                 os << "The look '" << name << "' ";
642                 os << "does not specify a process space.";
643                 getImpl()->sanitytext_ = os.str();
644                 throw Exception(getImpl()->sanitytext_.c_str());
645             }
646 
647             int csindex=0;
648             if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, processSpace))
649             {
650                 std::ostringstream os;
651                 os << "Config failed sanitycheck. ";
652                 os << "The look '" << name << "' ";
653                 os << "specifies a process color space, '";
654                 os << processSpace << "', which is not defined.";
655                 getImpl()->sanitytext_ = os.str();
656                 throw Exception(getImpl()->sanitytext_.c_str());
657             }
658         }
659 
660 
661 
662         // Everything is groovy.
663         getImpl()->sanity_ = SANITY_SANE;
664     }
665 
666     ///////////////////////////////////////////////////////////////////////////
667 
668     const char * Config::getDescription() const
669     {
670         return getImpl()->description_.c_str();
671     }
672 
673     void Config::setDescription(const char * description)
674     {
675         getImpl()->description_ = description;
676 
677         AutoMutex lock(getImpl()->cacheidMutex_);
678         getImpl()->resetCacheIDs();
679     }
680 
681 
682     // RESOURCES //////////////////////////////////////////////////////////////
683 
684     ConstContextRcPtr Config::getCurrentContext() const
685     {
686         return getImpl()->context_;
687     }
688 
689     void Config::addEnvironmentVar(const char * name, const char * defaultValue)
690     {
691         if(defaultValue)
692         {
693             getImpl()->env_[std::string(name)] = std::string(defaultValue);
694             getImpl()->context_->setStringVar(name, defaultValue);
695         }
696         else
697         {
698             StringMap::iterator iter = getImpl()->env_.find(std::string(name));
699             if(iter != getImpl()->env_.end()) getImpl()->env_.erase(iter);
700         }
701 
702         AutoMutex lock(getImpl()->cacheidMutex_);
703         getImpl()->resetCacheIDs();
704     }
705 
706     int Config::getNumEnvironmentVars() const
707     {
708         return static_cast<int>(getImpl()->env_.size());
709     }
710 
711     const char * Config::getEnvironmentVarNameByIndex(int index) const
712     {
713         if(index < 0 || index >= (int)getImpl()->env_.size()) return "";
714         StringMap::const_iterator iter = getImpl()->env_.begin();
715         for(int i = 0; i < index; ++i) ++iter;
716         return iter->first.c_str();
717     }
718 
719     const char * Config::getEnvironmentVarDefault(const char * name) const
720     {
721         return LookupEnvironment(getImpl()->env_, name);
722     }
723 
724     void Config::clearEnvironmentVars()
725     {
726         getImpl()->env_.clear();
727         getImpl()->context_->clearStringVars();
728 
729         AutoMutex lock(getImpl()->cacheidMutex_);
730         getImpl()->resetCacheIDs();
731     }
732 
733     void Config::setEnvironmentMode(EnvironmentMode mode)
734     {
735         getImpl()->context_->setEnvironmentMode(mode);
736 
737         AutoMutex lock(getImpl()->cacheidMutex_);
738         getImpl()->resetCacheIDs();
739     }
740 
741     EnvironmentMode Config::getEnvironmentMode() const
742     {
743         return getImpl()->context_->getEnvironmentMode();
744     }
745 
746     void Config::loadEnvironment()
747     {
748         getImpl()->context_->loadEnvironment();
749 
750         AutoMutex lock(getImpl()->cacheidMutex_);
751         getImpl()->resetCacheIDs();
752     }
753 
754     const char * Config::getSearchPath() const
755     {
756         return getImpl()->context_->getSearchPath();
757     }
758 
759     void Config::setSearchPath(const char * path)
760     {
761         getImpl()->context_->setSearchPath(path);
762 
763         AutoMutex lock(getImpl()->cacheidMutex_);
764         getImpl()->resetCacheIDs();
765     }
766 
767     const char * Config::getWorkingDir() const
768     {
769         return getImpl()->context_->getWorkingDir();
770     }
771 
772     void Config::setWorkingDir(const char * dirname)
773     {
774         getImpl()->context_->setWorkingDir(dirname);
775 
776         AutoMutex lock(getImpl()->cacheidMutex_);
777         getImpl()->resetCacheIDs();
778     }
779 
780 
781     ///////////////////////////////////////////////////////////////////////////
782 
783     int Config::getNumColorSpaces() const
784     {
785         return static_cast<int>(getImpl()->colorspaces_.size());
786     }
787 
788     const char * Config::getColorSpaceNameByIndex(int index) const
789     {
790         if(index<0 || index >= (int)getImpl()->colorspaces_.size())
791         {
792             return "";
793         }
794 
795         return getImpl()->colorspaces_[index]->getName();
796     }
797 
798     ConstColorSpaceRcPtr Config::getColorSpace(const char * name) const
799     {
800         int index = getIndexForColorSpace(name);
801         if(index<0 || index >= (int)getImpl()->colorspaces_.size())
802         {
803             return ColorSpaceRcPtr();
804         }
805 
806         return getImpl()->colorspaces_[index];
807     }
808 
809     int Config::getIndexForColorSpace(const char * name) const
810     {
811         int csindex = -1;
812 
813         // Check to see if the name is a color space
814         if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, name) )
815         {
816             return csindex;
817         }
818 
819         // Check to see if the name is a role
820         const char* csname = LookupRole(getImpl()->roles_, name);
821         if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
822         {
823             return csindex;
824         }
825 
826         // Is a default role defined?
827         // (And, are we allowed to use it)
828         if(!getImpl()->strictParsing_)
829         {
830             csname = LookupRole(getImpl()->roles_, ROLE_DEFAULT);
831             if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
832             {
833                 return csindex;
834             }
835         }
836 
837         return -1;
838     }
839 
840     void Config::addColorSpace(const ConstColorSpaceRcPtr & original)
841     {
842         ColorSpaceRcPtr cs = original->createEditableCopy();
843 
844         std::string name = cs->getName();
845         if(name.empty())
846             throw Exception("Cannot addColorSpace with an empty name.");
847 
848         // Check to see if the colorspace already exists
849         int csindex = -1;
850         if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, name) )
851         {
852             getImpl()->colorspaces_[csindex] = cs;
853         }
854         else
855         {
856             // Otherwise, add it
857             getImpl()->colorspaces_.push_back( cs );
858         }
859 
860         AutoMutex lock(getImpl()->cacheidMutex_);
861         getImpl()->resetCacheIDs();
862     }
863 
864     void Config::clearColorSpaces()
865     {
866         getImpl()->colorspaces_.clear();
867     }
868 
869 
870 
871 
872 
873 
874     const char * Config::parseColorSpaceFromString(const char * str) const
875     {
876         if(!str) return "";
877 
878         // Search the entire filePath, including directory name (if provided)
879         // convert the filename to lowercase.
880         std::string fullstr = pystring::lower(std::string(str));
881 
882         // See if it matches a lut name.
883         // This is the position of the RIGHT end of the colorspace substring, not the left
884         int rightMostColorPos=-1;
885         std::string rightMostColorspace = "";
886         int rightMostColorSpaceIndex = -1;
887 
888         // Find the right-most occcurance within the string for each colorspace.
889         for (unsigned int i=0; i<getImpl()->colorspaces_.size(); ++i)
890         {
891             std::string csname = pystring::lower(getImpl()->colorspaces_[i]->getName());
892 
893             // find right-most extension matched in filename
894             int colorspacePos = pystring::rfind(fullstr, csname);
895             if(colorspacePos < 0)
896                 continue;
897 
898             // If we have found a match, move the pointer over to the right end of the substring
899             // This will allow us to find the longest name that matches the rightmost colorspace
900             colorspacePos += (int)csname.size();
901 
902             if ( (colorspacePos > rightMostColorPos) ||
903                  ((colorspacePos == rightMostColorPos) && (csname.size() > rightMostColorspace.size()))
904                 )
905             {
906                 rightMostColorPos = colorspacePos;
907                 rightMostColorspace = csname;
908                 rightMostColorSpaceIndex = i;
909             }
910         }
911 
912         if(rightMostColorSpaceIndex>=0)
913         {
914             return getImpl()->colorspaces_[rightMostColorSpaceIndex]->getName();
915         }
916 
917         if(!getImpl()->strictParsing_)
918         {
919             // Is a default role defined?
920             const char* csname = LookupRole(getImpl()->roles_, ROLE_DEFAULT);
921             if(csname && *csname)
922             {
923                 int csindex = -1;
924                 if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
925                 {
926                     // This is necessary to not return a reference to
927                     // a local variable.
928                     return getImpl()->colorspaces_[csindex]->getName();
929                 }
930             }
931         }
932 
933         return "";
934     }
935 
936     bool Config::isStrictParsingEnabled() const
937     {
938         return getImpl()->strictParsing_;
939     }
940 
941     void Config::setStrictParsingEnabled(bool enabled)
942     {
943         getImpl()->strictParsing_ = enabled;
944 
945         AutoMutex lock(getImpl()->cacheidMutex_);
946         getImpl()->resetCacheIDs();
947     }
948 
949     // Roles
950     void Config::setRole(const char * role, const char * colorSpaceName)
951     {
952         // Set the role
953         if(colorSpaceName)
954         {
955             getImpl()->roles_[pystring::lower(role)] = std::string(colorSpaceName);
956         }
957         // Unset the role
958         else
959         {
960             StringMap::iterator iter = getImpl()->roles_.find(pystring::lower(role));
961             if(iter != getImpl()->roles_.end())
962             {
963                 getImpl()->roles_.erase(iter);
964             }
965         }
966 
967         AutoMutex lock(getImpl()->cacheidMutex_);
968         getImpl()->resetCacheIDs();
969     }
970 
971     int Config::getNumRoles() const
972     {
973         return static_cast<int>(getImpl()->roles_.size());
974     }
975 
976     bool Config::hasRole(const char * role) const
977     {
978         const char* rname = LookupRole(getImpl()->roles_, role);
979         return  rname && *rname;
980     }
981 
982     const char * Config::getRoleName(int index) const
983     {
984         if(index < 0 || index >= (int)getImpl()->roles_.size()) return "";
985         StringMap::const_iterator iter = getImpl()->roles_.begin();
986         for(int i = 0; i < index; ++i) ++iter;
987         return iter->first.c_str();
988     }
989 
990     ///////////////////////////////////////////////////////////////////////////
991     //
992     // Display/View Registration
993 
994 
995     const char * Config::getDefaultDisplay() const
996     {
997         if(getImpl()->displayCache_.empty())
998         {
999             ComputeDisplays(getImpl()->displayCache_,
1000                             getImpl()->displays_,
1001                             getImpl()->activeDisplays_,
1002                             getImpl()->activeDisplaysEnvOverride_);
1003         }
1004 
1005         int index = -1;
1006 
1007         if(!getImpl()->activeDisplaysEnvOverride_.empty())
1008         {
1009             StringVec orderedDisplays = IntersectStringVecsCaseIgnore(getImpl()->activeDisplaysEnvOverride_,
1010                                                            getImpl()->displayCache_);
1011             if(!orderedDisplays.empty())
1012             {
1013                 index = FindInStringVecCaseIgnore(getImpl()->displayCache_, orderedDisplays[0]);
1014             }
1015         }
1016         else if(!getImpl()->activeDisplays_.empty())
1017         {
1018             StringVec orderedDisplays = IntersectStringVecsCaseIgnore(getImpl()->activeDisplays_,
1019                                                            getImpl()->displayCache_);
1020             if(!orderedDisplays.empty())
1021             {
1022                 index = FindInStringVecCaseIgnore(getImpl()->displayCache_, orderedDisplays[0]);
1023             }
1024         }
1025 
1026         if(index >= 0)
1027         {
1028             return getImpl()->displayCache_[index].c_str();
1029         }
1030 
1031         if(!getImpl()->displayCache_.empty())
1032         {
1033             return getImpl()->displayCache_[0].c_str();
1034         }
1035 
1036         return "";
1037     }
1038 
1039 
1040     int Config::getNumDisplays() const
1041     {
1042         if(getImpl()->displayCache_.empty())
1043         {
1044             ComputeDisplays(getImpl()->displayCache_,
1045                             getImpl()->displays_,
1046                             getImpl()->activeDisplays_,
1047                             getImpl()->activeDisplaysEnvOverride_);
1048         }
1049 
1050         return static_cast<int>(getImpl()->displayCache_.size());
1051     }
1052 
1053     const char * Config::getDisplay(int index) const
1054     {
1055         if(getImpl()->displayCache_.empty())
1056         {
1057             ComputeDisplays(getImpl()->displayCache_,
1058                             getImpl()->displays_,
1059                             getImpl()->activeDisplays_,
1060                             getImpl()->activeDisplaysEnvOverride_);
1061         }
1062 
1063         if(index>=0 || index < static_cast<int>(getImpl()->displayCache_.size()))
1064         {
1065             return getImpl()->displayCache_[index].c_str();
1066         }
1067 
1068         return "";
1069     }
1070 
1071     const char * Config::getDefaultView(const char * display) const
1072     {
1073         if(getImpl()->displayCache_.empty())
1074         {
1075             ComputeDisplays(getImpl()->displayCache_,
1076                             getImpl()->displays_,
1077                             getImpl()->activeDisplays_,
1078                             getImpl()->activeDisplaysEnvOverride_);
1079         }
1080 
1081         if(!display) return "";
1082 
1083         DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
1084         if(iter == getImpl()->displays_.end()) return "";
1085 
1086         const ViewVec & views = iter->second;
1087 
1088         StringVec masterViews;
1089         for(unsigned int i=0; i<views.size(); ++i)
1090         {
1091             masterViews.push_back(views[i].name);
1092         }
1093 
1094         int index = -1;
1095 
1096         if(!getImpl()->activeViewsEnvOverride_.empty())
1097         {
1098             StringVec orderedViews = IntersectStringVecsCaseIgnore(getImpl()->activeViewsEnvOverride_,
1099                                                            masterViews);
1100             if(!orderedViews.empty())
1101             {
1102                 index = FindInStringVecCaseIgnore(masterViews, orderedViews[0]);
1103             }
1104         }
1105         else if(!getImpl()->activeViews_.empty())
1106         {
1107             StringVec orderedViews = IntersectStringVecsCaseIgnore(getImpl()->activeViews_,
1108                                                            masterViews);
1109             if(!orderedViews.empty())
1110             {
1111                 index = FindInStringVecCaseIgnore(masterViews, orderedViews[0]);
1112             }
1113         }
1114 
1115         if(index >= 0)
1116         {
1117             return views[index].name.c_str();
1118         }
1119 
1120         if(!views.empty())
1121         {
1122             return views[0].name.c_str();
1123         }
1124 
1125         return "";
1126     }
1127 
1128     int Config::getNumViews(const char * display) const
1129     {
1130         if(getImpl()->displayCache_.empty())
1131         {
1132             ComputeDisplays(getImpl()->displayCache_,
1133                             getImpl()->displays_,
1134                             getImpl()->activeDisplays_,
1135                             getImpl()->activeDisplaysEnvOverride_);
1136         }
1137 
1138         if(!display) return 0;
1139 
1140         DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
1141         if(iter == getImpl()->displays_.end()) return 0;
1142 
1143         const ViewVec & views = iter->second;
1144         return static_cast<int>(views.size());
1145     }
1146 
1147     const char * Config::getView(const char * display, int index) const
1148     {
1149         if(getImpl()->displayCache_.empty())
1150         {
1151             ComputeDisplays(getImpl()->displayCache_,
1152                             getImpl()->displays_,
1153                             getImpl()->activeDisplays_,
1154                             getImpl()->activeDisplaysEnvOverride_);
1155         }
1156 
1157         if(!display) return "";
1158 
1159         DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
1160         if(iter == getImpl()->displays_.end()) return "";
1161 
1162         const ViewVec & views = iter->second;
1163         return views[index].name.c_str();
1164     }
1165 
1166     const char * Config::getDisplayColorSpaceName(const char * display, const char * view) const
1167     {
1168         if(!display || !view) return "";
1169 
1170         DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
1171         if(iter == getImpl()->displays_.end()) return "";
1172 
1173         const ViewVec & views = iter->second;
1174         int index = find_view(views, view);
1175         if(index<0) return "";
1176 
1177         return views[index].colorspace.c_str();
1178     }
1179 
1180     const char * Config::getDisplayLooks(const char * display, const char * view) const
1181     {
1182         if(!display || !view) return "";
1183 
1184         DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
1185         if(iter == getImpl()->displays_.end()) return "";
1186 
1187         const ViewVec & views = iter->second;
1188         int index = find_view(views, view);
1189         if(index<0) return "";
1190 
1191         return views[index].looks.c_str();
1192     }
1193 
1194     void Config::addDisplay(const char * display, const char * view,
1195                             const char * colorSpaceName, const char * lookName)
1196     {
1197 
1198         if(!display || !view || !colorSpaceName || !lookName) return;
1199 
1200         AddDisplay(getImpl()->displays_,
1201                    display, view, colorSpaceName, lookName);
1202         getImpl()->displayCache_.clear();
1203 
1204         AutoMutex lock(getImpl()->cacheidMutex_);
1205         getImpl()->resetCacheIDs();
1206     }
1207 
1208     void Config::clearDisplays()
1209     {
1210         getImpl()->displays_.clear();
1211         getImpl()->displayCache_.clear();
1212 
1213         AutoMutex lock(getImpl()->cacheidMutex_);
1214         getImpl()->resetCacheIDs();
1215     }
1216 
1217     void Config::setActiveDisplays(const char * displays)
1218     {
1219         getImpl()->activeDisplays_.clear();
1220         SplitStringEnvStyle(getImpl()->activeDisplays_, displays);
1221 
1222         getImpl()->displayCache_.clear();
1223 
1224         AutoMutex lock(getImpl()->cacheidMutex_);
1225         getImpl()->resetCacheIDs();
1226     }
1227 
1228     const char * Config::getActiveDisplays() const
1229     {
1230         getImpl()->activeDisplaysStr_ = JoinStringEnvStyle(getImpl()->activeDisplays_);
1231         return getImpl()->activeDisplaysStr_.c_str();
1232     }
1233 
1234     void Config::setActiveViews(const char * views)
1235     {
1236         getImpl()->activeViews_.clear();
1237         SplitStringEnvStyle(getImpl()->activeViews_, views);
1238 
1239         getImpl()->displayCache_.clear();
1240 
1241         AutoMutex lock(getImpl()->cacheidMutex_);
1242         getImpl()->resetCacheIDs();
1243     }
1244 
1245     const char * Config::getActiveViews() const
1246     {
1247         getImpl()->activeViewsStr_ = JoinStringEnvStyle(getImpl()->activeViews_);
1248         return getImpl()->activeViewsStr_.c_str();
1249     }
1250 
1251     ///////////////////////////////////////////////////////////////////////////
1252 
1253 
1254     void Config::getDefaultLumaCoefs(float * c3) const
1255     {
1256         memcpy(c3, &getImpl()->defaultLumaCoefs_[0], 3*sizeof(float));
1257     }
1258 
1259     void Config::setDefaultLumaCoefs(const float * c3)
1260     {
1261         memcpy(&getImpl()->defaultLumaCoefs_[0], c3, 3*sizeof(float));
1262 
1263         AutoMutex lock(getImpl()->cacheidMutex_);
1264         getImpl()->resetCacheIDs();
1265     }
1266 
1267 
1268 
1269 
1270     ///////////////////////////////////////////////////////////////////////////
1271 
1272 
1273 
1274 
1275     ConstLookRcPtr Config::getLook(const char * name) const
1276     {
1277         std::string namelower = pystring::lower(name);
1278 
1279         for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
1280         {
1281             if(pystring::lower(getImpl()->looksList_[i]->getName()) == namelower)
1282             {
1283                 return getImpl()->looksList_[i];
1284             }
1285         }
1286 
1287         return ConstLookRcPtr();
1288     }
1289 
1290     int Config::getNumLooks() const
1291     {
1292         return static_cast<int>(getImpl()->looksList_.size());
1293     }
1294 
1295     const char * Config::getLookNameByIndex(int index) const
1296     {
1297         if(index<0 || index>=static_cast<int>(getImpl()->looksList_.size()))
1298         {
1299             return "";
1300         }
1301 
1302         return getImpl()->looksList_[index]->getName();
1303     }
1304 
1305     void Config::addLook(const ConstLookRcPtr & look)
1306     {
1307         std::string name = look->getName();
1308         if(name.empty())
1309             throw Exception("Cannot addLook with an empty name.");
1310 
1311         std::string namelower = pystring::lower(name);
1312 
1313         // If the look exists, replace it
1314         for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
1315         {
1316             if(pystring::lower(getImpl()->looksList_[i]->getName()) == namelower)
1317             {
1318                 getImpl()->looksList_[i] = look->createEditableCopy();
1319                 return;
1320             }
1321         }
1322 
1323         // Otherwise, add it
1324         getImpl()->looksList_.push_back(look->createEditableCopy());
1325 
1326         AutoMutex lock(getImpl()->cacheidMutex_);
1327         getImpl()->resetCacheIDs();
1328     }
1329 
1330     void Config::clearLooks()
1331     {
1332         getImpl()->looksList_.clear();
1333 
1334         AutoMutex lock(getImpl()->cacheidMutex_);
1335         getImpl()->resetCacheIDs();
1336     }
1337 
1338     ///////////////////////////////////////////////////////////////////////////
1339 
1340 
1341 
1342     ConstProcessorRcPtr Config::getProcessor(const ConstColorSpaceRcPtr & src,
1343                                              const ConstColorSpaceRcPtr & dst) const
1344     {
1345         ConstContextRcPtr context = getCurrentContext();
1346         return getProcessor(context, src, dst);
1347     }
1348 
1349     ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
1350                                              const ConstColorSpaceRcPtr & src,
1351                                              const ConstColorSpaceRcPtr & dst) const
1352     {
1353         if(!src)
1354         {
1355             throw Exception("Config::GetProcessor failed. Source colorspace is null.");
1356         }
1357         if(!dst)
1358         {
1359             throw Exception("Config::GetProcessor failed. Destination colorspace is null.");
1360         }
1361 
1362         ProcessorRcPtr processor = Processor::Create();
1363         processor->getImpl()->addColorSpaceConversion(*this, context, src, dst);
1364         processor->getImpl()->finalize();
1365         return processor;
1366     }
1367 
1368     ConstProcessorRcPtr Config::getProcessor(const char * srcName,
1369                                              const char * dstName) const
1370     {
1371         ConstContextRcPtr context = getCurrentContext();
1372         return getProcessor(context, srcName, dstName);
1373     }
1374 
1375     //! Names can be colorspace name or role name
1376     ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
1377                                              const char * srcName,
1378                                              const char * dstName) const
1379     {
1380         ConstColorSpaceRcPtr src = getColorSpace(srcName);
1381         if(!src)
1382         {
1383             std::ostringstream os;
1384             os << "Could not find colorspace '" << srcName << "'.";
1385             throw Exception(os.str().c_str());
1386         }
1387 
1388         ConstColorSpaceRcPtr dst = getColorSpace(dstName);
1389         if(!dst)
1390         {
1391             std::ostringstream os;
1392             os << "Could not find colorspace '" << dstName << "'.";
1393             throw Exception(os.str().c_str());
1394         }
1395 
1396         return getProcessor(context, src, dst);
1397     }
1398 
1399 
1400     ConstProcessorRcPtr Config::getProcessor(const ConstTransformRcPtr& transform) const
1401     {
1402         return getProcessor(transform, TRANSFORM_DIR_FORWARD);
1403     }
1404 
1405 
1406     ConstProcessorRcPtr Config::getProcessor(const ConstTransformRcPtr& transform,
1407                                              TransformDirection direction) const
1408     {
1409         ConstContextRcPtr context = getCurrentContext();
1410         return getProcessor(context, transform, direction);
1411     }
1412 
1413     ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
1414                                              const ConstTransformRcPtr& transform,
1415                                              TransformDirection direction) const
1416     {
1417         ProcessorRcPtr processor = Processor::Create();
1418         processor->getImpl()->addTransform(*this, context, transform, direction);
1419         processor->getImpl()->finalize();
1420         return processor;
1421     }
1422 
1423     std::ostream& operator<< (std::ostream& os, const Config& config)
1424     {
1425         config.serialize(os);
1426         return os;
1427     }
1428 
1429     ///////////////////////////////////////////////////////////////////////////
1430     //  CacheID
1431 
1432     const char * Config::getCacheID() const
1433     {
1434         return getCacheID(getCurrentContext());
1435     }
1436 
1437     const char * Config::getCacheID(const ConstContextRcPtr & context) const
1438     {
1439         AutoMutex lock(getImpl()->cacheidMutex_);
1440 
1441         // A null context will use the empty cacheid
1442         std::string contextcacheid = "";
1443         if(context) contextcacheid = context->getCacheID();
1444 
1445         StringMap::const_iterator cacheiditer = getImpl()->cacheids_.find(contextcacheid);
1446         if(cacheiditer != getImpl()->cacheids_.end())
1447         {
1448             return cacheiditer->second.c_str();
1449         }
1450 
1451         // Include the hash of the yaml config serialization
1452         if(getImpl()->cacheidnocontext_.empty())
1453         {
1454             std::stringstream cacheid;
1455             serialize(cacheid);
1456             std::string fullstr = cacheid.str();
1457             getImpl()->cacheidnocontext_ = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
1458         }
1459 
1460         // Also include all file references, using the context (if specified)
1461         std::string fileReferencesFashHash = "";
1462         if(context)
1463         {
1464             std::ostringstream filehash;
1465 
1466             ConstTransformVec allTransforms;
1467             getImpl()->getAllIntenalTransforms(allTransforms);
1468 
1469             std::set<std::string> files;
1470             for(unsigned int i=0; i<allTransforms.size(); ++i)
1471             {
1472                 GetFileReferences(files, allTransforms[i]);
1473             }
1474 
1475             for(std::set<std::string>::iterator iter = files.begin();
1476                 iter != files.end(); ++iter)
1477             {
1478                 if(iter->empty()) continue;
1479                 filehash << *iter << "=";
1480 
1481                 try
1482                 {
1483                     std::string resolvedLocation = context->resolveFileLocation(iter->c_str());
1484                     filehash << GetFastFileHash(resolvedLocation) << " ";
1485                 }
1486                 catch(...)
1487                 {
1488                     filehash << "? ";
1489                     continue;
1490                 }
1491             }
1492 
1493             std::string fullstr = filehash.str();
1494             fileReferencesFashHash = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
1495         }
1496 
1497         getImpl()->cacheids_[contextcacheid] = getImpl()->cacheidnocontext_ + ":" + fileReferencesFashHash;
1498         return getImpl()->cacheids_[contextcacheid].c_str();
1499     }
1500 
1501 
1502     ///////////////////////////////////////////////////////////////////////////
1503     //  Serialization
1504 
1505     void Config::serialize(std::ostream& os) const
1506     {
1507         try
1508         {
1509             getImpl()->io_.write(os, this);
1510         }
1511         catch( const std::exception & e)
1512         {
1513             std::ostringstream error;
1514             error << "Error building YAML: " << e.what();
1515             throw Exception(error.str().c_str());
1516         }
1517     }
1518 
1519     void Config::Impl::resetCacheIDs()
1520     {
1521         cacheids_.clear();
1522         cacheidnocontext_ = "";
1523         sanity_ = SANITY_UNKNOWN;
1524         sanitytext_ = "";
1525     }
1526 
1527     void Config::Impl::getAllIntenalTransforms(ConstTransformVec & transformVec) const
1528     {
1529         // Grab all transforms from the ColorSpaces
1530         for(unsigned int i=0; i<colorspaces_.size(); ++i)
1531         {
1532             if(colorspaces_[i]->getTransform(COLORSPACE_DIR_TO_REFERENCE))
1533                 transformVec.push_back(colorspaces_[i]->getTransform(COLORSPACE_DIR_TO_REFERENCE));
1534             if(colorspaces_[i]->getTransform(COLORSPACE_DIR_FROM_REFERENCE))
1535                 transformVec.push_back(colorspaces_[i]->getTransform(COLORSPACE_DIR_FROM_REFERENCE));
1536         }
1537 
1538         // Grab all transforms from the Looks
1539         for(unsigned int i=0; i<looksList_.size(); ++i)
1540         {
1541             if(looksList_[i]->getTransform())
1542                 transformVec.push_back(looksList_[i]->getTransform());
1543             if(looksList_[i]->getInverseTransform())
1544                 transformVec.push_back(looksList_[i]->getInverseTransform());
1545         }
1546 
1547     }
1548 }
1549 OCIO_NAMESPACE_EXIT
1550 
1551 ///////////////////////////////////////////////////////////////////////////////
1552 
1553 #ifdef OCIO_UNIT_TEST
1554 
1555 namespace OCIO = OCIO_NAMESPACE;
1556 #include "UnitTest.h"
1557 
1558 #include <sys/stat.h>
1559 #include "pystring/pystring.h"
1560 
1561 #if 0
1562 OIIO_ADD_TEST(Config, test_searchpath_filesystem)
1563 {
1564 
1565     OCIO::EnvMap env = OCIO::GetEnvMap();
1566     std::string OCIO_TEST_AREA("$OCIO_TEST_AREA");
1567     EnvExpand(&OCIO_TEST_AREA, &env);
1568 
1569     OCIO::ConfigRcPtr config = OCIO::Config::Create();
1570 
1571     // basic get/set/expand
1572     config->setSearchPath("."
1573                           ":$OCIO_TEST1"
1574                           ":/$OCIO_JOB/${OCIO_SEQ}/$OCIO_SHOT/ocio");
1575 
1576     OIIO_CHECK_ASSERT(strcmp(config->getSearchPath(),
1577         ".:$OCIO_TEST1:/$OCIO_JOB/${OCIO_SEQ}/$OCIO_SHOT/ocio") == 0);
1578     OIIO_CHECK_ASSERT(strcmp(config->getSearchPath(true),
1579         ".:foobar:/meatballs/cheesecake/mb-cc-001/ocio") == 0);
1580 
1581     // find some files
1582     config->setSearchPath(".."
1583                           ":$OCIO_TEST1"
1584                           ":${OCIO_TEST_AREA}/test_search/one"
1585                           ":$OCIO_TEST_AREA/test_search/two");
1586 
1587     // setup for search test
1588     std::string base_dir("$OCIO_TEST_AREA/test_search/");
1589     EnvExpand(&base_dir, &env);
1590     mkdir(base_dir.c_str(), 0777);
1591 
1592     std::string one_dir("$OCIO_TEST_AREA/test_search/one/");
1593     EnvExpand(&one_dir, &env);
1594     mkdir(one_dir.c_str(), 0777);
1595 
1596     std::string two_dir("$OCIO_TEST_AREA/test_search/two/");
1597     EnvExpand(&two_dir, &env);
1598     mkdir(two_dir.c_str(), 0777);
1599 
1600     std::string lut1(one_dir+"somelut1.lut");
1601     std::ofstream somelut1(lut1.c_str());
1602     somelut1.close();
1603 
1604     std::string lut2(two_dir+"somelut2.lut");
1605     std::ofstream somelut2(lut2.c_str());
1606     somelut2.close();
1607 
1608     std::string lut3(two_dir+"somelut3.lut");
1609     std::ofstream somelut3(lut3.c_str());
1610     somelut3.close();
1611 
1612     std::string lutdotdot(OCIO_TEST_AREA+"/lutdotdot.lut");
1613     std::ofstream somelutdotdot(lutdotdot.c_str());
1614     somelutdotdot.close();
1615 
1616     // basic search test
1617     OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut1.lut"),
1618         lut1.c_str()) == 0);
1619     OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut2.lut"),
1620         lut2.c_str()) == 0);
1621     OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut3.lut"),
1622         lut3.c_str()) == 0);
1623     OIIO_CHECK_ASSERT(strcmp(config->findFile("lutdotdot.lut"),
1624         lutdotdot.c_str()) == 0);
1625 
1626 }
1627 #endif
1628 
OIIO_ADD_TEST(Config,InternalRawProfile)1629 OIIO_ADD_TEST(Config, InternalRawProfile)
1630 {
1631     std::istringstream is;
1632     is.str(OCIO::INTERNAL_RAW_PROFILE);
1633     OIIO_CHECK_NO_THROW(OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromStream(is));
1634 }
1635 
OIIO_ADD_TEST(Config,SimpleConfig)1636 OIIO_ADD_TEST(Config, SimpleConfig)
1637 {
1638 
1639     std::string SIMPLE_PROFILE =
1640     "ocio_profile_version: 1\n"
1641     "resource_path: luts\n"
1642     "strictparsing: false\n"
1643     "luma: [0.2126, 0.7152, 0.0722]\n"
1644     "roles:\n"
1645     "  compositing_log: lgh\n"
1646     "  default: raw\n"
1647     "  scene_linear: lnh\n"
1648     "displays:\n"
1649     "  sRGB:\n"
1650     "  - !<View> {name: Film1D, colorspace: vd8}\n"
1651     "  - !<View> {name: Log, colorspace: lg10}\n"
1652     "  - !<View> {name: Raw, colorspace: raw}\n"
1653     "colorspaces:\n"
1654     "  - !<ColorSpace>\n"
1655     "      name: raw\n"
1656     "      family: raw\n"
1657     "      equalitygroup: \n"
1658     "      bitdepth: 32f\n"
1659     "      description: |\n"
1660     "        A raw color space. Conversions to and from this space are no-ops.\n"
1661     "      isdata: true\n"
1662     "      allocation: uniform\n"
1663     "  - !<ColorSpace>\n"
1664     "      name: lnh\n"
1665     "      family: ln\n"
1666     "      equalitygroup: \n"
1667     "      bitdepth: 16f\n"
1668     "      description: |\n"
1669     "        The show reference space. This is a sensor referred linear\n"
1670     "        representation of the scene with primaries that correspond to\n"
1671     "        scanned film. 0.18 in this space corresponds to a properly\n"
1672     "        exposed 18% grey card.\n"
1673     "      isdata: false\n"
1674     "      allocation: lg2\n"
1675     "  - !<ColorSpace>\n"
1676     "      name: loads_of_transforms\n"
1677     "      family: vd8\n"
1678     "      equalitygroup: \n"
1679     "      bitdepth: 8ui\n"
1680     "      description: 'how many transforms can we use?'\n"
1681     "      isdata: false\n"
1682     "      allocation: uniform\n"
1683     "      to_reference: !<GroupTransform>\n"
1684     "        direction: forward\n"
1685     "        children:\n"
1686     "          - !<FileTransform>\n"
1687     "            src: diffusemult.spimtx\n"
1688     "            interpolation: unknown\n"
1689     "          - !<ColorSpaceTransform>\n"
1690     "            src: vd8\n"
1691     "            dst: lnh\n"
1692     "          - !<ExponentTransform>\n"
1693     "            value: [2.2, 2.2, 2.2, 1]\n"
1694     "          - !<MatrixTransform>\n"
1695     "            matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]\n"
1696     "            offset: [0, 0, 0, 0]\n"
1697     "          - !<CDLTransform>\n"
1698     "            slope: [1, 1, 1]\n"
1699     "            offset: [0, 0, 0]\n"
1700     "            power: [1, 1, 1]\n"
1701     "            saturation: 1\n"
1702     "\n";
1703 
1704     std::istringstream is;
1705     is.str(SIMPLE_PROFILE);
1706     OCIO::ConstConfigRcPtr config;
1707     OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
1708 }
1709 
OIIO_ADD_TEST(Config,Roles)1710 OIIO_ADD_TEST(Config, Roles)
1711 {
1712 
1713     std::string SIMPLE_PROFILE =
1714     "ocio_profile_version: 1\n"
1715     "strictparsing: false\n"
1716     "roles:\n"
1717     "  compositing_log: lgh\n"
1718     "  default: raw\n"
1719     "  scene_linear: lnh\n"
1720     "colorspaces:\n"
1721     "  - !<ColorSpace>\n"
1722     "      name: raw\n"
1723     "  - !<ColorSpace>\n"
1724     "      name: lnh\n"
1725     "  - !<ColorSpace>\n"
1726     "      name: lgh\n"
1727     "\n";
1728 
1729     std::istringstream is;
1730     is.str(SIMPLE_PROFILE);
1731     OCIO::ConstConfigRcPtr config;
1732     OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
1733 
1734     OIIO_CHECK_EQUAL(config->getNumRoles(), 3);
1735 
1736     OIIO_CHECK_ASSERT(config->hasRole("compositing_log") == true);
1737     OIIO_CHECK_ASSERT(config->hasRole("cheese") == false);
1738     OIIO_CHECK_ASSERT(config->hasRole("") == false);
1739 
1740     OIIO_CHECK_ASSERT(strcmp(config->getRoleName(2), "scene_linear") == 0);
1741     OIIO_CHECK_ASSERT(strcmp(config->getRoleName(0), "compositing_log") == 0);
1742     OIIO_CHECK_ASSERT(strcmp(config->getRoleName(1), "default") == 0);
1743     OIIO_CHECK_ASSERT(strcmp(config->getRoleName(10), "") == 0);
1744     OIIO_CHECK_ASSERT(strcmp(config->getRoleName(-4), "") == 0);
1745 
1746 }
1747 
OIIO_ADD_TEST(Config,Serialize)1748 OIIO_ADD_TEST(Config, Serialize)
1749 {
1750 
1751     OCIO::ConfigRcPtr config = OCIO::Config::Create();
1752     {
1753         OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
1754         cs->setName("testing");
1755         cs->setFamily("test");
1756         OCIO::FileTransformRcPtr transform1 = \
1757             OCIO::FileTransform::Create();
1758         OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create();
1759         groupTransform->push_back(transform1);
1760         cs->setTransform(groupTransform, OCIO::COLORSPACE_DIR_TO_REFERENCE);
1761         config->addColorSpace(cs);
1762         config->setRole( OCIO::ROLE_COMPOSITING_LOG, cs->getName() );
1763     }
1764     {
1765         OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
1766         cs->setName("testing2");
1767         cs->setFamily("test");
1768         OCIO::ExponentTransformRcPtr transform1 = \
1769             OCIO::ExponentTransform::Create();
1770         OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create();
1771         groupTransform->push_back(transform1);
1772         cs->setTransform(groupTransform, OCIO::COLORSPACE_DIR_TO_REFERENCE);
1773         config->addColorSpace(cs);
1774         config->setRole( OCIO::ROLE_COMPOSITING_LOG, cs->getName() );
1775     }
1776 
1777     // for testing
1778     //std::ofstream outfile("/tmp/test.ocio");
1779     //config->serialize(outfile);
1780     //outfile.close();
1781 
1782     std::ostringstream os;
1783     config->serialize(os);
1784 
1785     std::string PROFILE_OUT =
1786     "ocio_profile_version: 1\n"
1787     "\n"
1788     "search_path: \"\"\n"
1789     "strictparsing: true\n"
1790     "luma: [0.2126, 0.7152, 0.0722]\n"
1791     "\n"
1792     "roles:\n"
1793     "  compositing_log: testing2\n"
1794     "\n"
1795     "displays:\n"
1796     "  {}\n"
1797     "\n"
1798     "active_displays: []\n"
1799     "active_views: []\n"
1800     "\n"
1801     "colorspaces:\n"
1802     "  - !<ColorSpace>\n"
1803     "    name: testing\n"
1804     "    family: test\n"
1805     "    equalitygroup: \"\"\n"
1806     "    bitdepth: unknown\n"
1807     "    isdata: false\n"
1808     "    allocation: uniform\n"
1809     "    to_reference: !<GroupTransform>\n"
1810     "      children:\n"
1811     "        - !<FileTransform> {src: \"\", interpolation: unknown}\n"
1812     "\n"
1813     "  - !<ColorSpace>\n"
1814     "    name: testing2\n"
1815     "    family: test\n"
1816     "    equalitygroup: \"\"\n"
1817     "    bitdepth: unknown\n"
1818     "    isdata: false\n"
1819     "    allocation: uniform\n"
1820     "    to_reference: !<GroupTransform>\n"
1821     "      children:\n"
1822     "        - !<ExponentTransform> {value: [1, 1, 1, 1]}\n";
1823 
1824     std::vector<std::string> osvec;
1825     OCIO::pystring::splitlines(os.str(), osvec);
1826     std::vector<std::string> PROFILE_OUTvec;
1827     OCIO::pystring::splitlines(PROFILE_OUT, PROFILE_OUTvec);
1828 
1829     OIIO_CHECK_EQUAL(osvec.size(), PROFILE_OUTvec.size());
1830     for(unsigned int i = 0; i < PROFILE_OUTvec.size(); ++i)
1831         OIIO_CHECK_EQUAL(osvec[i], PROFILE_OUTvec[i]);
1832 }
1833 
1834 
OIIO_ADD_TEST(Config,SanityCheck)1835 OIIO_ADD_TEST(Config, SanityCheck)
1836 {
1837     {
1838     std::string SIMPLE_PROFILE =
1839     "ocio_profile_version: 1\n"
1840     "colorspaces:\n"
1841     "  - !<ColorSpace>\n"
1842     "      name: raw\n"
1843     "  - !<ColorSpace>\n"
1844     "      name: raw\n"
1845     "strictparsing: false\n"
1846     "roles:\n"
1847     "  default: raw\n"
1848     "displays:\n"
1849     "  sRGB:\n"
1850     "  - !<View> {name: Raw, colorspace: raw}\n"
1851     "\n";
1852 
1853     std::istringstream is;
1854     is.str(SIMPLE_PROFILE);
1855     OCIO::ConstConfigRcPtr config;
1856     OIIO_CHECK_THROW(config = OCIO::Config::CreateFromStream(is), OCIO::Exception);
1857     }
1858 
1859     {
1860     std::string SIMPLE_PROFILE =
1861     "ocio_profile_version: 1\n"
1862     "colorspaces:\n"
1863     "  - !<ColorSpace>\n"
1864     "      name: raw\n"
1865     "strictparsing: false\n"
1866     "roles:\n"
1867     "  default: raw\n"
1868     "displays:\n"
1869     "  sRGB:\n"
1870     "  - !<View> {name: Raw, colorspace: raw}\n"
1871     "\n";
1872 
1873     std::istringstream is;
1874     is.str(SIMPLE_PROFILE);
1875     OCIO::ConstConfigRcPtr config;
1876     OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
1877 
1878     OIIO_CHECK_NO_THROW(config->sanityCheck());
1879     }
1880 }
1881 
1882 
OIIO_ADD_TEST(Config,EnvCheck)1883 OIIO_ADD_TEST(Config, EnvCheck)
1884 {
1885     {
1886     std::string SIMPLE_PROFILE =
1887     "ocio_profile_version: 1\n"
1888     "environment:\n"
1889     "  SHOW: super\n"
1890     "  SHOT: test\n"
1891     "  SEQ: foo\n"
1892     "  test: bar${cheese}\n"
1893     "  cheese: chedder\n"
1894     "colorspaces:\n"
1895     "  - !<ColorSpace>\n"
1896     "      name: raw\n"
1897     "strictparsing: false\n"
1898     "roles:\n"
1899     "  default: raw\n"
1900     "displays:\n"
1901     "  sRGB:\n"
1902     "  - !<View> {name: Raw, colorspace: raw}\n"
1903     "\n";
1904 
1905     std::string SIMPLE_PROFILE2 =
1906     "ocio_profile_version: 1\n"
1907     "colorspaces:\n"
1908     "  - !<ColorSpace>\n"
1909     "      name: raw\n"
1910     "strictparsing: false\n"
1911     "roles:\n"
1912     "  default: raw\n"
1913     "displays:\n"
1914     "  sRGB:\n"
1915     "  - !<View> {name: Raw, colorspace: raw}\n"
1916     "\n";
1917 
1918 
1919     std::string test("SHOW=bar");
1920     putenv((char *)test.c_str());
1921     std::string test2("TASK=lighting");
1922     putenv((char *)test2.c_str());
1923 
1924     std::istringstream is;
1925     is.str(SIMPLE_PROFILE);
1926     OCIO::ConstConfigRcPtr config;
1927     OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
1928     OIIO_CHECK_EQUAL(config->getNumEnvironmentVars(), 5);
1929     OIIO_CHECK_ASSERT(strcmp(config->getCurrentContext()->resolveStringVar("test${test}"),
1930         "testbarchedder") == 0);
1931     OIIO_CHECK_ASSERT(strcmp(config->getCurrentContext()->resolveStringVar("${SHOW}"),
1932         "bar") == 0);
1933     OIIO_CHECK_ASSERT(strcmp(config->getEnvironmentVarDefault("SHOW"), "super") == 0);
1934 
1935     OCIO::ConfigRcPtr edit = config->createEditableCopy();
1936     edit->clearEnvironmentVars();
1937     OIIO_CHECK_EQUAL(edit->getNumEnvironmentVars(), 0);
1938 
1939     edit->addEnvironmentVar("testing", "dupvar");
1940     edit->addEnvironmentVar("testing", "dupvar");
1941     edit->addEnvironmentVar("foobar", "testing");
1942     edit->addEnvironmentVar("blank", "");
1943     edit->addEnvironmentVar("dontadd", NULL);
1944     OIIO_CHECK_EQUAL(edit->getNumEnvironmentVars(), 3);
1945     edit->addEnvironmentVar("foobar", NULL); // remove
1946     OIIO_CHECK_EQUAL(edit->getNumEnvironmentVars(), 2);
1947     edit->clearEnvironmentVars();
1948 
1949     edit->addEnvironmentVar("SHOW", "super");
1950     edit->addEnvironmentVar("SHOT", "test");
1951     edit->addEnvironmentVar("SEQ", "foo");
1952     edit->addEnvironmentVar("test", "bar${cheese}");
1953     edit->addEnvironmentVar("cheese", "chedder");
1954 
1955     //Test
1956     OCIO::LoggingLevel loglevel = OCIO::GetLoggingLevel();
1957     OCIO::SetLoggingLevel(OCIO::LOGGING_LEVEL_DEBUG);
1958     is.str(SIMPLE_PROFILE2);
1959     OCIO::ConstConfigRcPtr noenv;
1960     OIIO_CHECK_NO_THROW(noenv = OCIO::Config::CreateFromStream(is));
1961     OIIO_CHECK_ASSERT(strcmp(noenv->getCurrentContext()->resolveStringVar("${TASK}"),
1962         "lighting") == 0);
1963     OCIO::SetLoggingLevel(loglevel);
1964 
1965     OIIO_CHECK_EQUAL(edit->getEnvironmentMode(), OCIO::ENV_ENVIRONMENT_LOAD_PREDEFINED);
1966     edit->setEnvironmentMode(OCIO::ENV_ENVIRONMENT_LOAD_ALL);
1967     OIIO_CHECK_EQUAL(edit->getEnvironmentMode(), OCIO::ENV_ENVIRONMENT_LOAD_ALL);
1968 
1969     }
1970 }
1971 
OIIO_ADD_TEST(Config,RoleWithoutColorSpace)1972 OIIO_ADD_TEST(Config, RoleWithoutColorSpace)
1973 {
1974     OCIO::ConfigRcPtr config = OCIO::Config::Create()->createEditableCopy();
1975     config->setRole("reference", "UnknownColorSpace");
1976 
1977     std::ostringstream os;
1978     OIIO_CHECK_THROW(config->serialize(os), OCIO::Exception);
1979 }
1980 
OIIO_ADD_TEST(Config,Env_colorspace_name)1981 OIIO_ADD_TEST(Config, Env_colorspace_name)
1982 {
1983     const std::string MY_OCIO_CONFIG =
1984         "ocio_profile_version: 1\n"
1985         "\n"
1986         "search_path: luts\n"
1987         "strictparsing: true\n"
1988         "luma: [0.2126, 0.7152, 0.0722]\n"
1989         "\n"
1990         "roles:\n"
1991         "  compositing_log: lgh\n"
1992         "  default: raw\n"
1993         "  scene_linear: lnh\n"
1994         "\n"
1995         "displays:\n"
1996         "  sRGB:\n"
1997         "    - !<View> {name: Raw, colorspace: raw}\n"
1998         "\n"
1999         "active_displays: []\n"
2000         "active_views: []\n"
2001         "\n"
2002         "colorspaces:\n"
2003         "  - !<ColorSpace>\n"
2004         "    name: raw\n"
2005         "    family: \"\"\n"
2006         "    equalitygroup: \"\"\n"
2007         "    bitdepth: unknown\n"
2008         "    isdata: false\n"
2009         "    allocation: uniform\n"
2010         "\n"
2011         "  - !<ColorSpace>\n"
2012         "    name: lnh\n"
2013         "    family: \"\"\n"
2014         "    equalitygroup: \"\"\n"
2015         "    bitdepth: unknown\n"
2016         "    isdata: false\n"
2017         "    allocation: uniform\n"
2018         "\n"
2019         "  - !<ColorSpace>\n"
2020         "    name: lgh\n"
2021         "    family: \"\"\n"
2022         "    equalitygroup: \"\"\n"
2023         "    bitdepth: unknown\n"
2024         "    isdata: false\n"
2025         "    allocation: uniform\n"
2026         "    allocationvars: [-0.125, 1.125]\n"
2027         "    from_reference: !<ColorSpaceTransform> {src: raw, dst: $CAMERARAW}\n";
2028 
2029 
2030     {
2031         // Test when the env. variable is missing
2032 
2033         std::istringstream is;
2034         is.str(MY_OCIO_CONFIG);
2035 
2036         OCIO::ConstConfigRcPtr config;
2037         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2038         OIIO_CHECK_NO_THROW(config->sanityCheck());
2039         OIIO_CHECK_THROW(config->getProcessor("raw", "lgh"), OCIO::Exception);
2040     }
2041 
2042     {
2043         char * env = (char *)"CAMERARAW=lnh";
2044         putenv(env);
2045 
2046         std::istringstream is;
2047         is.str(MY_OCIO_CONFIG);
2048 
2049         OCIO::ConstConfigRcPtr config;
2050         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2051         OIIO_CHECK_NO_THROW(config->sanityCheck());
2052         OIIO_CHECK_NO_THROW(config->getProcessor("raw", "lgh"));
2053     }
2054 
2055     {
2056         // Test when the env. variable content is wrong
2057 
2058         char * env = (char *)"CAMERARAW=FaultyColorSpaceName";
2059         putenv(env);
2060 
2061         std::istringstream is;
2062         is.str(MY_OCIO_CONFIG);
2063 
2064         OCIO::ConstConfigRcPtr config;
2065         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2066         OIIO_CHECK_NO_THROW(config->sanityCheck());
2067         OIIO_CHECK_THROW(config->getProcessor("raw", "lgh"), OCIO::Exception);
2068     }
2069 
2070     {
2071         // Check that the serialization preserves the env. variable
2072 
2073         std::istringstream is;
2074         is.str(MY_OCIO_CONFIG);
2075 
2076         OCIO::ConstConfigRcPtr config;
2077         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2078         OIIO_CHECK_NO_THROW(config->sanityCheck());
2079 
2080         std::stringstream ss;
2081         ss << *config.get();
2082         OIIO_CHECK_EQUAL(ss.str(), MY_OCIO_CONFIG);
2083     }
2084 }
2085 
OIIO_ADD_TEST(Config,display)2086 OIIO_ADD_TEST(Config, display)
2087 {
2088     static const std::string SIMPLE_PROFILE_HEADER =
2089         "ocio_profile_version: 1\n"
2090         "\n"
2091         "search_path: luts\n"
2092         "strictparsing: true\n"
2093         "luma: [0.2126, 0.7152, 0.0722]\n"
2094         "\n"
2095         "roles:\n"
2096         "  default: raw\n"
2097         "  scene_linear: lnh\n"
2098         "\n"
2099         "displays:\n"
2100         "  sRGB_1:\n"
2101         "    - !<View> {name: Raw, colorspace: raw}\n"
2102         "  sRGB_2:\n"
2103         "    - !<View> {name: Raw, colorspace: raw}\n"
2104         "  sRGB_3:\n"
2105         "    - !<View> {name: Raw, colorspace: raw}\n"
2106         "\n";
2107 
2108     static const std::string SIMPLE_PROFILE_FOOTER =
2109         "\n"
2110         "colorspaces:\n"
2111         "  - !<ColorSpace>\n"
2112         "    name: raw\n"
2113         "    family: \"\"\n"
2114         "    equalitygroup: \"\"\n"
2115         "    bitdepth: unknown\n"
2116         "    isdata: false\n"
2117         "    allocation: uniform\n"
2118         "\n"
2119         "  - !<ColorSpace>\n"
2120         "    name: lnh\n"
2121         "    family: \"\"\n"
2122         "    equalitygroup: \"\"\n"
2123         "    bitdepth: unknown\n"
2124         "    isdata: false\n"
2125         "    allocation: uniform\n";
2126 
2127     {
2128         std::string myProfile =
2129             SIMPLE_PROFILE_HEADER
2130             +
2131             "active_displays: []\n"
2132             "active_views: []\n"
2133             + SIMPLE_PROFILE_FOOTER;
2134 
2135         std::istringstream is(myProfile);
2136         OCIO::ConstConfigRcPtr config;
2137         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2138         OIIO_CHECK_EQUAL(config->getNumDisplays(), 3);
2139         OIIO_CHECK_EQUAL(std::string(config->getDisplay(0)), std::string("sRGB_1"));
2140         OIIO_CHECK_EQUAL(std::string(config->getDisplay(1)), std::string("sRGB_2"));
2141         OIIO_CHECK_EQUAL(std::string(config->getDisplay(2)), std::string("sRGB_3"));
2142         OIIO_CHECK_EQUAL(std::string(config->getDefaultDisplay()), "sRGB_1");
2143     }
2144 
2145     {
2146         std::string myProfile =
2147             SIMPLE_PROFILE_HEADER
2148             +
2149             "active_displays: [sRGB_1]\n"
2150             "active_views: []\n"
2151             + SIMPLE_PROFILE_FOOTER;
2152 
2153         std::istringstream is(myProfile);
2154         OCIO::ConstConfigRcPtr config;
2155         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2156         OIIO_CHECK_EQUAL(config->getNumDisplays(), 1);
2157         OIIO_CHECK_EQUAL(std::string(config->getDisplay(0)), std::string("sRGB_1"));
2158         OIIO_CHECK_EQUAL(std::string(config->getDefaultDisplay()), "sRGB_1");
2159     }
2160 
2161     {
2162         std::string myProfile =
2163             SIMPLE_PROFILE_HEADER
2164             +
2165             "active_displays: [sRGB_2, sRGB_1]\n"
2166             "active_views: []\n"
2167             + SIMPLE_PROFILE_FOOTER;
2168 
2169         std::istringstream is(myProfile);
2170         OCIO::ConstConfigRcPtr config;
2171         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2172         OIIO_CHECK_EQUAL(config->getNumDisplays(), 2);
2173         OIIO_CHECK_EQUAL(std::string(config->getDisplay(0)), std::string("sRGB_2"));
2174         OIIO_CHECK_EQUAL(std::string(config->getDisplay(1)), std::string("sRGB_1"));
2175         OIIO_CHECK_EQUAL(std::string(config->getDefaultDisplay()), "sRGB_2");
2176     }
2177 
2178     {
2179         std::string myProfile =
2180             SIMPLE_PROFILE_HEADER
2181             +
2182             "active_displays: []\n"
2183             "active_views: []\n"
2184             + SIMPLE_PROFILE_FOOTER;
2185 
2186         const std::string active_displays(
2187             std::string(OCIO::OCIO_ACTIVE_DISPLAYS_ENVVAR) + "= sRGB_3, sRGB_2");
2188         putenv(const_cast<char *>(active_displays.c_str()));
2189 
2190         std::istringstream is(myProfile);
2191         OCIO::ConstConfigRcPtr config;
2192         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2193         OIIO_CHECK_EQUAL(config->getNumDisplays(), 2);
2194         OIIO_CHECK_EQUAL(std::string(config->getDisplay(0)), std::string("sRGB_3"));
2195         OIIO_CHECK_EQUAL(std::string(config->getDisplay(1)), std::string("sRGB_2"));
2196         OIIO_CHECK_EQUAL(std::string(config->getDefaultDisplay()), "sRGB_3");
2197     }
2198 
2199     {
2200         std::string myProfile =
2201             SIMPLE_PROFILE_HEADER
2202             +
2203             "active_displays: [sRGB_2, sRGB_1]\n"
2204             "active_views: []\n"
2205             + SIMPLE_PROFILE_FOOTER;
2206 
2207         const std::string active_displays(
2208             std::string(OCIO::OCIO_ACTIVE_DISPLAYS_ENVVAR) + "= sRGB_3, sRGB_2");
2209         putenv(const_cast<char *>(active_displays.c_str()));
2210 
2211         std::istringstream is(myProfile);
2212         OCIO::ConstConfigRcPtr config;
2213         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2214         OIIO_CHECK_EQUAL(config->getNumDisplays(), 2);
2215         OIIO_CHECK_EQUAL(std::string(config->getDisplay(0)), std::string("sRGB_3"));
2216         OIIO_CHECK_EQUAL(std::string(config->getDisplay(1)), std::string("sRGB_2"));
2217         OIIO_CHECK_EQUAL(std::string(config->getDefaultDisplay()), "sRGB_3");
2218     }
2219 
2220     {
2221         const std::string active_displays(
2222             std::string(OCIO::OCIO_ACTIVE_DISPLAYS_ENVVAR) + "="); // No value
2223         putenv(const_cast<char *>(active_displays.c_str()));
2224 
2225         std::string myProfile =
2226             SIMPLE_PROFILE_HEADER
2227             +
2228             "active_displays: [sRGB_2, sRGB_1]\n"
2229             "active_views: []\n"
2230             + SIMPLE_PROFILE_FOOTER;
2231 
2232         std::istringstream is(myProfile);
2233         OCIO::ConstConfigRcPtr config;
2234         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2235         OIIO_CHECK_EQUAL(config->getNumDisplays(), 2);
2236         OIIO_CHECK_EQUAL(std::string(config->getDisplay(0)), std::string("sRGB_2"));
2237         OIIO_CHECK_EQUAL(std::string(config->getDisplay(1)), std::string("sRGB_1"));
2238         OIIO_CHECK_EQUAL(std::string(config->getDefaultDisplay()), "sRGB_2");
2239     }
2240 
2241     {
2242         const std::string active_displays(
2243             std::string(OCIO::OCIO_ACTIVE_DISPLAYS_ENVVAR) + "= "); // No value, but misleading space
2244         putenv(const_cast<char *>(active_displays.c_str()));
2245 
2246         std::string myProfile =
2247             SIMPLE_PROFILE_HEADER
2248             +
2249             "active_displays: [sRGB_2, sRGB_1]\n"
2250             "active_views: []\n"
2251             + SIMPLE_PROFILE_FOOTER;
2252 
2253         std::istringstream is(myProfile);
2254         OCIO::ConstConfigRcPtr config;
2255         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2256         OIIO_CHECK_EQUAL(config->getNumDisplays(), 2);
2257         OIIO_CHECK_EQUAL(std::string(config->getDisplay(0)), std::string("sRGB_2"));
2258         OIIO_CHECK_EQUAL(std::string(config->getDisplay(1)), std::string("sRGB_1"));
2259         OIIO_CHECK_EQUAL(std::string(config->getDefaultDisplay()), "sRGB_2");
2260     }
2261 }
2262 
OIIO_ADD_TEST(Config,view)2263 OIIO_ADD_TEST(Config, view)
2264 {
2265     static const std::string SIMPLE_PROFILE_HEADER =
2266         "ocio_profile_version: 1\n"
2267         "\n"
2268         "search_path: luts\n"
2269         "strictparsing: true\n"
2270         "luma: [0.2126, 0.7152, 0.0722]\n"
2271         "\n"
2272         "roles:\n"
2273         "  default: raw\n"
2274         "  scene_linear: lnh\n"
2275         "\n"
2276         "displays:\n"
2277         "  sRGB_1:\n"
2278         "    - !<View> {name: View_1, colorspace: raw}\n"
2279         "    - !<View> {name: View_2, colorspace: raw}\n"
2280         "  sRGB_2:\n"
2281         "    - !<View> {name: View_2, colorspace: raw}\n"
2282         "    - !<View> {name: View_3, colorspace: raw}\n"
2283         "  sRGB_3:\n"
2284         "    - !<View> {name: View_3, colorspace: raw}\n"
2285         "    - !<View> {name: View_1, colorspace: raw}\n"
2286         "\n";
2287 
2288     static const std::string SIMPLE_PROFILE_FOOTER =
2289         "\n"
2290         "colorspaces:\n"
2291         "  - !<ColorSpace>\n"
2292         "    name: raw\n"
2293         "    family: \"\"\n"
2294         "    equalitygroup: \"\"\n"
2295         "    bitdepth: unknown\n"
2296         "    isdata: false\n"
2297         "    allocation: uniform\n"
2298         "\n"
2299         "  - !<ColorSpace>\n"
2300         "    name: lnh\n"
2301         "    family: \"\"\n"
2302         "    equalitygroup: \"\"\n"
2303         "    bitdepth: unknown\n"
2304         "    isdata: false\n"
2305         "    allocation: uniform\n";
2306 
2307     {
2308         std::string myProfile =
2309             SIMPLE_PROFILE_HEADER
2310             +
2311             "active_displays: []\n"
2312             "active_views: []\n"
2313             + SIMPLE_PROFILE_FOOTER;
2314 
2315         std::istringstream is(myProfile);
2316         OCIO::ConstConfigRcPtr config;
2317         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2318         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_1")), "View_1");
2319         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 0)), "View_1");
2320         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 1)), "View_2");
2321         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_2")), "View_2");
2322         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 0)), "View_2");
2323         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 1)), "View_3");
2324         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_3")), "View_3");
2325         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 0)), "View_3");
2326         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 1)), "View_1");
2327     }
2328 
2329     {
2330         std::string myProfile =
2331             SIMPLE_PROFILE_HEADER
2332             +
2333             "active_displays: []\n"
2334             "active_views: [View_3]\n"
2335             + SIMPLE_PROFILE_FOOTER;
2336 
2337         std::istringstream is(myProfile);
2338         OCIO::ConstConfigRcPtr config;
2339         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2340         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_1")), "View_1");
2341         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 0)), "View_1");
2342         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 1)), "View_2");
2343         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_2")), "View_3");
2344         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 0)), "View_2");
2345         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 1)), "View_3");
2346         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_3")), "View_3");
2347         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 0)), "View_3");
2348         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 1)), "View_1");
2349     }
2350 
2351     {
2352         std::string myProfile =
2353             SIMPLE_PROFILE_HEADER
2354             +
2355             "active_displays: []\n"
2356             "active_views: [View_3, View_2, View_1]\n"
2357             + SIMPLE_PROFILE_FOOTER;
2358 
2359         std::istringstream is(myProfile);
2360         OCIO::ConstConfigRcPtr config;
2361         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2362         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_1")), "View_2");
2363         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 0)), "View_1");
2364         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 1)), "View_2");
2365         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_2")), "View_3");
2366         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 0)), "View_2");
2367         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 1)), "View_3");
2368         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_3")), "View_3");
2369         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 0)), "View_3");
2370         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 1)), "View_1");
2371     }
2372 
2373     {
2374         std::string myProfile =
2375             SIMPLE_PROFILE_HEADER
2376             +
2377             "active_displays: []\n"
2378             "active_views: []\n"
2379             + SIMPLE_PROFILE_FOOTER;
2380 
2381         const std::string active_displays(
2382             std::string(OCIO::OCIO_ACTIVE_VIEWS_ENVVAR) + "= View_3, View_2");
2383         putenv(const_cast<char *>(active_displays.c_str()));
2384 
2385         std::istringstream is(myProfile);
2386         OCIO::ConstConfigRcPtr config;
2387         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2388         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_1")), "View_2");
2389         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 0)), "View_1");
2390         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 1)), "View_2");
2391         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_2")), "View_3");
2392         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 0)), "View_2");
2393         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 1)), "View_3");
2394         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_3")), "View_3");
2395         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 0)), "View_3");
2396         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 1)), "View_1");
2397     }
2398 
2399     {
2400         std::string myProfile =
2401             SIMPLE_PROFILE_HEADER
2402             +
2403             "active_displays: []\n"
2404             "active_views: []\n"
2405             + SIMPLE_PROFILE_FOOTER;
2406 
2407         const std::string active_displays(
2408             std::string(OCIO::OCIO_ACTIVE_VIEWS_ENVVAR) + "="); // No value.
2409         putenv(const_cast<char *>(active_displays.c_str()));
2410 
2411         std::istringstream is(myProfile);
2412         OCIO::ConstConfigRcPtr config;
2413         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2414         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_1")), "View_1");
2415         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 0)), "View_1");
2416         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 1)), "View_2");
2417         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_2")), "View_2");
2418         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 0)), "View_2");
2419         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 1)), "View_3");
2420         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_3")), "View_3");
2421         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 0)), "View_3");
2422         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 1)), "View_1");
2423     }
2424 
2425     {
2426         std::string myProfile =
2427             SIMPLE_PROFILE_HEADER
2428             +
2429             "active_displays: []\n"
2430             "active_views: []\n"
2431             + SIMPLE_PROFILE_FOOTER;
2432 
2433         const std::string active_displays(
2434             std::string(OCIO::OCIO_ACTIVE_VIEWS_ENVVAR) + "= "); // No value, but misleading space
2435         putenv(const_cast<char *>(active_displays.c_str()));
2436 
2437         std::istringstream is(myProfile);
2438         OCIO::ConstConfigRcPtr config;
2439         OIIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is));
2440         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_1")), "View_1");
2441         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 0)), "View_1");
2442         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_1", 1)), "View_2");
2443         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_2")), "View_2");
2444         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 0)), "View_2");
2445         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_2", 1)), "View_3");
2446         OIIO_CHECK_EQUAL(std::string(config->getDefaultView("sRGB_3")), "View_3");
2447         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 0)), "View_3");
2448         OIIO_CHECK_EQUAL(std::string(config->getView("sRGB_3", 1)), "View_1");
2449     }
2450 }
2451 
2452 #endif // OCIO_UNIT_TEST
2453