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 <cstdio>
30 #include <cstring>
31 #include <iterator>
32 #include <algorithm>
33 
34 #include <OpenColorIO/OpenColorIO.h>
35 
36 #include "FileTransform.h"
37 #include "Lut1DOp.h"
38 #include "Lut3DOp.h"
39 #include "ParseUtils.h"
40 #include "pystring/pystring.h"
41 
42 /*
43 
44 Iridas itx format
45 LUT_3D_SIZE M
46 
47 #LUT_3D_SIZE M
48 #where M is the size of the texture
49 #a 3D texture has the size M x M x M
50 #e.g. LUT_3D_SIZE 16 creates a 16 x 16 x 16 3D texture
51 
52 #for 1D textures, the data is simply a list of floating point values,
53 #three per line, in RGB order
54 #for 3D textures, the data is also RGB, and ordered in such a way
55 #that the red coordinate changes fastest, then the green coordinate,
56 #and finally, the blue coordinate changes slowest:
57 0.0 0.0 0.0
58 1.0 0.0 0.0
59 0.0 1.0 0.0
60 1.0 1.0 0.0
61 0.0 0.0 1.0
62 1.0 0.0 1.0
63 0.0 1.0 1.0
64 1.0 1.0 1.0
65 */
66 
67 
68 OCIO_NAMESPACE_ENTER
69 {
70     namespace
71     {
72         class LocalCachedFile : public CachedFile
73         {
74         public:
75             LocalCachedFile ()
76             {
77                 lut3D = Lut3D::Create();
78             };
79             ~LocalCachedFile() {};
80 
81             Lut3DRcPtr lut3D;
82         };
83 
84         typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr;
85 
86 
87 
88         class LocalFileFormat : public FileFormat
89         {
90         public:
91 
92             ~LocalFileFormat() {};
93 
94             virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const;
95 
96             virtual CachedFileRcPtr Read(std::istream & istream) const;
97 
98             virtual void Write(const Baker & baker,
99                                const std::string & formatName,
100                                std::ostream & ostream) const;
101 
102             virtual void BuildFileOps(OpRcPtrVec & ops,
103                          const Config& config,
104                          const ConstContextRcPtr & context,
105                          CachedFileRcPtr untypedCachedFile,
106                          const FileTransform& fileTransform,
107                          TransformDirection dir) const;
108         };
109 
110         void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const
111         {
112             FormatInfo info;
113             info.name = "iridas_itx";
114             info.extension = "itx";
115             info.capabilities = (FORMAT_CAPABILITY_READ | FORMAT_CAPABILITY_WRITE);
116             formatInfoVec.push_back(info);
117         }
118 
119         CachedFileRcPtr
120         LocalFileFormat::Read(std::istream & istream) const
121         {
122             // this shouldn't happen
123             if(!istream)
124             {
125                 throw Exception ("File stream empty when trying to read Iridas .itx lut");
126             }
127 
128             // Parse the file
129             std::vector<float> raw;
130 
131             int size3d[] = { 0, 0, 0 };
132             bool in3d = false;
133 
134             {
135                 std::string line;
136                 std::vector<std::string> parts;
137                 std::vector<float> tmpfloats;
138 
139                 while(nextline(istream, line))
140                 {
141                     // All lines starting with '#' are comments
142                     if(pystring::startswith(line,"#")) continue;
143 
144                     // Strip, lowercase, and split the line
145                     pystring::split(pystring::lower(pystring::strip(line)), parts);
146                     if(parts.empty()) continue;
147 
148                     if(pystring::lower(parts[0]) == "lut_3d_size")
149                     {
150                         int size = 0;
151 
152                         if(parts.size() != 2 || !StringToInt( &size, parts[1].c_str()))
153                         {
154                             throw Exception("Malformed LUT_3D_SIZE tag in Iridas .itx lut.");
155                         }
156                         size3d[0] = size;
157                         size3d[1] = size;
158                         size3d[2] = size;
159 
160                         raw.reserve(3*size3d[0]*size3d[1]*size3d[2]);
161                         in3d = true;
162                     }
163                     else if(in3d)
164                     {
165                         // It must be a float triple!
166 
167                         if(!StringVecToFloatVec(tmpfloats, parts) || tmpfloats.size() != 3)
168                         {
169                             std::ostringstream os;
170                             os << "Malformed color triples specified in Iridas .itx lut:";
171                             os << "'" << line << "'.";
172                             throw Exception(os.str().c_str());
173                         }
174 
175                         for(int i=0; i<3; ++i)
176                         {
177                             raw.push_back(tmpfloats[i]);
178                         }
179                     }
180                 }
181             }
182 
183             // Interpret the parsed data, validate lut sizes
184 
185             LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
186 
187             if(in3d)
188             {
189                 if(size3d[0]*size3d[1]*size3d[2] != static_cast<int>(raw.size()/3))
190                 {
191                     std::ostringstream os;
192                     os << "Parse error in Iridas .itx lut. ";
193                     os << "Incorrect number of lut3d entries. ";
194                     os << "Found " << raw.size()/3 << ", expected " << size3d[0]*size3d[1]*size3d[2] << ".";
195                     throw Exception(os.str().c_str());
196                 }
197 
198                 // Reformat 3D data
199                 cachedFile->lut3D->size[0] = size3d[0];
200                 cachedFile->lut3D->size[1] = size3d[1];
201                 cachedFile->lut3D->size[2] = size3d[2];
202                 cachedFile->lut3D->lut = raw;
203             }
204             else
205             {
206                 std::ostringstream os;
207                 os << "Parse error in Iridas .itx lut. ";
208                 os << "Lut type (1D/3D) unspecified.";
209                 throw Exception(os.str().c_str());
210             }
211 
212             return cachedFile;
213         }
214 
215         void LocalFileFormat::Write(const Baker & baker,
216                                     const std::string & formatName,
217                                     std::ostream & ostream) const
218         {
219             int DEFAULT_CUBE_SIZE = 64;
220 
221             if(formatName != "iridas_itx")
222             {
223                 std::ostringstream os;
224                 os << "Unknown 3dl format name, '";
225                 os << formatName << "'.";
226                 throw Exception(os.str().c_str());
227             }
228 
229             ConstConfigRcPtr config = baker.getConfig();
230 
231             int cubeSize = baker.getCubeSize();
232             if(cubeSize==-1) cubeSize = DEFAULT_CUBE_SIZE;
233             cubeSize = std::max(2, cubeSize); // smallest cube is 2x2x2
234 
235             std::vector<float> cubeData;
236             cubeData.resize(cubeSize*cubeSize*cubeSize*3);
237             GenerateIdentityLut3D(&cubeData[0], cubeSize, 3, LUT3DORDER_FAST_RED);
238             PackedImageDesc cubeImg(&cubeData[0], cubeSize*cubeSize*cubeSize, 1, 3);
239 
240             // Apply our conversion from the input space to the output space.
241             ConstProcessorRcPtr inputToTarget;
242             std::string looks = baker.getLooks();
243             if (!looks.empty())
244             {
245                 LookTransformRcPtr transform = LookTransform::Create();
246                 transform->setLooks(looks.c_str());
247                 transform->setSrc(baker.getInputSpace());
248                 transform->setDst(baker.getTargetSpace());
249                 inputToTarget = config->getProcessor(transform,
250                     TRANSFORM_DIR_FORWARD);
251             }
252             else
253             {
254                 inputToTarget = config->getProcessor(baker.getInputSpace(),
255                     baker.getTargetSpace());
256             }
257             inputToTarget->apply(cubeImg);
258 
259             // Write out the file.
260             // For for maximum compatibility with other apps, we will
261             // not utilize the shaper or output any metadata
262 
263             ostream << "LUT_3D_SIZE " << cubeSize << "\n";
264             if(cubeSize < 2)
265             {
266                 throw Exception("Internal cube size exception.");
267             }
268 
269             // Set to a fixed 6 decimal precision
270             ostream.setf(std::ios::fixed, std::ios::floatfield);
271             ostream.precision(6);
272             for(int i=0; i<cubeSize*cubeSize*cubeSize; ++i)
273             {
274                 float r = cubeData[3*i+0];
275                 float g = cubeData[3*i+1];
276                 float b = cubeData[3*i+2];
277                 ostream << r << " " << g << " " << b << "\n";
278             }
279             ostream << "\n";
280         }
281 
282 
283         void
284         LocalFileFormat::BuildFileOps(OpRcPtrVec & ops,
285                                       const Config& /*config*/,
286                                       const ConstContextRcPtr & /*context*/,
287                                       CachedFileRcPtr untypedCachedFile,
288                                       const FileTransform& fileTransform,
289                                       TransformDirection dir) const
290         {
291             LocalCachedFileRcPtr cachedFile = DynamicPtrCast<LocalCachedFile>(untypedCachedFile);
292 
293             // This should never happen.
294             if(!cachedFile)
295             {
296                 std::ostringstream os;
297                 os << "Cannot build Iridas .itx Op. Invalid cache type.";
298                 throw Exception(os.str().c_str());
299             }
300 
301             TransformDirection newDir = CombineTransformDirections(dir,
302                 fileTransform.getDirection());
303             if(newDir == TRANSFORM_DIR_UNKNOWN)
304             {
305                 std::ostringstream os;
306                 os << "Cannot build file format transform,";
307                 os << " unspecified transform direction.";
308                 throw Exception(os.str().c_str());
309             }
310 
311             // TODO: INTERP_LINEAR should not be hard-coded.
312             // Instead query 'highest' interpolation?
313             // (right now, it's linear). If cubic is added, consider
314             // using it
315 
316             if(newDir == TRANSFORM_DIR_FORWARD)
317             {
318                 CreateLut3DOp(ops, cachedFile->lut3D,
319                                   fileTransform.getInterpolation(), newDir);
320             }
321             else if(newDir == TRANSFORM_DIR_INVERSE)
322             {
323                 CreateLut3DOp(ops, cachedFile->lut3D,
324                                   fileTransform.getInterpolation(), newDir);
325             }
326         }
327     }
328 
329     FileFormat * CreateFileFormatIridasItx()
330     {
331         return new LocalFileFormat();
332     }
333 }
334 OCIO_NAMESPACE_EXIT
335 
336 
337 ///////////////////////////////////////////////////////////////////////////////
338