/src/core/FileTransform.cpp

http://github.com/imageworks/OpenColorIO · C++ · 672 lines · 524 code · 98 blank · 50 comment · 55 complexity · 534f1d0e75f1d20840c7d1296d79bfd6 MD5 · raw file

  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 <OpenColorIO/OpenColorIO.h>
  28. #include "FileTransform.h"
  29. #include "Logging.h"
  30. #include "Mutex.h"
  31. #include "NoOps.h"
  32. #include "PathUtils.h"
  33. #include "pystring/pystring.h"
  34. #include <fstream>
  35. #include <map>
  36. #include <sstream>
  37. OCIO_NAMESPACE_ENTER
  38. {
  39. FileTransformRcPtr FileTransform::Create()
  40. {
  41. return FileTransformRcPtr(new FileTransform(), &deleter);
  42. }
  43. void FileTransform::deleter(FileTransform* t)
  44. {
  45. delete t;
  46. }
  47. class FileTransform::Impl
  48. {
  49. public:
  50. TransformDirection dir_;
  51. std::string src_;
  52. std::string cccid_;
  53. Interpolation interp_;
  54. Impl() :
  55. dir_(TRANSFORM_DIR_FORWARD),
  56. interp_(INTERP_UNKNOWN)
  57. { }
  58. ~Impl()
  59. { }
  60. Impl& operator= (const Impl & rhs)
  61. {
  62. dir_ = rhs.dir_;
  63. src_ = rhs.src_;
  64. cccid_ = rhs.cccid_;
  65. interp_ = rhs.interp_;
  66. return *this;
  67. }
  68. };
  69. ///////////////////////////////////////////////////////////////////////////
  70. FileTransform::FileTransform()
  71. : m_impl(new FileTransform::Impl)
  72. {
  73. }
  74. TransformRcPtr FileTransform::createEditableCopy() const
  75. {
  76. FileTransformRcPtr transform = FileTransform::Create();
  77. *(transform->m_impl) = *m_impl;
  78. return transform;
  79. }
  80. FileTransform::~FileTransform()
  81. {
  82. delete m_impl;
  83. m_impl = NULL;
  84. }
  85. FileTransform& FileTransform::operator= (const FileTransform & rhs)
  86. {
  87. *m_impl = *rhs.m_impl;
  88. return *this;
  89. }
  90. TransformDirection FileTransform::getDirection() const
  91. {
  92. return getImpl()->dir_;
  93. }
  94. void FileTransform::setDirection(TransformDirection dir)
  95. {
  96. getImpl()->dir_ = dir;
  97. }
  98. const char * FileTransform::getSrc() const
  99. {
  100. return getImpl()->src_.c_str();
  101. }
  102. void FileTransform::setSrc(const char * src)
  103. {
  104. getImpl()->src_ = src;
  105. }
  106. const char * FileTransform::getCCCId() const
  107. {
  108. return getImpl()->cccid_.c_str();
  109. }
  110. void FileTransform::setCCCId(const char * cccid)
  111. {
  112. getImpl()->cccid_ = cccid;
  113. }
  114. Interpolation FileTransform::getInterpolation() const
  115. {
  116. return getImpl()->interp_;
  117. }
  118. void FileTransform::setInterpolation(Interpolation interp)
  119. {
  120. getImpl()->interp_ = interp;
  121. }
  122. int FileTransform::getNumFormats()
  123. {
  124. return FormatRegistry::GetInstance().getNumFormats(FORMAT_CAPABILITY_READ);
  125. }
  126. const char * FileTransform::getFormatNameByIndex(int index)
  127. {
  128. return FormatRegistry::GetInstance().getFormatNameByIndex(FORMAT_CAPABILITY_READ, index);
  129. }
  130. const char * FileTransform::getFormatExtensionByIndex(int index)
  131. {
  132. return FormatRegistry::GetInstance().getFormatExtensionByIndex(FORMAT_CAPABILITY_READ, index);
  133. }
  134. std::ostream& operator<< (std::ostream& os, const FileTransform& t)
  135. {
  136. os << "<FileTransform ";
  137. os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
  138. os << "interpolation=" << InterpolationToString(t.getInterpolation()) << ", ";
  139. os << "src='" << t.getSrc() << "', ";
  140. os << "cccid='" << t.getCCCId() << "'";
  141. os << ">";
  142. return os;
  143. }
  144. ///////////////////////////////////////////////////////////////////////////
  145. // NOTE: You must be mindful when editing this function.
  146. // to be resiliant to the static initialization order 'fiasco'
  147. //
  148. // See
  149. // http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14
  150. // http://stackoverflow.com/questions/335369/finding-c-static-initialization-order-problems
  151. // for more info.
  152. namespace
  153. {
  154. FormatRegistry* g_formatRegistry = NULL;
  155. Mutex g_formatRegistryLock;
  156. }
  157. FormatRegistry & FormatRegistry::GetInstance()
  158. {
  159. AutoMutex lock(g_formatRegistryLock);
  160. if(!g_formatRegistry)
  161. {
  162. g_formatRegistry = new FormatRegistry();
  163. }
  164. return *g_formatRegistry;
  165. }
  166. FormatRegistry::FormatRegistry()
  167. {
  168. registerFileFormat(CreateFileFormat3DL());
  169. registerFileFormat(CreateFileFormatCCC());
  170. registerFileFormat(CreateFileFormatCC());
  171. registerFileFormat(CreateFileFormatCSP());
  172. registerFileFormat(CreateFileFormatHDL());
  173. registerFileFormat(CreateFileFormatIridasItx());
  174. registerFileFormat(CreateFileFormatIridasCube());
  175. registerFileFormat(CreateFileFormatIridasLook());
  176. registerFileFormat(CreateFileFormatPandora());
  177. registerFileFormat(CreateFileFormatSpi1D());
  178. registerFileFormat(CreateFileFormatSpi3D());
  179. registerFileFormat(CreateFileFormatSpiMtx());
  180. registerFileFormat(CreateFileFormatTruelight());
  181. registerFileFormat(CreateFileFormatVF());
  182. }
  183. FormatRegistry::~FormatRegistry()
  184. {
  185. }
  186. FileFormat* FormatRegistry::getFileFormatByName(const std::string & name) const
  187. {
  188. FileFormatMap::const_iterator iter = m_formatsByName.find(
  189. pystring::lower(name));
  190. if(iter != m_formatsByName.end())
  191. return iter->second;
  192. return NULL;
  193. }
  194. FileFormat* FormatRegistry::getFileFormatForExtension(const std::string & extension) const
  195. {
  196. FileFormatMap::const_iterator iter = m_formatsByExtension.find(
  197. pystring::lower(extension));
  198. if(iter != m_formatsByExtension.end())
  199. return iter->second;
  200. return NULL;
  201. }
  202. void FormatRegistry::registerFileFormat(FileFormat* format)
  203. {
  204. FormatInfoVec formatInfoVec;
  205. format->GetFormatInfo(formatInfoVec);
  206. if(formatInfoVec.empty())
  207. {
  208. std::ostringstream os;
  209. os << "FileFormat Registry error. ";
  210. os << "A file format did not provide the required format info.";
  211. throw Exception(os.str().c_str());
  212. }
  213. for(unsigned int i=0; i<formatInfoVec.size(); ++i)
  214. {
  215. if(formatInfoVec[i].capabilities == FORMAT_CAPABILITY_NONE)
  216. {
  217. std::ostringstream os;
  218. os << "FileFormat Registry error. ";
  219. os << "A file format does not define either reading or writing.";
  220. throw Exception(os.str().c_str());
  221. }
  222. if(getFileFormatByName(formatInfoVec[i].name))
  223. {
  224. std::ostringstream os;
  225. os << "Cannot register multiple file formats named, '";
  226. os << formatInfoVec[i].name << "'.";
  227. throw Exception(os.str().c_str());
  228. }
  229. m_formatsByName[formatInfoVec[i].name] = format;
  230. // For now, dont worry if multiple formats register the same extension
  231. // TODO: keep track of all of em! (make the value a vector)
  232. m_formatsByExtension[formatInfoVec[i].extension] = format;
  233. if(formatInfoVec[i].capabilities & FORMAT_CAPABILITY_READ)
  234. {
  235. m_readFormatNames.push_back(formatInfoVec[i].name);
  236. m_readFormatExtensions.push_back(formatInfoVec[i].extension);
  237. }
  238. if(formatInfoVec[i].capabilities & FORMAT_CAPABILITY_WRITE)
  239. {
  240. m_writeFormatNames.push_back(formatInfoVec[i].name);
  241. m_writeFormatExtensions.push_back(formatInfoVec[i].extension);
  242. }
  243. }
  244. m_rawFormats.push_back(format);
  245. }
  246. int FormatRegistry::getNumRawFormats() const
  247. {
  248. return static_cast<int>(m_rawFormats.size());
  249. }
  250. FileFormat* FormatRegistry::getRawFormatByIndex(int index) const
  251. {
  252. if(index<0 || index>=getNumRawFormats())
  253. {
  254. return NULL;
  255. }
  256. return m_rawFormats[index];
  257. }
  258. int FormatRegistry::getNumFormats(int capability) const
  259. {
  260. if(capability == FORMAT_CAPABILITY_READ)
  261. {
  262. return static_cast<int>(m_readFormatNames.size());
  263. }
  264. else if(capability == FORMAT_CAPABILITY_WRITE)
  265. {
  266. return static_cast<int>(m_writeFormatNames.size());
  267. }
  268. return 0;
  269. }
  270. const char * FormatRegistry::getFormatNameByIndex(int capability, int index) const
  271. {
  272. if(capability == FORMAT_CAPABILITY_READ)
  273. {
  274. if(index<0 || index>=static_cast<int>(m_readFormatNames.size()))
  275. {
  276. return "";
  277. }
  278. return m_readFormatNames[index].c_str();
  279. }
  280. else if(capability == FORMAT_CAPABILITY_WRITE)
  281. {
  282. if(index<0 || index>=static_cast<int>(m_readFormatNames.size()))
  283. {
  284. return "";
  285. }
  286. return m_writeFormatNames[index].c_str();
  287. }
  288. return "";
  289. }
  290. const char * FormatRegistry::getFormatExtensionByIndex(int capability, int index) const
  291. {
  292. if(capability == FORMAT_CAPABILITY_READ)
  293. {
  294. if(index<0 || index>=static_cast<int>(m_readFormatExtensions.size()))
  295. {
  296. return "";
  297. }
  298. return m_readFormatExtensions[index].c_str();
  299. }
  300. else if(capability == FORMAT_CAPABILITY_WRITE)
  301. {
  302. if(index<0 || index>=static_cast<int>(m_writeFormatExtensions.size()))
  303. {
  304. return "";
  305. }
  306. return m_writeFormatExtensions[index].c_str();
  307. }
  308. return "";
  309. }
  310. ///////////////////////////////////////////////////////////////////////////
  311. FileFormat::~FileFormat()
  312. {
  313. }
  314. std::string FileFormat::getName() const
  315. {
  316. FormatInfoVec infoVec;
  317. GetFormatInfo(infoVec);
  318. if(infoVec.size()>0)
  319. {
  320. return infoVec[0].name;
  321. }
  322. return "Unknown Format";
  323. }
  324. void FileFormat::Write(const Baker & /*baker*/,
  325. const std::string & formatName,
  326. std::ostream & /*ostream*/) const
  327. {
  328. std::ostringstream os;
  329. os << "Format " << formatName << " does not support writing.";
  330. throw Exception(os.str().c_str());
  331. }
  332. namespace
  333. {
  334. void LoadFileUncached(FileFormat * & returnFormat,
  335. CachedFileRcPtr & returnCachedFile,
  336. const std::string & filepath)
  337. {
  338. returnFormat = NULL;
  339. {
  340. std::ostringstream os;
  341. os << "Opening " << filepath;
  342. LogDebug(os.str());
  343. }
  344. // Open the filePath
  345. std::ifstream filestream;
  346. filestream.open(filepath.c_str(), std::ios_base::in);
  347. if (!filestream.good())
  348. {
  349. std::ostringstream os;
  350. os << "The specified FileTransform srcfile, '";
  351. os << filepath <<"', could not be opened. ";
  352. os << "Please confirm the file exists with appropriate read";
  353. os << " permissions.";
  354. throw Exception(os.str().c_str());
  355. }
  356. // Try the initial format.
  357. std::string primaryErrorText;
  358. std::string root, extension;
  359. pystring::os::path::splitext(root, extension, filepath);
  360. extension = pystring::replace(extension,".","",1); // remove the leading '.'
  361. FormatRegistry & formatRegistry = FormatRegistry::GetInstance();
  362. FileFormat * primaryFormat =
  363. formatRegistry.getFileFormatForExtension(extension);
  364. if(primaryFormat)
  365. {
  366. try
  367. {
  368. CachedFileRcPtr cachedFile = primaryFormat->Read(filestream);
  369. if(IsDebugLoggingEnabled())
  370. {
  371. std::ostringstream os;
  372. os << " Loaded primary format ";
  373. os << primaryFormat->getName();
  374. LogDebug(os.str());
  375. }
  376. returnFormat = primaryFormat;
  377. returnCachedFile = cachedFile;
  378. return;
  379. }
  380. catch(std::exception & e)
  381. {
  382. primaryErrorText = e.what();
  383. if(IsDebugLoggingEnabled())
  384. {
  385. std::ostringstream os;
  386. os << " Failed primary format ";
  387. os << primaryFormat->getName();
  388. os << ": " << e.what();
  389. LogDebug(os.str());
  390. }
  391. }
  392. }
  393. filestream.clear();
  394. filestream.seekg( std::ifstream::beg );
  395. // If this fails, try all other formats
  396. CachedFileRcPtr cachedFile;
  397. FileFormat * altFormat = NULL;
  398. for(int findex = 0;
  399. findex<formatRegistry.getNumRawFormats();
  400. ++findex)
  401. {
  402. altFormat = formatRegistry.getRawFormatByIndex(findex);
  403. // Dont bother trying the primaryFormat twice.
  404. if(altFormat == primaryFormat) continue;
  405. try
  406. {
  407. cachedFile = altFormat->Read(filestream);
  408. if(IsDebugLoggingEnabled())
  409. {
  410. std::ostringstream os;
  411. os << " Loaded alt format ";
  412. os << altFormat->getName();
  413. LogDebug(os.str());
  414. }
  415. returnFormat = altFormat;
  416. returnCachedFile = cachedFile;
  417. return;
  418. }
  419. catch(std::exception & e)
  420. {
  421. if(IsDebugLoggingEnabled())
  422. {
  423. std::ostringstream os;
  424. os << " Failed alt format ";
  425. os << altFormat->getName();
  426. os << ": " << e.what();
  427. LogDebug(os.str());
  428. }
  429. }
  430. filestream.clear();
  431. filestream.seekg( std::ifstream::beg );
  432. }
  433. // No formats succeeded. Error out with a sensible message.
  434. if(primaryFormat)
  435. {
  436. std::ostringstream os;
  437. os << "The specified transform file '";
  438. os << filepath <<"' could not be loaded. ";
  439. os << primaryErrorText;
  440. throw Exception(os.str().c_str());
  441. }
  442. else
  443. {
  444. std::ostringstream os;
  445. os << "The specified transform file '";
  446. os << filepath <<"' does not appear to be a valid, known LUT file format.";
  447. throw Exception(os.str().c_str());
  448. }
  449. }
  450. // We mutex both the main map and each item individually, so that
  451. // the potentially slow file access wont block other lookups to already
  452. // existing items. (Loads of the *same* file will mutually block though)
  453. struct FileCacheResult
  454. {
  455. Mutex mutex;
  456. FileFormat * format;
  457. bool ready;
  458. bool error;
  459. CachedFileRcPtr cachedFile;
  460. std::string exceptionText;
  461. FileCacheResult():
  462. format(NULL),
  463. ready(false),
  464. error(false)
  465. {}
  466. };
  467. typedef OCIO_SHARED_PTR<FileCacheResult> FileCacheResultPtr;
  468. typedef std::map<std::string, FileCacheResultPtr> FileCacheMap;
  469. FileCacheMap g_fileCache;
  470. Mutex g_fileCacheLock;
  471. void GetCachedFileAndFormat(
  472. FileFormat * & format, CachedFileRcPtr & cachedFile,
  473. const std::string & filepath)
  474. {
  475. // Load the file cache ptr from the global map
  476. FileCacheResultPtr result;
  477. {
  478. AutoMutex lock(g_fileCacheLock);
  479. FileCacheMap::iterator iter = g_fileCache.find(filepath);
  480. if(iter != g_fileCache.end())
  481. {
  482. result = iter->second;
  483. }
  484. else
  485. {
  486. result = FileCacheResultPtr(new FileCacheResult);
  487. g_fileCache[filepath] = result;
  488. }
  489. }
  490. // If this file has already been loaded, return
  491. // the result immediately
  492. AutoMutex lock(result->mutex);
  493. if(!result->ready)
  494. {
  495. result->ready = true;
  496. result->error = false;
  497. try
  498. {
  499. LoadFileUncached(result->format,
  500. result->cachedFile,
  501. filepath);
  502. }
  503. catch(std::exception & e)
  504. {
  505. result->error = true;
  506. result->exceptionText = e.what();
  507. }
  508. catch(...)
  509. {
  510. result->error = true;
  511. std::ostringstream os;
  512. os << "An unknown error occurred in LoadFileUncached, ";
  513. os << filepath;
  514. result->exceptionText = os.str();
  515. }
  516. }
  517. if(result->error)
  518. {
  519. throw Exception(result->exceptionText.c_str());
  520. }
  521. else
  522. {
  523. format = result->format;
  524. cachedFile = result->cachedFile;
  525. }
  526. }
  527. } // namespace
  528. void ClearFileTransformCaches()
  529. {
  530. AutoMutex lock(g_fileCacheLock);
  531. g_fileCache.clear();
  532. }
  533. void BuildFileOps(OpRcPtrVec & ops,
  534. const Config& config,
  535. const ConstContextRcPtr & context,
  536. const FileTransform& fileTransform,
  537. TransformDirection dir)
  538. {
  539. std::string src = fileTransform.getSrc();
  540. if(src.empty())
  541. {
  542. std::ostringstream os;
  543. os << "The transform file has not been specified.";
  544. throw Exception(os.str().c_str());
  545. }
  546. std::string filepath = context->resolveFileLocation(src.c_str());
  547. CreateFileNoOp(ops, filepath);
  548. FileFormat* format = NULL;
  549. CachedFileRcPtr cachedFile;
  550. GetCachedFileAndFormat(format, cachedFile, filepath);
  551. if(!format)
  552. {
  553. std::ostringstream os;
  554. os << "The specified file load ";
  555. os << filepath << " appeared to succeed, but no format ";
  556. os << "was returned.";
  557. throw Exception(os.str().c_str());
  558. }
  559. if(!cachedFile.get())
  560. {
  561. std::ostringstream os;
  562. os << "The specified file load ";
  563. os << filepath << " appeared to succeed, but no cachedFile ";
  564. os << "was returned.";
  565. throw Exception(os.str().c_str());
  566. }
  567. format->BuildFileOps(ops,
  568. config, context,
  569. cachedFile, fileTransform,
  570. dir);
  571. }
  572. }
  573. OCIO_NAMESPACE_EXIT