PageRenderTime 60ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/core/Config.cpp

http://github.com/imageworks/OpenColorIO
C++ | 1957 lines | 1514 code | 336 blank | 107 comment | 189 complexity | 86cd1be9ee9af0728bff52be00b4832d MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*
  2. Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
  3. All Rights Reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are
  6. met:
  7. * Redistributions of source code must retain the above copyright
  8. notice, this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright
  10. notice, this list of conditions and the following disclaimer in the
  11. documentation and/or other materials provided with the distribution.
  12. * Neither the name of Sony Pictures Imageworks nor the names of its
  13. contributors may be used to endorse or promote products derived from
  14. this software without specific prior written permission.
  15. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  16. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  17. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  18. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  19. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  20. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  21. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  22. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  23. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. #include <cstdlib>
  28. #include <cstring>
  29. #include <set>
  30. #include <sstream>
  31. #include <fstream>
  32. #include <utility>
  33. #include <vector>
  34. #include <OpenColorIO/OpenColorIO.h>
  35. #include "HashUtils.h"
  36. #include "Logging.h"
  37. #include "LookParse.h"
  38. #include "Display.h"
  39. #include "MathUtils.h"
  40. #include "Mutex.h"
  41. #include "OpBuilders.h"
  42. #include "PathUtils.h"
  43. #include "ParseUtils.h"
  44. #include "Processor.h"
  45. #include "PrivateTypes.h"
  46. #include "pystring/pystring.h"
  47. #include "OCIOYaml.h"
  48. OCIO_NAMESPACE_ENTER
  49. {
  50. namespace
  51. {
  52. const char * OCIO_CONFIG_ENVVAR = "OCIO";
  53. const char * OCIO_ACTIVE_DISPLAYS_ENVVAR = "OCIO_ACTIVE_DISPLAYS";
  54. const char * OCIO_ACTIVE_VIEWS_ENVVAR = "OCIO_ACTIVE_VIEWS";
  55. enum Sanity
  56. {
  57. SANITY_UNKNOWN = 0,
  58. SANITY_SANE,
  59. SANITY_INSANE
  60. };
  61. // These are the 709 primaries specified by the ASC.
  62. const float DEFAULT_LUMA_COEFF_R = 0.2126f;
  63. const float DEFAULT_LUMA_COEFF_G = 0.7152f;
  64. const float DEFAULT_LUMA_COEFF_B = 0.0722f;
  65. const char * INTERNAL_RAW_PROFILE =
  66. "ocio_profile_version: 1\n"
  67. "strictparsing: false\n"
  68. "roles:\n"
  69. " default: raw\n"
  70. "displays:\n"
  71. " sRGB:\n"
  72. " - !<View> {name: Raw, colorspace: raw}\n"
  73. "colorspaces:\n"
  74. " - !<ColorSpace>\n"
  75. " name: raw\n"
  76. " family: raw\n"
  77. " equalitygroup:\n"
  78. " bitdepth: 32f\n"
  79. " isdata: true\n"
  80. " allocation: uniform\n"
  81. " description: 'A raw color space. Conversions to and from this space are no-ops.'\n";
  82. }
  83. ///////////////////////////////////////////////////////////////////////////
  84. const char * GetVersion()
  85. {
  86. return OCIO_VERSION;
  87. }
  88. int GetVersionHex()
  89. {
  90. return OCIO_VERSION_HEX;
  91. }
  92. namespace
  93. {
  94. ConstConfigRcPtr g_currentConfig;
  95. Mutex g_currentConfigLock;
  96. }
  97. ConstConfigRcPtr GetCurrentConfig()
  98. {
  99. AutoMutex lock(g_currentConfigLock);
  100. if(!g_currentConfig)
  101. {
  102. g_currentConfig = Config::CreateFromEnv();
  103. }
  104. return g_currentConfig;
  105. }
  106. void SetCurrentConfig(const ConstConfigRcPtr & config)
  107. {
  108. AutoMutex lock(g_currentConfigLock);
  109. g_currentConfig = config->createEditableCopy();
  110. }
  111. namespace
  112. {
  113. // Environment
  114. std::string LookupEnvironment(const StringMap & env, const std::string & name)
  115. {
  116. StringMap::const_iterator iter = env.find(name);
  117. if(iter == env.end()) return "";
  118. return iter->second;
  119. }
  120. // Roles
  121. // (lower case role name: colorspace name)
  122. std::string LookupRole(const StringMap & roles, const std::string & rolename)
  123. {
  124. StringMap::const_iterator iter = roles.find(pystring::lower(rolename));
  125. if(iter == roles.end()) return "";
  126. return iter->second;
  127. }
  128. void GetFileReferences(std::set<std::string> & files,
  129. const ConstTransformRcPtr & transform)
  130. {
  131. if(!transform) return;
  132. if(ConstGroupTransformRcPtr groupTransform = \
  133. DynamicPtrCast<const GroupTransform>(transform))
  134. {
  135. for(int i=0; i<groupTransform->size(); ++i)
  136. {
  137. GetFileReferences(files, groupTransform->getTransform(i));
  138. }
  139. }
  140. else if(ConstFileTransformRcPtr fileTransform = \
  141. DynamicPtrCast<const FileTransform>(transform))
  142. {
  143. files.insert(fileTransform->getSrc());
  144. }
  145. }
  146. void GetColorSpaceReferences(std::set<std::string> & colorSpaceNames,
  147. const ConstTransformRcPtr & transform)
  148. {
  149. if(!transform) return;
  150. if(ConstGroupTransformRcPtr groupTransform = \
  151. DynamicPtrCast<const GroupTransform>(transform))
  152. {
  153. for(int i=0; i<groupTransform->size(); ++i)
  154. {
  155. GetColorSpaceReferences(colorSpaceNames, groupTransform->getTransform(i));
  156. }
  157. }
  158. else if(ConstColorSpaceTransformRcPtr colorSpaceTransform = \
  159. DynamicPtrCast<const ColorSpaceTransform>(transform))
  160. {
  161. colorSpaceNames.insert(colorSpaceTransform->getSrc());
  162. colorSpaceNames.insert(colorSpaceTransform->getDst());
  163. }
  164. else if(ConstDisplayTransformRcPtr displayTransform = \
  165. DynamicPtrCast<const DisplayTransform>(transform))
  166. {
  167. colorSpaceNames.insert(displayTransform->getInputColorSpaceName());
  168. }
  169. else if(ConstLookTransformRcPtr lookTransform = \
  170. DynamicPtrCast<const LookTransform>(transform))
  171. {
  172. colorSpaceNames.insert(colorSpaceTransform->getSrc());
  173. colorSpaceNames.insert(colorSpaceTransform->getDst());
  174. }
  175. }
  176. bool FindColorSpaceIndex(int * index,
  177. const ColorSpaceVec & colorspaces,
  178. const std::string & csname)
  179. {
  180. if(csname.empty()) return false;
  181. std::string csnamelower = pystring::lower(csname);
  182. for(unsigned int i = 0; i < colorspaces.size(); ++i)
  183. {
  184. if(csnamelower == pystring::lower(colorspaces[i]->getName()))
  185. {
  186. if(index) *index = i;
  187. return true;
  188. }
  189. }
  190. return false;
  191. }
  192. } // namespace
  193. class Config::Impl
  194. {
  195. public:
  196. StringMap env_;
  197. ContextRcPtr context_;
  198. std::string description_;
  199. ColorSpaceVec colorspaces_;
  200. StringMap roles_;
  201. LookVec looksList_;
  202. DisplayMap displays_;
  203. StringVec activeDisplays_;
  204. StringVec activeDisplaysEnvOverride_;
  205. StringVec activeViews_;
  206. StringVec activeViewsEnvOverride_;
  207. mutable std::string activeDisplaysStr_;
  208. mutable std::string activeViewsStr_;
  209. mutable StringVec displayCache_;
  210. // Misc
  211. std::vector<float> defaultLumaCoefs_;
  212. bool strictParsing_;
  213. mutable Sanity sanity_;
  214. mutable std::string sanitytext_;
  215. mutable Mutex cacheidMutex_;
  216. mutable StringMap cacheids_;
  217. mutable std::string cacheidnocontext_;
  218. OCIOYaml io_;
  219. Impl() :
  220. context_(Context::Create()),
  221. strictParsing_(true),
  222. sanity_(SANITY_UNKNOWN)
  223. {
  224. char* activeDisplays = std::getenv(OCIO_ACTIVE_DISPLAYS_ENVVAR);
  225. SplitStringEnvStyle(activeDisplaysEnvOverride_, activeDisplays);
  226. char * activeViews = std::getenv(OCIO_ACTIVE_VIEWS_ENVVAR);
  227. SplitStringEnvStyle(activeViewsEnvOverride_, activeViews);
  228. defaultLumaCoefs_.resize(3);
  229. defaultLumaCoefs_[0] = DEFAULT_LUMA_COEFF_R;
  230. defaultLumaCoefs_[1] = DEFAULT_LUMA_COEFF_G;
  231. defaultLumaCoefs_[2] = DEFAULT_LUMA_COEFF_B;
  232. }
  233. ~Impl()
  234. {
  235. }
  236. Impl& operator= (const Impl & rhs)
  237. {
  238. env_ = rhs.env_;
  239. context_ = rhs.context_->createEditableCopy();
  240. description_ = rhs.description_;
  241. // Deep copy the colorspaces
  242. colorspaces_.clear();
  243. colorspaces_.reserve(rhs.colorspaces_.size());
  244. for(unsigned int i=0; i<rhs.colorspaces_.size(); ++i)
  245. {
  246. colorspaces_.push_back(rhs.colorspaces_[i]->createEditableCopy());
  247. }
  248. // Deep copy the looks
  249. looksList_.clear();
  250. looksList_.reserve(rhs.looksList_.size());
  251. for(unsigned int i=0; i<rhs.looksList_.size(); ++i)
  252. {
  253. looksList_.push_back(rhs.looksList_[i]->createEditableCopy());
  254. }
  255. // Assignment operator will suffice for these
  256. roles_ = rhs.roles_;
  257. displays_ = rhs.displays_;
  258. activeDisplays_ = rhs.activeDisplays_;
  259. activeViews_ = rhs.activeViews_;
  260. activeViewsEnvOverride_ = rhs.activeViewsEnvOverride_;
  261. activeDisplaysEnvOverride_ = rhs.activeDisplaysEnvOverride_;
  262. activeDisplaysStr_ = rhs.activeDisplaysStr_;
  263. displayCache_ = rhs.displayCache_;
  264. defaultLumaCoefs_ = rhs.defaultLumaCoefs_;
  265. strictParsing_ = rhs.strictParsing_;
  266. sanity_ = rhs.sanity_;
  267. sanitytext_ = rhs.sanitytext_;
  268. cacheids_ = rhs.cacheids_;
  269. cacheidnocontext_ = cacheidnocontext_;
  270. return *this;
  271. }
  272. // Any time you modify the state of the config, you must call this
  273. // to reset internal cache states. You also should do this in a
  274. // thread safe manner by acquiring the cacheidMutex_;
  275. void resetCacheIDs();
  276. // Get all internal transforms (to generate cacheIDs, validation, etc).
  277. // This currently crawls colorspaces + looks
  278. void getAllIntenalTransforms(ConstTransformVec & transformVec) const;
  279. };
  280. ///////////////////////////////////////////////////////////////////////////
  281. ConfigRcPtr Config::Create()
  282. {
  283. return ConfigRcPtr(new Config(), &deleter);
  284. }
  285. void Config::deleter(Config* c)
  286. {
  287. delete c;
  288. }
  289. ConstConfigRcPtr Config::CreateFromEnv()
  290. {
  291. char* file = std::getenv(OCIO_CONFIG_ENVVAR);
  292. if(file) return CreateFromFile(file);
  293. std::ostringstream os;
  294. os << "Color management disabled. ";
  295. os << "(Specify the $OCIO environment variable to enable.)";
  296. LogInfo(os.str());
  297. std::istringstream istream;
  298. istream.str(INTERNAL_RAW_PROFILE);
  299. ConfigRcPtr config = Config::Create();
  300. config->getImpl()->io_.open(istream, config);
  301. return config;
  302. }
  303. ConstConfigRcPtr Config::CreateFromFile(const char * filename)
  304. {
  305. std::ifstream istream(filename);
  306. if(istream.fail()) {
  307. std::ostringstream os;
  308. os << "Error could not read '" << filename;
  309. os << "' OCIO profile.";
  310. throw Exception (os.str().c_str());
  311. }
  312. ConfigRcPtr config = Config::Create();
  313. config->getImpl()->io_.open(istream, config, filename);
  314. return config;
  315. }
  316. ConstConfigRcPtr Config::CreateFromStream(std::istream & istream)
  317. {
  318. ConfigRcPtr config = Config::Create();
  319. config->getImpl()->io_.open(istream, config);
  320. return config;
  321. }
  322. ///////////////////////////////////////////////////////////////////////////
  323. Config::Config()
  324. : m_impl(new Config::Impl)
  325. {
  326. }
  327. Config::~Config()
  328. {
  329. delete m_impl;
  330. m_impl = NULL;
  331. }
  332. ConfigRcPtr Config::createEditableCopy() const
  333. {
  334. ConfigRcPtr config = Config::Create();
  335. *config->m_impl = *m_impl;
  336. return config;
  337. }
  338. void Config::sanityCheck() const
  339. {
  340. if(getImpl()->sanity_ == SANITY_SANE) return;
  341. if(getImpl()->sanity_ == SANITY_INSANE)
  342. {
  343. throw Exception(getImpl()->sanitytext_.c_str());
  344. }
  345. getImpl()->sanity_ = SANITY_INSANE;
  346. getImpl()->sanitytext_ = "";
  347. ///// COLORSPACES
  348. StringSet existingColorSpaces;
  349. // Confirm all ColorSpaces are valid
  350. for(unsigned int i=0; i<getImpl()->colorspaces_.size(); ++i)
  351. {
  352. if(!getImpl()->colorspaces_[i])
  353. {
  354. std::ostringstream os;
  355. os << "Config failed sanitycheck. ";
  356. os << "The colorspace at index " << i << " is null.";
  357. getImpl()->sanitytext_ = os.str();
  358. throw Exception(getImpl()->sanitytext_.c_str());
  359. }
  360. const char * name = getImpl()->colorspaces_[i]->getName();
  361. if(!name || strlen(name) == 0)
  362. {
  363. std::ostringstream os;
  364. os << "Config failed sanitycheck. ";
  365. os << "The colorspace at index " << i << " is not named.";
  366. getImpl()->sanitytext_ = os.str();
  367. throw Exception(getImpl()->sanitytext_.c_str());
  368. }
  369. std::string namelower = pystring::lower(name);
  370. StringSet::const_iterator it = existingColorSpaces.find(namelower);
  371. if(it != existingColorSpaces.end())
  372. {
  373. std::ostringstream os;
  374. os << "Config failed sanitycheck. ";
  375. os << "Two colorspaces are defined with the same name, '";
  376. os << namelower << "'.";
  377. getImpl()->sanitytext_ = os.str();
  378. throw Exception(getImpl()->sanitytext_.c_str());
  379. }
  380. existingColorSpaces.insert(namelower);
  381. }
  382. // Confirm all roles are valid
  383. {
  384. for(StringMap::const_iterator iter = getImpl()->roles_.begin(),
  385. end = getImpl()->roles_.end(); iter!=end; ++iter)
  386. {
  387. int csindex = -1;
  388. if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, iter->second))
  389. {
  390. std::ostringstream os;
  391. os << "Config failed sanitycheck. ";
  392. os << "The role '" << iter->first << "' ";
  393. os << "refers to a colorspace, '" << iter->second << "', ";
  394. os << "which is not defined.";
  395. getImpl()->sanitytext_ = os.str();
  396. throw Exception(getImpl()->sanitytext_.c_str());
  397. }
  398. // Confirm no name conflicts between colorspaces and roles
  399. if(FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, iter->first))
  400. {
  401. std::ostringstream os;
  402. os << "Config failed sanitycheck. ";
  403. os << "The role '" << iter->first << "' ";
  404. os << " is in conflict with a colorspace of the same name.";
  405. getImpl()->sanitytext_ = os.str();
  406. throw Exception(getImpl()->sanitytext_.c_str());
  407. }
  408. }
  409. }
  410. ///// DISPLAYS
  411. int numviews = 0;
  412. // Confirm all Displays transforms refer to colorspaces that exit
  413. for(DisplayMap::const_iterator iter = getImpl()->displays_.begin();
  414. iter != getImpl()->displays_.end();
  415. ++iter)
  416. {
  417. std::string display = iter->first;
  418. const ViewVec & views = iter->second;
  419. if(views.empty())
  420. {
  421. std::ostringstream os;
  422. os << "Config failed sanitycheck. ";
  423. os << "The display '" << display << "' ";
  424. os << "does not define any views.";
  425. getImpl()->sanitytext_ = os.str();
  426. throw Exception(getImpl()->sanitytext_.c_str());
  427. }
  428. for(unsigned int i=0; i<views.size(); ++i)
  429. {
  430. if(views[i].name.empty() || views[i].colorspace.empty())
  431. {
  432. std::ostringstream os;
  433. os << "Config failed sanitycheck. ";
  434. os << "The display '" << display << "' ";
  435. os << "defines a view with an empty name and/or colorspace.";
  436. getImpl()->sanitytext_ = os.str();
  437. throw Exception(getImpl()->sanitytext_.c_str());
  438. }
  439. int csindex = -1;
  440. if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, views[i].colorspace))
  441. {
  442. std::ostringstream os;
  443. os << "Config failed sanitycheck. ";
  444. os << "The display '" << display << "' ";
  445. os << "refers to a colorspace, '" << views[i].colorspace << "', ";
  446. os << "which is not defined.";
  447. getImpl()->sanitytext_ = os.str();
  448. throw Exception(getImpl()->sanitytext_.c_str());
  449. }
  450. // Confirm looks references exist
  451. LookParseResult looks;
  452. const LookParseResult::Options & options = looks.parse(views[i].looks);
  453. for(unsigned int optionindex=0;
  454. optionindex<options.size();
  455. ++optionindex)
  456. {
  457. for(unsigned int tokenindex=0;
  458. tokenindex<options[optionindex].size();
  459. ++tokenindex)
  460. {
  461. std::string look = options[optionindex][tokenindex].name;
  462. if(!look.empty() && !getLook(look.c_str()))
  463. {
  464. std::ostringstream os;
  465. os << "Config failed sanitycheck. ";
  466. os << "The display '" << display << "' ";
  467. os << "refers to a look, '" << look << "', ";
  468. os << "which is not defined.";
  469. getImpl()->sanitytext_ = os.str();
  470. throw Exception(getImpl()->sanitytext_.c_str());
  471. }
  472. }
  473. }
  474. ++numviews;
  475. }
  476. }
  477. // Confirm at least one display entry exists.
  478. if(numviews == 0)
  479. {
  480. std::ostringstream os;
  481. os << "Config failed sanitycheck. ";
  482. os << "No displays are specified.";
  483. getImpl()->sanitytext_ = os.str();
  484. throw Exception(getImpl()->sanitytext_.c_str());
  485. }
  486. // Confirm for all Transforms that reference internal colorspaces,
  487. // the named space exists
  488. {
  489. ConstTransformVec allTransforms;
  490. getImpl()->getAllIntenalTransforms(allTransforms);
  491. std::set<std::string> colorSpaceNames;
  492. for(unsigned int i=0; i<colorSpaceNames.size(); ++i)
  493. {
  494. GetColorSpaceReferences(colorSpaceNames, allTransforms[i]);
  495. }
  496. for(std::set<std::string>::iterator iter = colorSpaceNames.begin();
  497. iter != colorSpaceNames.end(); ++iter)
  498. {
  499. int csindex = -1;
  500. if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, *iter))
  501. {
  502. std::ostringstream os;
  503. os << "Config failed sanitycheck. ";
  504. os << "This config references a ColorSpace, '" << *iter << "', ";
  505. os << "which is not defined.";
  506. getImpl()->sanitytext_ = os.str();
  507. throw Exception(getImpl()->sanitytext_.c_str());
  508. }
  509. }
  510. }
  511. ///// LOOKS
  512. // For all looks, confirm the process space exists and the look is named
  513. for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
  514. {
  515. std::string name = getImpl()->looksList_[i]->getName();
  516. if(name.empty())
  517. {
  518. std::ostringstream os;
  519. os << "Config failed sanitycheck. ";
  520. os << "The look at index '" << i << "' ";
  521. os << "does not specify a name.";
  522. getImpl()->sanitytext_ = os.str();
  523. throw Exception(getImpl()->sanitytext_.c_str());
  524. }
  525. std::string processSpace = getImpl()->looksList_[i]->getProcessSpace();
  526. if(processSpace.empty())
  527. {
  528. std::ostringstream os;
  529. os << "Config failed sanitycheck. ";
  530. os << "The look '" << name << "' ";
  531. os << "does not specify a process space.";
  532. getImpl()->sanitytext_ = os.str();
  533. throw Exception(getImpl()->sanitytext_.c_str());
  534. }
  535. int csindex=0;
  536. if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, processSpace))
  537. {
  538. std::ostringstream os;
  539. os << "Config failed sanitycheck. ";
  540. os << "The look '" << name << "' ";
  541. os << "specifies a process color space, '";
  542. os << processSpace << "', which is not defined.";
  543. getImpl()->sanitytext_ = os.str();
  544. throw Exception(getImpl()->sanitytext_.c_str());
  545. }
  546. }
  547. // Everything is groovy.
  548. getImpl()->sanity_ = SANITY_SANE;
  549. }
  550. ///////////////////////////////////////////////////////////////////////////
  551. const char * Config::getDescription() const
  552. {
  553. return getImpl()->description_.c_str();
  554. }
  555. void Config::setDescription(const char * description)
  556. {
  557. getImpl()->description_ = description;
  558. AutoMutex lock(getImpl()->cacheidMutex_);
  559. getImpl()->resetCacheIDs();
  560. }
  561. // RESOURCES //////////////////////////////////////////////////////////////
  562. ConstContextRcPtr Config::getCurrentContext() const
  563. {
  564. return getImpl()->context_;
  565. }
  566. void Config::addEnvironmentVar(const char * name, const char * defaultValue)
  567. {
  568. if(defaultValue)
  569. {
  570. getImpl()->env_[std::string(name)] = std::string(defaultValue);
  571. getImpl()->context_->setStringVar(name, defaultValue);
  572. }
  573. else
  574. {
  575. StringMap::iterator iter = getImpl()->env_.find(std::string(name));
  576. if(iter != getImpl()->env_.end()) getImpl()->env_.erase(iter);
  577. }
  578. AutoMutex lock(getImpl()->cacheidMutex_);
  579. getImpl()->resetCacheIDs();
  580. }
  581. int Config::getNumEnvironmentVars() const
  582. {
  583. return static_cast<int>(getImpl()->env_.size());
  584. }
  585. const char * Config::getEnvironmentVarNameByIndex(int index) const
  586. {
  587. if(index < 0 || index >= (int)getImpl()->env_.size()) return "";
  588. StringMap::const_iterator iter = getImpl()->env_.begin();
  589. for(int i = 0; i < index; ++i) ++iter;
  590. return iter->first.c_str();
  591. }
  592. const char * Config::getEnvironmentVarDefault(const char * name) const
  593. {
  594. return LookupEnvironment(getImpl()->env_, name).c_str();
  595. }
  596. void Config::clearEnvironmentVars()
  597. {
  598. getImpl()->env_.clear();
  599. getImpl()->context_->clearStringVars();
  600. AutoMutex lock(getImpl()->cacheidMutex_);
  601. getImpl()->resetCacheIDs();
  602. }
  603. void Config::setEnvironmentMode(EnvironmentMode mode)
  604. {
  605. getImpl()->context_->setEnvironmentMode(mode);
  606. AutoMutex lock(getImpl()->cacheidMutex_);
  607. getImpl()->resetCacheIDs();
  608. }
  609. EnvironmentMode Config::getEnvironmentMode() const
  610. {
  611. return getImpl()->context_->getEnvironmentMode();
  612. }
  613. void Config::loadEnvironment()
  614. {
  615. getImpl()->context_->loadEnvironment();
  616. AutoMutex lock(getImpl()->cacheidMutex_);
  617. getImpl()->resetCacheIDs();
  618. }
  619. const char * Config::getSearchPath() const
  620. {
  621. return getImpl()->context_->getSearchPath();
  622. }
  623. void Config::setSearchPath(const char * path)
  624. {
  625. getImpl()->context_->setSearchPath(path);
  626. AutoMutex lock(getImpl()->cacheidMutex_);
  627. getImpl()->resetCacheIDs();
  628. }
  629. const char * Config::getWorkingDir() const
  630. {
  631. return getImpl()->context_->getWorkingDir();
  632. }
  633. void Config::setWorkingDir(const char * dirname)
  634. {
  635. getImpl()->context_->setWorkingDir(dirname);
  636. AutoMutex lock(getImpl()->cacheidMutex_);
  637. getImpl()->resetCacheIDs();
  638. }
  639. ///////////////////////////////////////////////////////////////////////////
  640. int Config::getNumColorSpaces() const
  641. {
  642. return static_cast<int>(getImpl()->colorspaces_.size());
  643. }
  644. const char * Config::getColorSpaceNameByIndex(int index) const
  645. {
  646. if(index<0 || index >= (int)getImpl()->colorspaces_.size())
  647. {
  648. return "";
  649. }
  650. return getImpl()->colorspaces_[index]->getName();
  651. }
  652. ConstColorSpaceRcPtr Config::getColorSpace(const char * name) const
  653. {
  654. int index = getIndexForColorSpace(name);
  655. if(index<0 || index >= (int)getImpl()->colorspaces_.size())
  656. {
  657. return ColorSpaceRcPtr();
  658. }
  659. return getImpl()->colorspaces_[index];
  660. }
  661. int Config::getIndexForColorSpace(const char * name) const
  662. {
  663. int csindex = -1;
  664. // Check to see if the name is a color space
  665. if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, name) )
  666. {
  667. return csindex;
  668. }
  669. // Check to see if the name is a role
  670. std::string csname = LookupRole(getImpl()->roles_, name);
  671. if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
  672. {
  673. return csindex;
  674. }
  675. // Is a default role defined?
  676. // (And, are we allowed to use it)
  677. if(!getImpl()->strictParsing_)
  678. {
  679. csname = LookupRole(getImpl()->roles_, ROLE_DEFAULT);
  680. if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
  681. {
  682. return csindex;
  683. }
  684. }
  685. return -1;
  686. }
  687. void Config::addColorSpace(const ConstColorSpaceRcPtr & original)
  688. {
  689. ColorSpaceRcPtr cs = original->createEditableCopy();
  690. std::string name = cs->getName();
  691. if(name.empty())
  692. throw Exception("Cannot addColorSpace with an empty name.");
  693. // Check to see if the colorspace already exists
  694. int csindex = -1;
  695. if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, name) )
  696. {
  697. getImpl()->colorspaces_[csindex] = cs;
  698. }
  699. else
  700. {
  701. // Otherwise, add it
  702. getImpl()->colorspaces_.push_back( cs );
  703. }
  704. AutoMutex lock(getImpl()->cacheidMutex_);
  705. getImpl()->resetCacheIDs();
  706. }
  707. void Config::clearColorSpaces()
  708. {
  709. getImpl()->colorspaces_.clear();
  710. }
  711. const char * Config::parseColorSpaceFromString(const char * str) const
  712. {
  713. if(!str) return "";
  714. // Search the entire filePath, including directory name (if provided)
  715. // convert the filename to lowercase.
  716. std::string fullstr = pystring::lower(std::string(str));
  717. // See if it matches a lut name.
  718. // This is the position of the RIGHT end of the colorspace substring, not the left
  719. int rightMostColorPos=-1;
  720. std::string rightMostColorspace = "";
  721. int rightMostColorSpaceIndex = -1;
  722. // Find the right-most occcurance within the string for each colorspace.
  723. for (unsigned int i=0; i<getImpl()->colorspaces_.size(); ++i)
  724. {
  725. std::string csname = pystring::lower(getImpl()->colorspaces_[i]->getName());
  726. // find right-most extension matched in filename
  727. int colorspacePos = pystring::rfind(fullstr, csname);
  728. if(colorspacePos < 0)
  729. continue;
  730. // If we have found a match, move the pointer over to the right end of the substring
  731. // This will allow us to find the longest name that matches the rightmost colorspace
  732. colorspacePos += (int)csname.size();
  733. if ( (colorspacePos > rightMostColorPos) ||
  734. ((colorspacePos == rightMostColorPos) && (csname.size() > rightMostColorspace.size()))
  735. )
  736. {
  737. rightMostColorPos = colorspacePos;
  738. rightMostColorspace = csname;
  739. rightMostColorSpaceIndex = i;
  740. }
  741. }
  742. if(rightMostColorSpaceIndex>=0)
  743. {
  744. return getImpl()->colorspaces_[rightMostColorSpaceIndex]->getName();
  745. }
  746. if(!getImpl()->strictParsing_)
  747. {
  748. // Is a default role defined?
  749. std::string csname = LookupRole(getImpl()->roles_, ROLE_DEFAULT);
  750. if(!csname.empty())
  751. {
  752. int csindex = -1;
  753. if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
  754. {
  755. // This is necessary to not return a reference to
  756. // a local variable.
  757. return getImpl()->colorspaces_[csindex]->getName();
  758. }
  759. }
  760. }
  761. return "";
  762. }
  763. bool Config::isStrictParsingEnabled() const
  764. {
  765. return getImpl()->strictParsing_;
  766. }
  767. void Config::setStrictParsingEnabled(bool enabled)
  768. {
  769. getImpl()->strictParsing_ = enabled;
  770. AutoMutex lock(getImpl()->cacheidMutex_);
  771. getImpl()->resetCacheIDs();
  772. }
  773. // Roles
  774. void Config::setRole(const char * role, const char * colorSpaceName)
  775. {
  776. // Set the role
  777. if(colorSpaceName)
  778. {
  779. getImpl()->roles_[pystring::lower(role)] = std::string(colorSpaceName);
  780. }
  781. // Unset the role
  782. else
  783. {
  784. StringMap::iterator iter = getImpl()->roles_.find(pystring::lower(role));
  785. if(iter != getImpl()->roles_.end())
  786. {
  787. getImpl()->roles_.erase(iter);
  788. }
  789. }
  790. AutoMutex lock(getImpl()->cacheidMutex_);
  791. getImpl()->resetCacheIDs();
  792. }
  793. int Config::getNumRoles() const
  794. {
  795. return static_cast<int>(getImpl()->roles_.size());
  796. }
  797. bool Config::hasRole(const char * role) const
  798. {
  799. return LookupRole(getImpl()->roles_, role) == "" ? false : true;
  800. }
  801. const char * Config::getRoleName(int index) const
  802. {
  803. if(index < 0 || index >= (int)getImpl()->roles_.size()) return "";
  804. StringMap::const_iterator iter = getImpl()->roles_.begin();
  805. for(int i = 0; i < index; ++i) ++iter;
  806. return iter->first.c_str();
  807. }
  808. ///////////////////////////////////////////////////////////////////////////
  809. //
  810. // Display/View Registration
  811. const char * Config::getDefaultDisplay() const
  812. {
  813. if(getImpl()->displayCache_.empty())
  814. {
  815. ComputeDisplays(getImpl()->displayCache_,
  816. getImpl()->displays_,
  817. getImpl()->activeDisplays_,
  818. getImpl()->activeDisplaysEnvOverride_);
  819. }
  820. int index = -1;
  821. if(!getImpl()->activeDisplaysEnvOverride_.empty())
  822. {
  823. StringVec orderedDisplays = IntersectStringVecsCaseIgnore(getImpl()->activeDisplaysEnvOverride_,
  824. getImpl()->displayCache_);
  825. if(!orderedDisplays.empty())
  826. {
  827. index = FindInStringVecCaseIgnore(getImpl()->displayCache_, orderedDisplays[0]);
  828. }
  829. }
  830. else if(!getImpl()->activeDisplays_.empty())
  831. {
  832. StringVec orderedDisplays = IntersectStringVecsCaseIgnore(getImpl()->activeDisplays_,
  833. getImpl()->displayCache_);
  834. if(!orderedDisplays.empty())
  835. {
  836. index = FindInStringVecCaseIgnore(getImpl()->displayCache_, orderedDisplays[0]);
  837. }
  838. }
  839. if(index >= 0)
  840. {
  841. return getImpl()->displayCache_[index].c_str();
  842. }
  843. if(!getImpl()->displayCache_.empty())
  844. {
  845. return getImpl()->displayCache_[0].c_str();
  846. }
  847. return "";
  848. }
  849. int Config::getNumDisplays() const
  850. {
  851. if(getImpl()->displayCache_.empty())
  852. {
  853. ComputeDisplays(getImpl()->displayCache_,
  854. getImpl()->displays_,
  855. getImpl()->activeDisplays_,
  856. getImpl()->activeDisplaysEnvOverride_);
  857. }
  858. return static_cast<int>(getImpl()->displayCache_.size());
  859. }
  860. const char * Config::getDisplay(int index) const
  861. {
  862. if(getImpl()->displayCache_.empty())
  863. {
  864. ComputeDisplays(getImpl()->displayCache_,
  865. getImpl()->displays_,
  866. getImpl()->activeDisplays_,
  867. getImpl()->activeDisplaysEnvOverride_);
  868. }
  869. if(index>=0 || index < static_cast<int>(getImpl()->displayCache_.size()))
  870. {
  871. return getImpl()->displayCache_[index].c_str();
  872. }
  873. return "";
  874. }
  875. const char * Config::getDefaultView(const char * display) const
  876. {
  877. if(getImpl()->displayCache_.empty())
  878. {
  879. ComputeDisplays(getImpl()->displayCache_,
  880. getImpl()->displays_,
  881. getImpl()->activeDisplays_,
  882. getImpl()->activeDisplaysEnvOverride_);
  883. }
  884. if(!display) return "";
  885. DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
  886. if(iter == getImpl()->displays_.end()) return "";
  887. const ViewVec & views = iter->second;
  888. StringVec masterViews;
  889. for(unsigned int i=0; i<views.size(); ++i)
  890. {
  891. masterViews.push_back(views[i].name);
  892. }
  893. int index = -1;
  894. if(!getImpl()->activeViewsEnvOverride_.empty())
  895. {
  896. StringVec orderedViews = IntersectStringVecsCaseIgnore(getImpl()->activeViewsEnvOverride_,
  897. masterViews);
  898. if(!orderedViews.empty())
  899. {
  900. index = FindInStringVecCaseIgnore(masterViews, orderedViews[0]);
  901. }
  902. }
  903. else if(!getImpl()->activeViews_.empty())
  904. {
  905. StringVec orderedViews = IntersectStringVecsCaseIgnore(getImpl()->activeViews_,
  906. masterViews);
  907. if(!orderedViews.empty())
  908. {
  909. index = FindInStringVecCaseIgnore(masterViews, orderedViews[0]);
  910. }
  911. }
  912. if(index >= 0)
  913. {
  914. return views[index].name.c_str();
  915. }
  916. if(!views.empty())
  917. {
  918. return views[0].name.c_str();
  919. }
  920. return "";
  921. }
  922. int Config::getNumViews(const char * display) const
  923. {
  924. if(getImpl()->displayCache_.empty())
  925. {
  926. ComputeDisplays(getImpl()->displayCache_,
  927. getImpl()->displays_,
  928. getImpl()->activeDisplays_,
  929. getImpl()->activeDisplaysEnvOverride_);
  930. }
  931. if(!display) return 0;
  932. DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
  933. if(iter == getImpl()->displays_.end()) return 0;
  934. const ViewVec & views = iter->second;
  935. return static_cast<int>(views.size());
  936. }
  937. const char * Config::getView(const char * display, int index) const
  938. {
  939. if(getImpl()->displayCache_.empty())
  940. {
  941. ComputeDisplays(getImpl()->displayCache_,
  942. getImpl()->displays_,
  943. getImpl()->activeDisplays_,
  944. getImpl()->activeDisplaysEnvOverride_);
  945. }
  946. if(!display) return "";
  947. DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
  948. if(iter == getImpl()->displays_.end()) return "";
  949. const ViewVec & views = iter->second;
  950. return views[index].name.c_str();
  951. }
  952. const char * Config::getDisplayColorSpaceName(const char * display, const char * view) const
  953. {
  954. if(!display || !view) return "";
  955. DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
  956. if(iter == getImpl()->displays_.end()) return "";
  957. const ViewVec & views = iter->second;
  958. int index = find_view(views, view);
  959. if(index<0) return "";
  960. return views[index].colorspace.c_str();
  961. }
  962. const char * Config::getDisplayLooks(const char * display, const char * view) const
  963. {
  964. if(!display || !view) return "";
  965. DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
  966. if(iter == getImpl()->displays_.end()) return "";
  967. const ViewVec & views = iter->second;
  968. int index = find_view(views, view);
  969. if(index<0) return "";
  970. return views[index].looks.c_str();
  971. }
  972. void Config::addDisplay(const char * display, const char * view,
  973. const char * colorSpaceName, const char * lookName)
  974. {
  975. if(!display || !view || !colorSpaceName || !lookName) return;
  976. AddDisplay(getImpl()->displays_,
  977. display, view, colorSpaceName, lookName);
  978. getImpl()->displayCache_.clear();
  979. AutoMutex lock(getImpl()->cacheidMutex_);
  980. getImpl()->resetCacheIDs();
  981. }
  982. void Config::clearDisplays()
  983. {
  984. getImpl()->displays_.clear();
  985. getImpl()->displayCache_.clear();
  986. AutoMutex lock(getImpl()->cacheidMutex_);
  987. getImpl()->resetCacheIDs();
  988. }
  989. void Config::setActiveDisplays(const char * displays)
  990. {
  991. getImpl()->activeDisplays_.clear();
  992. SplitStringEnvStyle(getImpl()->activeDisplays_, displays);
  993. getImpl()->displayCache_.clear();
  994. AutoMutex lock(getImpl()->cacheidMutex_);
  995. getImpl()->resetCacheIDs();
  996. }
  997. const char * Config::getActiveDisplays() const
  998. {
  999. getImpl()->activeDisplaysStr_ = JoinStringEnvStyle(getImpl()->activeDisplays_);
  1000. return getImpl()->activeDisplaysStr_.c_str();
  1001. }
  1002. void Config::setActiveViews(const char * views)
  1003. {
  1004. getImpl()->activeViews_.clear();
  1005. SplitStringEnvStyle(getImpl()->activeViews_, views);
  1006. getImpl()->displayCache_.clear();
  1007. AutoMutex lock(getImpl()->cacheidMutex_);
  1008. getImpl()->resetCacheIDs();
  1009. }
  1010. const char * Config::getActiveViews() const
  1011. {
  1012. getImpl()->activeViewsStr_ = JoinStringEnvStyle(getImpl()->activeViews_);
  1013. return getImpl()->activeViewsStr_.c_str();
  1014. }
  1015. ///////////////////////////////////////////////////////////////////////////
  1016. void Config::getDefaultLumaCoefs(float * c3) const
  1017. {
  1018. memcpy(c3, &getImpl()->defaultLumaCoefs_[0], 3*sizeof(float));
  1019. }
  1020. void Config::setDefaultLumaCoefs(const float * c3)
  1021. {
  1022. memcpy(&getImpl()->defaultLumaCoefs_[0], c3, 3*sizeof(float));
  1023. AutoMutex lock(getImpl()->cacheidMutex_);
  1024. getImpl()->resetCacheIDs();
  1025. }
  1026. ///////////////////////////////////////////////////////////////////////////
  1027. ConstLookRcPtr Config::getLook(const char * name) const
  1028. {
  1029. std::string namelower = pystring::lower(name);
  1030. for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
  1031. {
  1032. if(pystring::lower(getImpl()->looksList_[i]->getName()) == namelower)
  1033. {
  1034. return getImpl()->looksList_[i];
  1035. }
  1036. }
  1037. return ConstLookRcPtr();
  1038. }
  1039. int Config::getNumLooks() const
  1040. {
  1041. return static_cast<int>(getImpl()->looksList_.size());
  1042. }
  1043. const char * Config::getLookNameByIndex(int index) const
  1044. {
  1045. if(index<0 || index>=static_cast<int>(getImpl()->looksList_.size()))
  1046. {
  1047. return "";
  1048. }
  1049. return getImpl()->looksList_[index]->getName();
  1050. }
  1051. void Config::addLook(const ConstLookRcPtr & look)
  1052. {
  1053. std::string name = look->getName();
  1054. if(name.empty())
  1055. throw Exception("Cannot addLook with an empty name.");
  1056. std::string namelower = pystring::lower(name);
  1057. // If the look exists, replace it
  1058. for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
  1059. {
  1060. if(pystring::lower(getImpl()->looksList_[i]->getName()) == namelower)
  1061. {
  1062. getImpl()->looksList_[i] = look->createEditableCopy();
  1063. return;
  1064. }
  1065. }
  1066. // Otherwise, add it
  1067. getImpl()->looksList_.push_back(look->createEditableCopy());
  1068. AutoMutex lock(getImpl()->cacheidMutex_);
  1069. getImpl()->resetCacheIDs();
  1070. }
  1071. void Config::clearLooks()
  1072. {
  1073. getImpl()->looksList_.clear();
  1074. AutoMutex lock(getImpl()->cacheidMutex_);
  1075. getImpl()->resetCacheIDs();
  1076. }
  1077. ///////////////////////////////////////////////////////////////////////////
  1078. ConstProcessorRcPtr Config::getProcessor(const ConstColorSpaceRcPtr & src,
  1079. const ConstColorSpaceRcPtr & dst) const
  1080. {
  1081. ConstContextRcPtr context = getCurrentContext();
  1082. return getProcessor(context, src, dst);
  1083. }
  1084. ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
  1085. const ConstColorSpaceRcPtr & src,
  1086. const ConstColorSpaceRcPtr & dst) const
  1087. {
  1088. if(!src)
  1089. {
  1090. throw Exception("Config::GetProcessor failed. Source colorspace is null.");
  1091. }
  1092. if(!dst)
  1093. {
  1094. throw Exception("Config::GetProcessor failed. Destination colorspace is null.");
  1095. }
  1096. ProcessorRcPtr processor = Processor::Create();
  1097. processor->getImpl()->addColorSpaceConversion(*this, context, src, dst);
  1098. processor->getImpl()->finalize();
  1099. return processor;
  1100. }
  1101. ConstProcessorRcPtr Config::getProcessor(const char * srcName,
  1102. const char * dstName) const
  1103. {
  1104. ConstContextRcPtr context = getCurrentContext();
  1105. return getProcessor(context, srcName, dstName);
  1106. }
  1107. //! Names can be colorspace name or role name
  1108. ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
  1109. const char * srcName,
  1110. const char * dstName) const
  1111. {
  1112. ConstColorSpaceRcPtr src = getColorSpace(srcName);
  1113. if(!src)
  1114. {
  1115. std::ostringstream os;
  1116. os << "Could not find colorspace '" << srcName << "'.";
  1117. throw Exception(os.str().c_str());
  1118. }
  1119. ConstColorSpaceRcPtr dst = getColorSpace(dstName);
  1120. if(!dst)
  1121. {
  1122. std::ostringstream os;
  1123. os << "Could not find colorspace '" << dstName << "'.";
  1124. throw Exception(os.str().c_str());
  1125. }
  1126. return getProcessor(context, src, dst);
  1127. }
  1128. ConstProcessorRcPtr Config::getProcessor(const ConstTransformRcPtr& transform) const
  1129. {
  1130. return getProcessor(transform, TRANSFORM_DIR_FORWARD);
  1131. }
  1132. ConstProcessorRcPtr Config::getProcessor(const ConstTransformRcPtr& transform,
  1133. TransformDirection direction) const
  1134. {
  1135. ConstContextRcPtr context = getCurrentContext();
  1136. return getProcessor(context, transform, direction);
  1137. }
  1138. ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
  1139. const ConstTransformRcPtr& transform,
  1140. TransformDirection direction) const
  1141. {
  1142. ProcessorRcPtr processor = Processor::Create();
  1143. processor->getImpl()->addTransform(*this, context, transform, direction);
  1144. processor->getImpl()->finalize();
  1145. return processor;
  1146. }
  1147. std::ostream& operator<< (std::ostream& os, const Config& config)
  1148. {
  1149. config.serialize(os);
  1150. return os;
  1151. }
  1152. ///////////////////////////////////////////////////////////////////////////
  1153. // CacheID
  1154. const char * Config::getCacheID() const
  1155. {
  1156. return getCacheID(getCurrentContext());
  1157. }
  1158. const char * Config::getCacheID(const ConstContextRcPtr & context) const
  1159. {
  1160. AutoMutex lock(getImpl()->cacheidMutex_);
  1161. // A null context will use the empty cacheid
  1162. std::string contextcacheid = "";
  1163. if(context) contextcacheid = context->getCacheID();
  1164. StringMap::const_iterator cacheiditer = getImpl()->cacheids_.find(contextcacheid);
  1165. if(cacheiditer != getImpl()->cacheids_.end())
  1166. {
  1167. return cacheiditer->second.c_str();
  1168. }
  1169. // Include the hash of the yaml config serialization
  1170. if(getImpl()->cacheidnocontext_.empty())
  1171. {
  1172. std::stringstream cacheid;
  1173. serialize(cacheid);
  1174. std::string fullstr = cacheid.str();
  1175. getImpl()->cacheidnocontext_ = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
  1176. }
  1177. // Also include all file references, using the context (if specified)
  1178. std::string fileReferencesFashHash = "";
  1179. if(context)
  1180. {
  1181. std::ostringstream filehash;
  1182. ConstTransformVec allTransforms;
  1183. getImpl()->getAllIntenalTransforms(allTransforms);
  1184. std::set<std::string> files;
  1185. for(unsigned int i=0; i<allTransforms.size(); ++i)
  1186. {
  1187. GetFileReferences(files, allTransforms[i]);
  1188. }
  1189. for(std::set<std::string>::iterator iter = files.begin();
  1190. iter != files.end(); ++iter)
  1191. {
  1192. if(iter->empty()) continue;
  1193. filehash << *iter << "=";
  1194. try
  1195. {
  1196. std::string resolvedLocation = context->resolveFileLocation(iter->c_str());
  1197. filehash << GetFastFileHash(resolvedLocation) << " ";
  1198. }
  1199. catch(...)
  1200. {
  1201. filehash << "? ";
  1202. continue;
  1203. }
  1204. }
  1205. std::string fullstr = filehash.str();
  1206. fileReferencesFashHash = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
  1207. }
  1208. getImpl()->cacheids_[contextcacheid] = getImpl()->cacheidnocontext_ + ":" + fileReferencesFashHash;
  1209. return getImpl()->cacheids_[contextcacheid].c_str();
  1210. }
  1211. ///////////////////////////////////////////////////////////////////////////
  1212. // Serialization
  1213. void Config::serialize(std::ostream& os) const
  1214. {
  1215. try
  1216. {
  1217. getImpl()->io_.write(os, this);
  1218. }
  1219. catch( const std::exception & e)
  1220. {
  1221. std::ostringstream error;
  1222. error << "Error building YAML: " << e.what();
  1223. throw Exception(error.str().c_str());
  1224. }
  1225. }
  1226. void Config::Impl::resetCacheIDs()
  1227. {
  1228. cacheids_.clear();
  1229. cacheidnocontext_ = "";
  1230. sanity_ = SANITY_UNKNOWN;
  1231. sanitytext_ = "";
  1232. }
  1233. void Config::Impl::getAllIntenalTransforms(ConstTransformVec & transformVec) const
  1234. {
  1235. // Grab all transforms from the ColorSpaces
  1236. for(unsigned int i=0; i<colorspaces_.size(); ++i)
  1237. {
  1238. if(colorspaces_[i]->getTransform(COLORSPACE_DIR_TO_REFERENCE))
  1239. transformVec.push_back(colorspaces_[i]->getTransform(COLORSPACE_DIR_TO_REFERENCE));
  1240. if(colorspaces_[i]->getTransform(COLORSPACE_DIR_FROM_REFERENCE))
  1241. transformVec.push_back(colorspaces_[i]->getTransform(COLORSPACE_DIR_FROM_REFERENCE));
  1242. }
  1243. // Grab all transforms from the Looks
  1244. for(unsigned int i=0; i<looksList_.size(); ++i)
  1245. {
  1246. if(looksList_[i]->getTransform())
  1247. transformVec.push_back(looksList_[i]->getTransform());
  1248. if(looksList_[i]->getInverseTransform())
  1249. transformVec.push_back(looksList_[i]->getInverseTransform());
  1250. }
  1251. }
  1252. }
  1253. OCIO_NAMESPACE_EXIT
  1254. ///////////////////////////////////////////////////////////////////////////////
  1255. #ifdef OCIO_UNIT_TEST
  1256. namespace OCIO = OCIO_NAMESPACE;
  1257. #include "UnitTest.h"
  1258. #include <sys/stat.h>
  1259. #include "pystring/pystring.h"
  1260. #if 0
  1261. OIIO_ADD_TEST(Config, test_searchpath_filesystem)
  1262. {
  1263. OCIO::EnvMap env = OCIO::GetEnvMap();
  1264. std::string OCIO_TEST_AREA("$OCIO_TEST_AREA");
  1265. EnvExpand(&OCIO_TEST_AREA, &env);
  1266. OCIO::ConfigRcPtr config = OCIO::Config::Create();
  1267. // basic get/set/expand
  1268. config->setSearchPath("."
  1269. ":$OCIO_TEST1"
  1270. ":/$OCIO_JOB/${OCIO_SEQ}/$OCIO_SHOT/ocio");
  1271. OIIO_CHECK_ASSERT(strcmp(config->getSearchPath(),
  1272. ".:$OCIO_TEST1:/$OCIO_JOB/${OCIO_SEQ}/$OCIO_SHOT/ocio") == 0);
  1273. OIIO_CHECK_ASSERT(strcmp(config->getSearchPath(true),
  1274. ".:foobar:/meatballs/cheesecake/mb-cc-001/ocio") == 0);
  1275. // find some files
  1276. config->setSearchPath(".."
  1277. ":$OCIO_TEST1"
  1278. ":${OCIO_TEST_AREA}/test_search/one"
  1279. ":$OCIO_TEST_AREA/test_search/two");
  1280. // setup for search test
  1281. std::string base_dir("$OCIO_TEST_AREA/test_search/");
  1282. EnvExpand(&base_dir, &env);
  1283. mkdir(base_dir.c_str(), 0777);
  1284. std::string one_dir("$OCIO_TEST_AREA/test_search/one/");
  1285. EnvExpand(&one_dir, &env);
  1286. mkdir(one_dir.c_str(), 0777);
  1287. std::string two_dir("$OCIO_TEST_AREA/test_search/two/");
  1288. EnvExpand(&two_dir, &env);
  1289. mkdir(two_dir.c_str(), 0777);
  1290. std::string lut1(one_dir+"somelut1.lut");
  1291. std::ofstream somelut1(lut1.c_str());
  1292. somelut1.close();
  1293. std::string lut2(two_dir+"somelut2.lut");
  1294. std::ofstream somelut2(lut2.c_str());
  1295. somelut2.close();
  1296. std::string lut3(two_dir+"somelut3.lut");
  1297. std::ofstream somelut3(lut3.c_str());
  1298. somelut3.close();
  1299. std::string lutdotdot(OCIO_TEST_AREA+"/lutdotdot.lut");
  1300. std::ofstream somelutdotdot(lutdotdot.c_str());
  1301. somelutdotdot.close();
  1302. // basic search test
  1303. OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut1.lut"),
  1304. lut1.c_str()) == 0);
  1305. OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut2.lut"),
  1306. lut2.c_str()) == 0);
  1307. OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut3.lut"),
  1308. lut3.c_str()) == 0);
  1309. OIIO_CHECK_ASSERT(strcmp(config->findFile("lutdotdot.lut"),
  1310. lutdotdot.c_str()) == 0);
  1311. }
  1312. #endif
  1313. OIIO_ADD_TEST(Config, InternalRawProfile)
  1314. {
  1315. std::istringstream is;
  1316. is.str(OCIO::INTERNAL_RAW_PROFILE);
  1317. OIIO_CHECK_NO_THOW(OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromStream(is));
  1318. }
  1319. OIIO_ADD_TEST(Config, SimpleConfig)
  1320. {
  1321. std::string SIMPLE_PROFILE =
  1322. "ocio_profile_version: 1\n"
  1323. "resource_path: luts\n"
  1324. "strictparsing: false\n"
  1325. "luma: [0.2126, 0.7152, 0.0722]\n"
  1326. "roles:\n"
  1327. " compositing_log: lgh\n"
  1328. " default: raw\n"
  1329. " scene_linear: lnh\n"
  1330. "displays:\n"
  1331. " sRGB:\n"
  1332. " - !<View> {name: Film1D, colorspace: vd8}\n"
  1333. " - !<View> {name: Log, colorspace: lg10}\n"
  1334. " - !<View> {name: Raw, colorspace: raw}\n"
  1335. "colorspaces:\n"
  1336. " - !<ColorSpace>\n"
  1337. " name: raw\n"
  1338. " family: raw\n"
  1339. " equalitygroup: \n"
  1340. " bitdepth: 32f\n"
  1341. " description: |\n"
  1342. " A raw color space. Conversions to and from this space are no-ops.\n"
  1343. " isdata: true\n"
  1344. " allocation: uniform\n"
  1345. " - !<ColorSpace>\n"
  1346. " name: lnh\n"
  1347. " family: ln\n"
  1348. " equalitygroup: \n"
  1349. " bitdepth: 16f\n"
  1350. " description: |\n"
  1351. " The show reference space. This is a sensor referred linear\n"
  1352. " representation of the scene with primaries that correspond to\n"
  1353. " scanned film. 0.18 in this space corresponds to a properly\n"
  1354. " exposed 18% grey card.\n"
  1355. " isdata: false\n"
  1356. " allocation: lg2\n"
  1357. " - !<ColorSpace>\n"
  1358. " name: loads_of_transforms\n"
  1359. " family: vd8\n"
  1360. " equalitygroup: \n"
  1361. " bitdepth: 8ui\n"
  1362. " description: 'how many transforms can we use?'\n"
  1363. " isdata: false\n"
  1364. " allocation: uniform\n"
  1365. " to_reference: !<GroupTransform>\n"
  1366. " direction: forward\n"
  1367. " children:\n"
  1368. " - !<FileTransform>\n"
  1369. " src: diffusemult.spimtx\n"
  1370. " interpolation: unknown\n"
  1371. " - !<ColorSpaceTransform>\n"
  1372. " src: vd8\n"
  1373. " dst: lnh\n"
  1374. " - !<ExponentTransform>\n"
  1375. " value: [2.2, 2.2, 2.2, 1]\n"
  1376. " - !<MatrixTransform>\n"
  1377. " matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]\n"
  1378. " offset: [0, 0, 0, 0]\n"
  1379. " - !<CDLTransform>\n"
  1380. " slope: [1, 1, 1]\n"
  1381. " offset: [0, 0, 0]\n"
  1382. " power: [1, 1, 1]\n"
  1383. " saturation: 1\n"
  1384. "\n";
  1385. std::istringstream is;
  1386. is.str(SIMPLE_PROFILE);
  1387. OCIO::ConstConfigRcPtr config;
  1388. OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is));
  1389. }
  1390. OIIO_ADD_TEST(Config, Roles)
  1391. {
  1392. std::string SIMPLE_PROFILE =
  1393. "ocio_profile_version: 1\n"
  1394. "strictparsing: false\n"
  1395. "roles:\n"
  1396. " compositing_log: lgh\n"
  1397. " default: raw\n"
  1398. " scene_linear: lnh\n"
  1399. "colorspaces:\n"
  1400. " - !<ColorSpace>\n"
  1401. " name: raw\n"
  1402. " - !<ColorSpace>\n"
  1403. " name: lnh\n"
  1404. " - !<ColorSpace>\n"
  1405. " name: lgh\n"
  1406. "\n";
  1407. std::istringstream is;
  1408. is.str(SIMPLE_PROFILE);
  1409. OCIO::ConstConfigRcPtr config;
  1410. OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is));
  1411. OIIO_CHECK_EQUAL(config->getNumRoles(), 3);
  1412. OIIO_CHECK_ASSERT(config->hasRole("compositing_log") == true);
  1413. OIIO_CHECK_ASSERT(config->hasRole("cheese") == false);
  1414. OIIO_CHECK_ASSERT(config->hasRole("") == false);
  1415. OIIO_CHECK_ASSERT(strcmp(config->getRoleName(2), "scene_linear") == 0);
  1416. OIIO_CHECK_ASSERT(strcmp(config->getRoleName(0), "compositing_log") == 0);
  1417. OIIO_CHECK_ASSERT(strcmp(config->getRoleName(1), "default") == 0);
  1418. OIIO_CHECK_ASSERT(strcmp(config->getRoleName(10), "") == 0);
  1419. OIIO_CHECK_ASSERT(strcmp(config->getRoleName(-4), "") == 0);
  1420. }
  1421. OIIO_ADD_TEST(Config, Serialize)
  1422. {
  1423. OCIO::ConfigRcPtr config = OCIO::Config::Create();
  1424. {
  1425. OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
  1426. cs->setName("testing");
  1427. cs->setFamily("test");
  1428. OCIO::FileTransformRcPtr transform1 = \
  1429. OCIO::FileTransform::Create();
  1430. OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create();
  1431. groupTransform->push_back(transform1);
  1432. cs->setTransform(groupTransform, OCIO::COLORSPACE_DIR_TO_REFERENCE);
  1433. config->addColorSpace(cs);
  1434. config->setRole( OCIO::ROLE_COMPOSITING_LOG, cs->getName() );
  1435. }
  1436. {
  1437. OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
  1438. cs->setName("testing2");
  1439. cs->setFamily("test");
  1440. OCIO::ExponentTransformRcPtr transform1 = \
  1441. OCIO::ExponentTransform::Create();
  1442. OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create();
  1443. groupTransform->push_back(transform1);
  1444. cs->setTransform(groupTransform, OCIO::COLORSPACE_DIR_TO_REFERENCE);
  1445. config->addColorSpace(cs);
  1446. config->setRole( OCIO::ROLE_COMPOSITING_LOG, cs->getName() );
  1447. }
  1448. // for testing
  1449. //std::ofstream outfile("/tmp/test.ocio");
  1450. //config->serialize(outfile);
  1451. //outfile.close();
  1452. std::ostringstream os;
  1453. config->serialize(os);
  1454. std::string PROFILE_OUT =
  1455. "ocio_profile_version: 1\n"
  1456. "\n"
  1457. "search_path: \"\"\n"
  1458. "strictparsing: true\n"
  1459. "luma: [0.2126, 0.7152, 0.0722]\n"
  1460. "\n"
  1461. "roles:\n"
  1462. " compositing_log: testing2\n"
  1463. "\n"
  1464. "displays:\n"
  1465. " {}\n"
  1466. "\n"
  1467. "active_displays: []\n"
  1468. "active_views: []\n"
  1469. "\n"
  1470. "colorspaces:\n"
  1471. " - !<ColorSpace>\n"
  1472. " name: testing\n"
  1473. " family: test\n"
  1474. " equalitygroup: \"\"\n"
  1475. " bitdepth: unknown\n"
  1476. " isdata: false\n"
  1477. " allocation: uniform\n"
  1478. " to_reference: !<GroupTransform>\n"
  1479. " children:\n"
  1480. " - !<FileTransform> {src: \"\", interpolation: unknown}\n"
  1481. "\n"
  1482. " - !<ColorSpace>\n"
  1483. " name: testing2\n"
  1484. " family: test\n"
  1485. " equalitygroup: \"\"\n"
  1486. " bitdepth: unknown\n"
  1487. " isdata: false\n"
  1488. " allocation: uniform\n"
  1489. " to_reference: !<GroupTransform>\n"
  1490. " children:\n"
  1491. " - !<ExponentTransform> {value: [1, 1, 1, 1]}\n";
  1492. std::vector<std::string> osvec;
  1493. OCIO::pystring::splitlines(os.str(), osvec);
  1494. std::vector<std::string> PROFILE_OUTvec;
  1495. OCIO::pystring::splitlines(PROFILE_OUT, PROFILE_OUTvec);
  1496. OIIO_CHECK_EQUAL(osvec.size(), PROFILE_OUTvec.size());
  1497. for(unsigned int i = 0; i < PROFILE_OUTvec.size(); ++i)
  1498. OIIO_CHECK_EQUAL(osvec[i], PROFILE_OUTvec[i]);
  1499. }
  1500. OIIO_ADD_TEST(Config, SanityCheck)
  1501. {
  1502. {
  1503. std::string SIMPLE_PROFILE =
  1504. "ocio_profile_version: 1\n"
  1505. "colorspaces:\n"
  1506. " - !<ColorSpace>\n"
  1507. " name: raw\n"
  1508. " - !<ColorSpace>\n"
  1509. " name: raw\n"
  1510. "strictparsing: false\n"
  1511. "roles:\n"
  1512. " default: raw\n"
  1513. "displays:\n"
  1514. " sRGB:\n"
  1515. " - !<View> {name: Raw, colorspace: raw}\n"
  1516. "\n";
  1517. std::istringstream is;
  1518. is.str(SIMPLE_PROFILE);
  1519. OCIO::ConstConfigRcPtr config;
  1520. OIIO_CHECK_THOW(config = OCIO::Config::CreateFromStream(is), OCIO::Exception);
  1521. }
  1522. {
  1523. std::string SIMPLE_PROFILE =
  1524. "ocio_profile_version: 1\n"
  1525. "colorspaces:\n"
  1526. " - !<ColorSpace>\n"
  1527. " name: raw\n"
  1528. "strictparsing: false\n"
  1529. "roles:\n"
  1530. " default: raw\n"
  1531. "displays:\n"
  1532. " sRGB:\n"
  1533. " - !<View> {name: Raw, colorspace: raw}\n"
  1534. "\n";
  1535. std::istringstream is;
  1536. is.str(SIMPLE_PROFILE);
  1537. OCIO::ConstConfigRcPtr config;
  1538. OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is));
  1539. OIIO_CHECK_NO_THOW(config->sanityCheck());
  1540. }
  1541. }
  1542. OIIO_ADD_TEST(Config, EnvCheck)
  1543. {
  1544. {
  1545. std::string SIMPLE_PROFILE =
  1546. "ocio_profile_version: 1\n"
  1547. "environment:\n"
  1548. " SHOW: super\n"
  1549. " SHOT: test\n"
  1550. " SEQ: foo\n"
  1551. " test: bar${cheese}\n"
  1552. " cheese: chedder\n"
  1553. "colorspaces:\n"
  1554. " - !<ColorSpace>\n"
  1555. " name: raw\n"
  1556. "strictparsing: false\n"
  1557. "roles:\n"
  1558. " default: raw\n"
  1559. "displays:\n"
  1560. " sRGB:\n"
  1561. " - !<View> {name: Raw, colorspace: raw}\n"
  1562. "\n";
  1563. std::string SIMPLE_PROFILE2 =
  1564. "ocio_profile_version: 1\n"
  1565. "colorspaces:\n"
  1566. " - !<ColorSpace>\n"
  1567. " name: raw\n"
  1568. "strictparsing: false\n"
  1569. "roles:\n"
  1570. " default: raw\n"
  1571. "displays:\n"
  1572. " sRGB:\n"
  1573. " - !<View> {name: Raw, colorspace: raw}\n"
  1574. "\n";
  1575. std::string test("SHOW=bar");
  1576. putenv((char *)test.c_str());
  1577. std::string test2("TASK=lighting");
  1578. putenv((char *)test2.c_str());
  1579. std::istringstream is;
  1580. is.str(SIMPLE_PROFILE);
  1581. OCIO::ConstConfigRcPtr config;
  1582. OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is));
  1583. OIIO_CHECK_EQUAL(config->getNumEnvironmentVars(), 5);
  1584. OIIO_CHECK_ASSERT(strcmp(config->getCurrentContext()->resolveStringVar("test${test}"),
  1585. "testbarchedder") == 0);
  1586. OIIO_CHECK_ASSERT(strcmp(config->getCurrentContext()->resolveStringVar("${SHOW}"),
  1587. "bar") == 0);
  1588. OIIO_CHECK_ASSERT(strcmp(config->getEnvironmentVarDefault("SHOW"), "super") == 0);
  1589. OCIO::ConfigRcPtr edit = config->createEditableCopy();
  1590. edit->clearEnvironmentVars();
  1591. OIIO_CHECK_EQUAL(edit->getNumEnvironmentVars(), 0);
  1592. edit->addEnvironmentVar("testing", "dupvar");
  1593. edit->addEnvironmentVar("testing", "dupvar");
  1594. edit->addEnvironmentVar("foobar", "testing");
  1595. edit->addEnvironmentVar("blank", "");
  1596. edit->addEnvironmentVar("dontadd", NULL);
  1597. OIIO_CHECK_EQUAL(edit->getNumEnvironmentVars(), 3);
  1598. edit->addEnvironmentVar("foobar", NULL); // remove
  1599. OIIO_CHECK_EQUAL(edit->getNumEnvironmentVars(), 2);
  1600. edit->clearEnvironmentVars();
  1601. edit->addEnvironmentVar("SHOW", "super");
  1602. edit->addEnvironmentVar("SHOT", "test");
  1603. edit->addEnvironmentVar("SEQ", "foo");
  1604. edit->addEnvironmentVar("test", "bar${cheese}");
  1605. edit->addEnvironmentVar("cheese", "chedder");
  1606. //Test
  1607. OCIO::LoggingLevel loglevel = OCIO::GetLoggingLevel();
  1608. OCIO::SetLoggingLevel(OCIO::LOGGING_LEVEL_DEBUG);
  1609. is.str(SIMPLE_PROFILE2);
  1610. OCIO::ConstConfigRcPtr noenv;
  1611. OIIO_CHECK_NO_THOW(noenv = OCIO::Config::CreateFromStream(is));
  1612. OIIO_CHECK_ASSERT(strcmp(noenv->getCurrentContext()->resolveStringVar("${TASK}"),
  1613. "lighting") == 0);
  1614. OCIO::SetLoggingLevel(loglevel);
  1615. OIIO_CHECK_EQUAL(edit->getEnvironmentMode(), OCIO::ENV_ENVIRONMENT_LOAD_PREDEFINED);
  1616. edit->setEnvironmentMode(OCIO::ENV_ENVIRONMENT_LOAD_ALL);
  1617. OIIO_CHECK_EQUAL(edit->getEnvironmentMode(), OCIO::ENV_ENVIRONMENT_LOAD_ALL);
  1618. }
  1619. }
  1620. #endif // OCIO_UNIT_TEST