PageRenderTime 96ms CodeModel.GetById 53ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/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/*
  2Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
  3All Rights Reserved.
  4
  5Redistribution and use in source and binary forms, with or without
  6modification, are permitted provided that the following conditions are
  7met:
  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.
 16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 17"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 18LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 19A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 20OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 21SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 22LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 23DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 24THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 25(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 26OF 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
 42OCIO_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(CreateFileFormatCC());
208        registerFileFormat(CreateFileFormatCSP());
209        registerFileFormat(CreateFileFormatHDL());
210        registerFileFormat(CreateFileFormatIridasItx());
211        registerFileFormat(CreateFileFormatIridasCube());
212        registerFileFormat(CreateFileFormatIridasLook());
213        registerFileFormat(CreateFileFormatPandora());
214        registerFileFormat(CreateFileFormatSpi1D());
215        registerFileFormat(CreateFileFormatSpi3D());
216        registerFileFormat(CreateFileFormatSpiMtx());
217        registerFileFormat(CreateFileFormatTruelight());
218        registerFileFormat(CreateFileFormatVF());
219    }
220    
221    FormatRegistry::~FormatRegistry()
222    {
223    }
224    
225    FileFormat* FormatRegistry::getFileFormatByName(const std::string & name) const
226    {
227        FileFormatMap::const_iterator iter = m_formatsByName.find(
228            pystring::lower(name));
229        if(iter != m_formatsByName.end())
230            return iter->second;
231        return NULL;
232    }
233    
234    FileFormat* FormatRegistry::getFileFormatForExtension(const std::string & extension) const
235    {
236        FileFormatMap::const_iterator iter = m_formatsByExtension.find(
237            pystring::lower(extension));
238        if(iter != m_formatsByExtension.end())
239            return iter->second;
240        return NULL;
241    }
242    
243    void FormatRegistry::registerFileFormat(FileFormat* format)
244    {
245        FormatInfoVec formatInfoVec;
246        format->GetFormatInfo(formatInfoVec);
247        
248        if(formatInfoVec.empty())
249        {
250            std::ostringstream os;
251            os << "FileFormat Registry error. ";
252            os << "A file format did not provide the required format info.";
253            throw Exception(os.str().c_str());
254        }
255        
256        for(unsigned int i=0; i<formatInfoVec.size(); ++i)
257        {
258            if(formatInfoVec[i].capabilities == FORMAT_CAPABILITY_NONE)
259            {
260                std::ostringstream os;
261                os << "FileFormat Registry error. ";
262                os << "A file format does not define either reading or writing.";
263                throw Exception(os.str().c_str());
264            }
265            
266            if(getFileFormatByName(formatInfoVec[i].name))
267            {
268                std::ostringstream os;
269                os << "Cannot register multiple file formats named, '";
270                os << formatInfoVec[i].name << "'.";
271                throw Exception(os.str().c_str());
272            }
273            
274            m_formatsByName[formatInfoVec[i].name] = format;
275            
276            // For now, dont worry if multiple formats register the same extension
277            // TODO: keep track of all of em! (make the value a vector)
278            m_formatsByExtension[formatInfoVec[i].extension] = format;
279            
280            if(formatInfoVec[i].capabilities & FORMAT_CAPABILITY_READ)
281            {
282                m_readFormatNames.push_back(formatInfoVec[i].name);
283                m_readFormatExtensions.push_back(formatInfoVec[i].extension);
284            }
285            
286            if(formatInfoVec[i].capabilities & FORMAT_CAPABILITY_WRITE)
287            {
288                m_writeFormatNames.push_back(formatInfoVec[i].name);
289                m_writeFormatExtensions.push_back(formatInfoVec[i].extension);
290            }
291        }
292        
293        m_rawFormats.push_back(format);
294    }
295    
296    int FormatRegistry::getNumRawFormats() const
297    {
298        return static_cast<int>(m_rawFormats.size());
299    }
300    
301    FileFormat* FormatRegistry::getRawFormatByIndex(int index) const
302    {
303        if(index<0 || index>=getNumRawFormats())
304        {
305            return NULL;
306        }
307        
308        return m_rawFormats[index];
309    }
310    
311    int FormatRegistry::getNumFormats(int capability) const
312    {
313        if(capability == FORMAT_CAPABILITY_READ)
314        {
315            return static_cast<int>(m_readFormatNames.size());
316        }
317        else if(capability == FORMAT_CAPABILITY_WRITE)
318        {
319            return static_cast<int>(m_writeFormatNames.size());
320        }
321        return 0;
322    }
323    
324    const char * FormatRegistry::getFormatNameByIndex(int capability, int index) const
325    {
326        if(capability == FORMAT_CAPABILITY_READ)
327        {
328            if(index<0 || index>=static_cast<int>(m_readFormatNames.size()))
329            {
330                return "";
331            }
332            return m_readFormatNames[index].c_str();
333        }
334        else if(capability == FORMAT_CAPABILITY_WRITE)
335        {
336            if(index<0 || index>=static_cast<int>(m_readFormatNames.size()))
337            {
338                return "";
339            }
340            return m_writeFormatNames[index].c_str();
341        }
342        return "";
343    }
344    
345    const char * FormatRegistry::getFormatExtensionByIndex(int capability, int index) const
346    {
347        if(capability == FORMAT_CAPABILITY_READ)
348        {
349            if(index<0 || index>=static_cast<int>(m_readFormatExtensions.size()))
350            {
351                return "";
352            }
353            return m_readFormatExtensions[index].c_str();
354        }
355        else if(capability == FORMAT_CAPABILITY_WRITE)
356        {
357            if(index<0 || index>=static_cast<int>(m_writeFormatExtensions.size()))
358            {
359                return "";
360            }
361            return m_writeFormatExtensions[index].c_str();
362        }
363        return "";
364    }
365    
366    ///////////////////////////////////////////////////////////////////////////
367    
368    FileFormat::~FileFormat()
369    {
370    
371    }
372    
373    std::string FileFormat::getName() const
374    {
375        FormatInfoVec infoVec;
376        GetFormatInfo(infoVec);
377        if(infoVec.size()>0)
378        {
379            return infoVec[0].name;
380        }
381        return "Unknown Format";
382    }
383        
384    
385    
386    void FileFormat::Write(const Baker & /*baker*/,
387                           const std::string & formatName,
388                           std::ostream & /*ostream*/) const
389    {
390        std::ostringstream os;
391        os << "Format " << formatName << " does not support writing.";
392        throw Exception(os.str().c_str());
393    }
394    
395    namespace
396    {
397    
398        void LoadFileUncached(FileFormat * & returnFormat,
399            CachedFileRcPtr & returnCachedFile,
400            const std::string & filepath)
401        {
402            returnFormat = NULL;
403            
404            {
405                std::ostringstream os;
406                os << "Opening " << filepath;
407                LogDebug(os.str());
408            }
409            
410            // Open the filePath
411            std::ifstream filestream;
412            filestream.open(filepath.c_str(), std::ios_base::in);
413            if (!filestream.good())
414            {
415                std::ostringstream os;
416                os << "The specified FileTransform srcfile, '";
417                os << filepath <<"', could not be opened. ";
418                os << "Please confirm the file exists with appropriate read";
419                os << " permissions.";
420                throw Exception(os.str().c_str());
421            }
422            
423            // Try the initial format.
424            std::string primaryErrorText;
425            std::string root, extension;
426            pystring::os::path::splitext(root, extension, filepath);
427            extension = pystring::replace(extension,".","",1); // remove the leading '.'
428            
429            FormatRegistry & formatRegistry = FormatRegistry::GetInstance();
430            
431            FileFormat * primaryFormat = 
432                formatRegistry.getFileFormatForExtension(extension);
433            if(primaryFormat)
434            {
435                try
436                {
437                    CachedFileRcPtr cachedFile = primaryFormat->Read(filestream);
438                    
439                    if(IsDebugLoggingEnabled())
440                    {
441                        std::ostringstream os;
442                        os << "    Loaded primary format ";
443                        os << primaryFormat->getName();
444                        LogDebug(os.str());
445                    }
446                    
447                    returnFormat = primaryFormat;
448                    returnCachedFile = cachedFile;
449                    return;
450                }
451                catch(std::exception & e)
452                {
453                    primaryErrorText = e.what();
454                    
455                    if(IsDebugLoggingEnabled())
456                    {
457                        std::ostringstream os;
458                        os << "    Failed primary format ";
459                        os << primaryFormat->getName();
460                        os << ":  " << e.what();
461                        LogDebug(os.str());
462                    }
463                }
464            }
465            
466            filestream.clear();
467            filestream.seekg( std::ifstream::beg );
468            
469            // If this fails, try all other formats
470            CachedFileRcPtr cachedFile;
471            FileFormat * altFormat = NULL;
472            
473            for(int findex = 0;
474                findex<formatRegistry.getNumRawFormats();
475                ++findex)
476            {
477                altFormat = formatRegistry.getRawFormatByIndex(findex);
478                
479                // Dont bother trying the primaryFormat twice.
480                if(altFormat == primaryFormat) continue;
481                
482                try
483                {
484                    cachedFile = altFormat->Read(filestream);
485                    
486                    if(IsDebugLoggingEnabled())
487                    {
488                        std::ostringstream os;
489                        os << "    Loaded alt format ";
490                        os << altFormat->getName();
491                        LogDebug(os.str());
492                    }
493                    
494                    returnFormat = altFormat;
495                    returnCachedFile = cachedFile;
496                    return;
497                }
498                catch(std::exception & e)
499                {
500                    if(IsDebugLoggingEnabled())
501                    {
502                        std::ostringstream os;
503                        os << "    Failed alt format ";
504                        os << altFormat->getName();
505                        os << ":  " << e.what();
506                        LogDebug(os.str());
507                    }
508                }
509                
510                filestream.clear();
511                filestream.seekg( std::ifstream::beg );
512            }
513            
514            // No formats succeeded. Error out with a sensible message.
515            if(primaryFormat)
516            {
517                std::ostringstream os;
518                os << "The specified transform file '";
519                os << filepath <<"' could not be loaded. ";
520                os << primaryErrorText;
521                
522                throw Exception(os.str().c_str());
523            }
524            else
525            {
526                std::ostringstream os;
527                os << "The specified transform file '";
528                os << filepath <<"' does not appear to be a valid, known LUT file format.";
529                throw Exception(os.str().c_str());
530            }
531        }
532        
533        // We mutex both the main map and each item individually, so that
534        // the potentially slow file access wont block other lookups to already
535        // existing items. (Loads of the *same* file will mutually block though)
536        
537        struct FileCacheResult
538        {
539            Mutex mutex;
540            FileFormat * format;
541            bool ready;
542            bool error;
543            CachedFileRcPtr cachedFile;
544            std::string exceptionText;
545            
546            FileCacheResult():
547                format(NULL),
548                ready(false),
549                error(false)
550            {}
551        };
552        
553        typedef OCIO_SHARED_PTR<FileCacheResult> FileCacheResultPtr;
554        typedef std::map<std::string, FileCacheResultPtr> FileCacheMap;
555        
556        FileCacheMap g_fileCache;
557        Mutex g_fileCacheLock;
558        
559        void GetCachedFileAndFormat(
560            FileFormat * & format, CachedFileRcPtr & cachedFile,
561            const std::string & filepath)
562        {
563            // Load the file cache ptr from the global map
564            FileCacheResultPtr result;
565            {
566                AutoMutex lock(g_fileCacheLock);
567                FileCacheMap::iterator iter = g_fileCache.find(filepath);
568                if(iter != g_fileCache.end())
569                {
570                    result = iter->second;
571                }
572                else
573                {
574                    result = FileCacheResultPtr(new FileCacheResult);
575                    g_fileCache[filepath] = result;
576                }
577            }
578            
579            // If this file has already been loaded, return
580            // the result immediately
581            
582            AutoMutex lock(result->mutex);
583            if(!result->ready)
584            {
585                result->ready = true;
586                result->error = false;
587                
588                try
589                {
590                    LoadFileUncached(result->format,
591                                     result->cachedFile,
592                                     filepath);
593                }
594                catch(std::exception & e)
595                {
596                    result->error = true;
597                    result->exceptionText = e.what();
598                }
599                catch(...)
600                {
601                    result->error = true;
602                    std::ostringstream os;
603                    os << "An unknown error occurred in LoadFileUncached, ";
604                    os << filepath;
605                    result->exceptionText = os.str();
606                }
607            }
608            
609            if(result->error)
610            {
611                throw Exception(result->exceptionText.c_str());
612            }
613            else
614            {
615                format = result->format;
616                cachedFile = result->cachedFile;
617            }
618        }
619    } // namespace
620    
621    void ClearFileTransformCaches()
622    {
623        AutoMutex lock(g_fileCacheLock);
624        g_fileCache.clear();
625    }
626    
627    void BuildFileOps(OpRcPtrVec & ops,
628                      const Config& config,
629                      const ConstContextRcPtr & context,
630                      const FileTransform& fileTransform,
631                      TransformDirection dir)
632    {
633        std::string src = fileTransform.getSrc();
634        if(src.empty())
635        {
636            std::ostringstream os;
637            os << "The transform file has not been specified.";
638            throw Exception(os.str().c_str());
639        }
640        
641        std::string filepath = context->resolveFileLocation(src.c_str());
642        CreateFileNoOp(ops, filepath);
643        
644        FileFormat* format = NULL;
645        CachedFileRcPtr cachedFile;
646        
647        GetCachedFileAndFormat(format, cachedFile, filepath);
648        if(!format)
649        {
650            std::ostringstream os;
651            os << "The specified file load ";
652            os << filepath << " appeared to succeed, but no format ";
653            os << "was returned.";
654            throw Exception(os.str().c_str());
655        }
656        
657        if(!cachedFile.get())
658        {
659            std::ostringstream os;
660            os << "The specified file load ";
661            os << filepath << " appeared to succeed, but no cachedFile ";
662            os << "was returned.";
663            throw Exception(os.str().c_str());
664        }
665        
666        format->BuildFileOps(ops,
667                             config, context,
668                             cachedFile, fileTransform,
669                             dir);
670    }
671}
672OCIO_NAMESPACE_EXIT