1 /*
2 Copyright (c) 2014 Cinesite VFX Ltd, 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 <map>
30 #include <tinyxml.h>
31 
32 #include <OpenColorIO/OpenColorIO.h>
33 
34 #include "CDLTransform.h"
35 #include "FileTransform.h"
36 #include "OpBuilders.h"
37 #include "ParseUtils.h"
38 #include "pystring/pystring.h"
39 
40 OCIO_NAMESPACE_ENTER
41 {
42     ////////////////////////////////////////////////////////////////
43 
44     namespace
45     {
46         class LocalCachedFile : public CachedFile
47         {
48         public:
49             LocalCachedFile () {};
50             ~LocalCachedFile() {};
51 
52             CDLTransformMap transformMap;
53             CDLTransformVec transformVec;
54         };
55 
56         typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr;
57         typedef OCIO_SHARED_PTR<TiXmlDocument> TiXmlDocumentRcPtr;
58 
59 
60 
61         class LocalFileFormat : public FileFormat
62         {
63         public:
64 
65             ~LocalFileFormat() {};
66 
67             virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const;
68 
69             virtual CachedFileRcPtr Read(std::istream & istream) const;
70 
71             virtual void BuildFileOps(OpRcPtrVec & ops,
72                                       const Config& config,
73                                       const ConstContextRcPtr & context,
74                                       CachedFileRcPtr untypedCachedFile,
75                                       const FileTransform& fileTransform,
76                                       TransformDirection dir) const;
77         };
78 
79         void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const
80         {
81             FormatInfo info;
82             info.name = "ColorDecisionList";
83             info.extension = "cdl";
84             info.capabilities = FORMAT_CAPABILITY_READ;
85             formatInfoVec.push_back(info);
86         }
87 
88         // Try and load the format
89         // Raise an exception if it can't be loaded.
90 
91         CachedFileRcPtr LocalFileFormat::Read(std::istream & istream) const
92         {
93             std::ostringstream rawdata;
94             rawdata << istream.rdbuf();
95 
96             LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
97 
98             TiXmlDocumentRcPtr doc = TiXmlDocumentRcPtr(new TiXmlDocument());
99             doc->Parse(rawdata.str().c_str());
100 
101             if(doc->Error())
102             {
103                 std::ostringstream os;
104                 os << "XML Parse Error. ";
105                 os << doc->ErrorDesc() << " (line ";
106                 os << doc->ErrorRow() << ", character ";
107                 os << doc->ErrorCol() << ")";
108                 throw Exception(os.str().c_str());
109             }
110 
111             TiXmlElement* rootElement = doc->RootElement();
112             GetCDLTransforms(cachedFile->transformMap,
113                              cachedFile->transformVec,
114                              rootElement);
115             return cachedFile;
116         }
117 
118         void
119         LocalFileFormat::BuildFileOps(OpRcPtrVec & ops,
120                                       const Config& config,
121                                       const ConstContextRcPtr & context,
122                                       CachedFileRcPtr untypedCachedFile,
123                                       const FileTransform& fileTransform,
124                                       TransformDirection dir) const
125         {
126             LocalCachedFileRcPtr cachedFile = DynamicPtrCast<LocalCachedFile>(untypedCachedFile);
127 
128             // This should never happen.
129             if(!cachedFile)
130             {
131                 std::ostringstream os;
132                 os << "Cannot build .cdl Op. Invalid cache type.";
133                 throw Exception(os.str().c_str());
134             }
135 
136             TransformDirection newDir = CombineTransformDirections(dir,
137                 fileTransform.getDirection());
138             if(newDir == TRANSFORM_DIR_UNKNOWN)
139             {
140                 std::ostringstream os;
141                 os << "Cannot build ASC FileTransform,";
142                 os << " unspecified transform direction.";
143                 throw Exception(os.str().c_str());
144             }
145 
146             // Below this point, we should throw ExceptionMissingFile on
147             // errors rather than Exception
148             // This is because we've verified that the cdl file is valid,
149             // at now we're only querying whether the specified cccid can
150             // be found.
151             //
152             // Using ExceptionMissingFile enables the missing looks fallback
153             // mechanism to function properly.
154             // At the time ExceptionMissingFile was named, we errently assumed
155             // a 1:1 relationship between files and color corrections, which is
156             // not true for .cdl files.
157             //
158             // In a future OCIO release, it may be more appropriate to
159             // rename ExceptionMissingFile -> ExceptionMissingCorrection.
160             // But either way, it's what we should throw below.
161 
162             std::string cccid = fileTransform.getCCCId();
163             cccid = context->resolveStringVar(cccid.c_str());
164 
165             if(cccid.empty())
166             {
167                 std::ostringstream os;
168                 os << "You must specify which cccid to load from the ccc file";
169                 os << " (either by name or index).";
170                 throw ExceptionMissingFile(os.str().c_str());
171             }
172 
173             bool success=false;
174 
175             // Try to parse the cccid as a string id
176             CDLTransformMap::const_iterator iter = cachedFile->transformMap.find(cccid);
177             if(iter != cachedFile->transformMap.end())
178             {
179                 success = true;
180                 BuildCDLOps(ops,
181                             config,
182                             *(iter->second),
183                             newDir);
184             }
185 
186             // Try to parse the cccid as an integer index
187             // We want to be strict, so fail if leftover chars in the parse.
188             if(!success)
189             {
190                 int cccindex=0;
191                 if(StringToInt(&cccindex, cccid.c_str(), true))
192                 {
193                     int maxindex = ((int)cachedFile->transformVec.size())-1;
194                     if(cccindex<0 || cccindex>maxindex)
195                     {
196                         std::ostringstream os;
197                         os << "The specified cccindex " << cccindex;
198                         os << " is outside the valid range for this file [0,";
199                         os << maxindex << "]";
200                         throw ExceptionMissingFile(os.str().c_str());
201                     }
202 
203                     success = true;
204                     BuildCDLOps(ops,
205                                 config,
206                                 *cachedFile->transformVec[cccindex],
207                                 newDir);
208                 }
209             }
210 
211             if(!success)
212             {
213                 std::ostringstream os;
214                 os << "You must specify a valid cccid to load from the ccc file";
215                 os << " (either by name or index). id='" << cccid << "' ";
216                 os << "is not found in the file, and is not parsable as an ";
217                 os << "integer index.";
218                 throw ExceptionMissingFile(os.str().c_str());
219             }
220         }
221     }
222 
223     FileFormat * CreateFileFormatCDL()
224     {
225         return new LocalFileFormat();
226     }
227 }
228 OCIO_NAMESPACE_EXIT
229 
230