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 "Lut1DOp.h"
33 #include "pystring/pystring.h"
34 
35 #include <cstdio>
36 #include <sstream>
37 
38 /*
39 Version 1
40 From -7.5 3.7555555555555555
41 Components 1
42 Length 4096
43 {
44         0.031525943963232252
45         0.045645604561056156
46 	...
47 }
48 
49 */
50 
51 
52 OCIO_NAMESPACE_ENTER
53 {
54     ////////////////////////////////////////////////////////////////
55 
56     namespace
57     {
58         class LocalCachedFile : public CachedFile
59         {
60         public:
61             LocalCachedFile()
62             {
63                 lut = Lut1D::Create();
64             };
65             ~LocalCachedFile() {};
66 
67             Lut1DRcPtr lut;
68         };
69 
70         typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr;
71 
72 
73         class LocalFileFormat : public FileFormat
74         {
75         public:
76 
77             ~LocalFileFormat() {};
78 
79             virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const;
80 
81             virtual CachedFileRcPtr Read(std::istream & istream) const;
82 
83             virtual void BuildFileOps(OpRcPtrVec & ops,
84                          const Config& config,
85                          const ConstContextRcPtr & context,
86                          CachedFileRcPtr untypedCachedFile,
87                          const FileTransform& fileTransform,
88                          TransformDirection dir) const;
89         };
90 
91         void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const
92         {
93             FormatInfo info;
94             info.name = "spi1d";
95             info.extension = "spi1d";
96             info.capabilities = FORMAT_CAPABILITY_READ;
97             formatInfoVec.push_back(info);
98         }
99 
100         // Try and load the format
101         // Raise an exception if it can't be loaded.
102 
103         CachedFileRcPtr LocalFileFormat::Read(std::istream & istream) const
104         {
105             Lut1DRcPtr lut1d = Lut1D::Create();
106 
107             // Parse Header Info
108             int lut_size = -1;
109             float from_min = 0.0;
110             float from_max = 1.0;
111             int version = -1;
112             int components = -1;
113 
114             const int MAX_LINE_SIZE = 4096;
115             char lineBuffer[MAX_LINE_SIZE];
116 
117             // PARSE HEADER INFO
118             {
119                 std::string headerLine("");
120                 do
121                 {
122                     istream.getline(lineBuffer, MAX_LINE_SIZE);
123                     headerLine = std::string(lineBuffer);
124                     if(pystring::startswith(headerLine, "Version"))
125                     {
126                         if(sscanf(lineBuffer, "Version %d", &version)!=1)
127                             throw Exception("Invalid 'Version' Tag");
128                     }
129                     else if(pystring::startswith(headerLine, "From"))
130                     {
131                         if(sscanf(lineBuffer, "From %f %f", &from_min, &from_max)!=2)
132                             throw Exception("Invalid 'From' Tag");
133                     }
134                     else if(pystring::startswith(headerLine, "Components"))
135                     {
136                         if(sscanf(lineBuffer, "Components %d", &components)!=1)
137                             throw Exception("Invalid 'Components' Tag");
138                     }
139                     else if(pystring::startswith(headerLine, "Length"))
140                     {
141                         if(sscanf(lineBuffer, "Length %d", &lut_size)!=1)
142                             throw Exception("Invalid 'Length' Tag");
143                     }
144                 }
145                 while (istream.good() && !pystring::startswith(headerLine,"{"));
146             }
147 
148             if(version == -1)
149                 throw Exception("Could not find 'Version' Tag");
150             if(version != 1)
151                 throw Exception("Only format version 1 supported.");
152             if (lut_size == -1)
153                 throw Exception("Could not find 'Length' Tag");
154             if (components == -1)
155                 throw Exception("Could not find 'Components' Tag");
156             if (components<0 || components>3)
157                 throw Exception("Components must be [1,2,3]");
158 
159 
160 
161             for(int i=0; i<3; ++i)
162             {
163                 lut1d->from_min[i] = from_min;
164                 lut1d->from_max[i] = from_max;
165                 lut1d->luts[i].clear();
166                 lut1d->luts[i].reserve(lut_size);
167             }
168 
169             {
170                 istream.getline(lineBuffer, MAX_LINE_SIZE);
171 
172                 int lineCount=0;
173                 float values[4];
174 
175                 while (istream.good())
176                 {
177                     // If 1 component is specificed, use x1 x1 x1 defaultA
178                     if(components==1 && sscanf(lineBuffer,"%f",&values[0])==1)
179                     {
180                         lut1d->luts[0].push_back(values[0]);
181                         lut1d->luts[1].push_back(values[0]);
182                         lut1d->luts[2].push_back(values[0]);
183                         ++lineCount;
184                     }
185                     // If 2 components are specificed, use x1 x2 0.0
186                     else if(components==2 && sscanf(lineBuffer,"%f %f",&values[0],&values[1])==2)
187                     {
188                         lut1d->luts[0].push_back(values[0]);
189                         lut1d->luts[1].push_back(values[1]);
190                         lut1d->luts[2].push_back(0.0);
191                         ++lineCount;
192                     }
193                     // If 3 component is specificed, use x1 x2 x3 defaultA
194                     else if(components==3 && sscanf(lineBuffer,"%f %f %f",&values[0],&values[1],&values[2])==3)
195                     {
196                         lut1d->luts[0].push_back(values[0]);
197                         lut1d->luts[1].push_back(values[1]);
198                         lut1d->luts[2].push_back(values[2]);
199                         ++lineCount;
200                     }
201 
202                     if(lineCount == lut_size) break;
203 
204                     istream.getline(lineBuffer, MAX_LINE_SIZE);
205                 }
206 
207                 if(lineCount!=lut_size)
208                     throw Exception("Not enough entries found.");
209             }
210 
211             // 1e-5 rel error is a good threshold when float numbers near 0
212             // are written out with 6 decimal places of precision.  This is
213             // a bit aggressive, I.e., changes in the 6th decimal place will
214             // be considered roundoff error, but changes in the 5th decimal
215             // will be considered lut 'intent'.
216             // 1.0
217             // 1.000005 equal to 1.0
218             // 1.000007 equal to 1.0
219             // 1.000010 not equal
220             // 0.0
221             // 0.000001 not equal
222             lut1d->maxerror = 1e-5f;
223             lut1d->errortype = ERROR_RELATIVE;
224 
225             LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
226             cachedFile->lut = lut1d;
227             return cachedFile;
228         }
229 
230         void LocalFileFormat::BuildFileOps(OpRcPtrVec & ops,
231                                   const Config& /*config*/,
232                                   const ConstContextRcPtr & /*context*/,
233                                   CachedFileRcPtr untypedCachedFile,
234                                   const FileTransform& fileTransform,
235                                   TransformDirection dir) const
236         {
237             LocalCachedFileRcPtr cachedFile = DynamicPtrCast<LocalCachedFile>(untypedCachedFile);
238 
239             if(!cachedFile) // This should never happen.
240             {
241                 std::ostringstream os;
242                 os << "Cannot build Spi1D Op. Invalid cache type.";
243                 throw Exception(os.str().c_str());
244             }
245 
246             TransformDirection newDir = CombineTransformDirections(dir,
247                 fileTransform.getDirection());
248 
249             CreateLut1DOp(ops,
250                           cachedFile->lut,
251                           fileTransform.getInterpolation(),
252                           newDir);
253         }
254     }
255 
256     FileFormat * CreateFileFormatSpi1D()
257     {
258         return new LocalFileFormat();
259     }
260 }
261 OCIO_NAMESPACE_EXIT
262