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