PageRenderTime 58ms CodeModel.GetById 21ms app.highlight 32ms RepoModel.GetById 2ms app.codeStats 0ms

/src/core/FileFormatIridasCube.cpp

http://github.com/imageworks/OpenColorIO
C++ | 398 lines | 252 code | 48 blank | 98 comment | 58 complexity | 4c2ee10abe1f2dbc11e0c03a66331f17 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 <cstdio>
 30#include <cstring>
 31#include <iterator>
 32
 33#include <OpenColorIO/OpenColorIO.h>
 34
 35#include "FileTransform.h"
 36#include "Lut1DOp.h"
 37#include "Lut3DOp.h"
 38#include "ParseUtils.h"
 39#include "pystring/pystring.h"
 40
 41/*
 42
 43http://doc.iridas.com/index.php/LUT_Formats
 44
 45#comments start with '#'
 46#title is currently ignored, but it's not an error to enter one
 47TITLE "title"
 48
 49#LUT_1D_SIZE M or
 50#LUT_3D_SIZE M
 51#where M is the size of the texture
 52#a 3D texture has the size M x M x M
 53#e.g. LUT_3D_SIZE 16 creates a 16 x 16 x 16 3D texture
 54LUT_3D_SIZE 2 
 55
 56#Default input value range (domain) is 0.0 (black) to 1.0 (white)
 57#Specify other min/max values to map the cube to any custom input
 58#range you wish to use, for example if you're working with HDR data
 59DOMAIN_MIN 0.0 0.0 0.0
 60DOMAIN_MAX 1.0 1.0 1.0
 61
 62#for 1D textures, the data is simply a list of floating point values,
 63#three per line, in RGB order
 64#for 3D textures, the data is also RGB, and ordered in such a way
 65#that the red coordinate changes fastest, then the green coordinate,
 66#and finally, the blue coordinate changes slowest:
 670.0 0.0 0.0
 681.0 0.0 0.0
 690.0 1.0 0.0
 701.0 1.0 0.0
 710.0 0.0 1.0
 721.0 0.0 1.0
 730.0 1.0 1.0
 741.0 1.0 1.0
 75
 76#Note that the LUT data is not limited to any particular range
 77#and can contain values under 0.0 and over 1.0
 78#The processing application might however still clip the
 79#output values to the 0.0 - 1.0 range, depending on the internal
 80#precision of that application's pipeline
 81#IRIDAS applications generally use a floating point pipeline
 82#with little or no clipping
 83
 84
 85*/
 86
 87
 88OCIO_NAMESPACE_ENTER
 89{
 90    namespace
 91    {
 92        class LocalCachedFile : public CachedFile
 93        {
 94        public:
 95            LocalCachedFile () : 
 96                has1D(false),
 97                has3D(false)
 98            {
 99                lut1D = Lut1D::Create();
100                lut3D = Lut3D::Create();
101            };
102            ~LocalCachedFile() {};
103            
104            bool has1D;
105            bool has3D;
106            Lut1DRcPtr lut1D;
107            Lut3DRcPtr lut3D;
108        };
109        
110        typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr;
111        
112        
113        
114        class LocalFileFormat : public FileFormat
115        {
116        public:
117            
118            ~LocalFileFormat() {};
119            
120            virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const;
121            
122            virtual CachedFileRcPtr Read(std::istream & istream) const;
123            
124            virtual void BuildFileOps(OpRcPtrVec & ops,
125                         const Config& config,
126                         const ConstContextRcPtr & context,
127                         CachedFileRcPtr untypedCachedFile,
128                         const FileTransform& fileTransform,
129                         TransformDirection dir) const;
130        };
131        
132        void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const
133        {
134            FormatInfo info;
135            info.name = "iridas_cube";
136            info.extension = "cube";
137            info.capabilities = FORMAT_CAPABILITY_READ;
138            formatInfoVec.push_back(info);
139        }
140        
141        CachedFileRcPtr
142        LocalFileFormat::Read(std::istream & istream) const
143        {
144            // this shouldn't happen
145            if(!istream)
146            {
147                throw Exception ("File stream empty when trying to read Iridas .cube lut");
148            }
149            
150            // Parse the file
151            std::vector<float> raw;
152            
153            int size3d[] = { 0, 0, 0 };
154            int size1d = 0;
155            
156            bool in1d = false;
157            bool in3d = false;
158            
159            float domain_min[] = { 0.0f, 0.0f, 0.0f };
160            float domain_max[] = { 1.0f, 1.0f, 1.0f };
161            
162            {
163                std::string line;
164                std::vector<std::string> parts;
165                std::vector<float> tmpfloats;
166                
167                while(nextline(istream, line))
168                {
169                    // All lines starting with '#' are comments
170                    if(pystring::startswith(line,"#")) continue;
171                    
172                    // Strip, lowercase, and split the line
173                    pystring::split(pystring::lower(pystring::strip(line)), parts);
174                    if(parts.empty()) continue;
175                    
176                    if(pystring::lower(parts[0]) == "title")
177                    {
178                        // Optional, and currently unhandled
179                    }
180                    else if(pystring::lower(parts[0]) == "lut_1d_size")
181                    {
182                        if(parts.size() != 2 || !StringToInt( &size1d, parts[1].c_str()))
183                        {
184                            throw Exception("Malformed LUT_1D_SIZE tag in Iridas .cube lut.");
185                        }
186                        
187                        raw.reserve(3*size1d);
188                        in1d = true;
189                    }
190                    else if(pystring::lower(parts[0]) == "lut_2d_size")
191                    {
192                        throw Exception("Unsupported Iridas .cube lut tag: 'LUT_2D_SIZE'.");
193                    }
194                    else if(pystring::lower(parts[0]) == "lut_3d_size")
195                    {
196                        int size = 0;
197                        
198                        if(parts.size() != 2 || !StringToInt( &size, parts[1].c_str()))
199                        {
200                            throw Exception("Malformed LUT_3D_SIZE tag in Iridas .cube lut.");
201                        }
202                        size3d[0] = size;
203                        size3d[1] = size;
204                        size3d[2] = size;
205                        
206                        raw.reserve(3*size3d[0]*size3d[1]*size3d[2]);
207                        in3d = true;
208                    }
209                    else if(pystring::lower(parts[0]) == "domain_min")
210                    {
211                        if(parts.size() != 4 || 
212                            !StringToFloat( &domain_min[0], parts[1].c_str()) ||
213                            !StringToFloat( &domain_min[1], parts[2].c_str()) ||
214                            !StringToFloat( &domain_min[2], parts[3].c_str()))
215                        {
216                            throw Exception("Malformed DOMAIN_MIN tag in Iridas .cube lut.");
217                        }
218                    }
219                    else if(pystring::lower(parts[0]) == "domain_max")
220                    {
221                        if(parts.size() != 4 || 
222                            !StringToFloat( &domain_max[0], parts[1].c_str()) ||
223                            !StringToFloat( &domain_max[1], parts[2].c_str()) ||
224                            !StringToFloat( &domain_max[2], parts[3].c_str()))
225                        {
226                            throw Exception("Malformed DOMAIN_MAX tag in Iridas .cube lut.");
227                        }
228                    }
229                    else
230                    {
231                        // It must be a float triple!
232                        
233                        if(!StringVecToFloatVec(tmpfloats, parts) || tmpfloats.size() != 3)
234                        {
235                            std::ostringstream os;
236                            os << "Malformed color triples specified in Iridas .cube lut:";
237                            os << "'" << line << "'.";
238                            throw Exception(os.str().c_str());
239                        }
240                        
241                        for(int i=0; i<3; ++i)
242                        {
243                            raw.push_back(tmpfloats[i]);
244                        }
245                    }
246                }
247            }
248            
249            // Interpret the parsed data, validate lut sizes
250            
251            LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
252            
253            if(in1d)
254            {
255                if(size1d != static_cast<int>(raw.size()/3))
256                {
257                    std::ostringstream os;
258                    os << "Parse error in Iridas .cube lut. ";
259                    os << "Incorrect number of lut1d entries. ";
260                    os << "Found " << raw.size()/3 << ", expected " << size1d << ".";
261                    throw Exception(os.str().c_str());
262                }
263                
264                // Reformat 1D data
265                if(size1d>0)
266                {
267                    cachedFile->has1D = true;
268                    memcpy(cachedFile->lut1D->from_min, domain_min, 3*sizeof(float));
269                    memcpy(cachedFile->lut1D->from_max, domain_max, 3*sizeof(float));
270                    
271                    for(int channel=0; channel<3; ++channel)
272                    {
273                        cachedFile->lut1D->luts[channel].resize(size1d);
274                        for(int i=0; i<size1d; ++i)
275                        {
276                            cachedFile->lut1D->luts[channel][i] = raw[3*i+channel];
277                        }
278                    }
279                    
280                    // 1e-5 rel error is a good threshold when float numbers near 0
281                    // are written out with 6 decimal places of precision.  This is
282                    // a bit aggressive, I.e., changes in the 6th decimal place will
283                    // be considered roundoff error, but changes in the 5th decimal
284                    // will be considered lut 'intent'.
285                    // 1.0
286                    // 1.000005 equal to 1.0
287                    // 1.000007 equal to 1.0
288                    // 1.000010 not equal
289                    // 0.0
290                    // 0.000001 not equal
291                    
292                    cachedFile->lut1D->maxerror = 1e-5f;
293                    cachedFile->lut1D->errortype = ERROR_RELATIVE;
294                }
295            }
296            else if(in3d)
297            {
298                cachedFile->has3D = true;
299                
300                if(size3d[0]*size3d[1]*size3d[2] != static_cast<int>(raw.size()/3))
301                {
302                    std::ostringstream os;
303                    os << "Parse error in Iridas .cube lut. ";
304                    os << "Incorrect number of lut3d entries. ";
305                    os << "Found " << raw.size()/3 << ", expected " << size3d[0]*size3d[1]*size3d[2] << ".";
306                    throw Exception(os.str().c_str());
307                }
308                
309                // Reformat 3D data
310                memcpy(cachedFile->lut3D->from_min, domain_min, 3*sizeof(float));
311                memcpy(cachedFile->lut3D->from_max, domain_max, 3*sizeof(float));
312                cachedFile->lut3D->size[0] = size3d[0];
313                cachedFile->lut3D->size[1] = size3d[1];
314                cachedFile->lut3D->size[2] = size3d[2];
315                cachedFile->lut3D->lut = raw;
316            }
317            else
318            {
319                std::ostringstream os;
320                os << "Parse error in Iridas .cube lut. ";
321                os << "Lut type (1D/3D) unspecified.";
322                throw Exception(os.str().c_str());
323            }
324            
325            return cachedFile;
326        }
327        
328        void
329        LocalFileFormat::BuildFileOps(OpRcPtrVec & ops,
330                                      const Config& /*config*/,
331                                      const ConstContextRcPtr & /*context*/,
332                                      CachedFileRcPtr untypedCachedFile,
333                                      const FileTransform& fileTransform,
334                                      TransformDirection dir) const
335        {
336            LocalCachedFileRcPtr cachedFile = DynamicPtrCast<LocalCachedFile>(untypedCachedFile);
337            
338            // This should never happen.
339            if(!cachedFile)
340            {
341                std::ostringstream os;
342                os << "Cannot build Iridas .cube Op. Invalid cache type.";
343                throw Exception(os.str().c_str());
344            }
345            
346            TransformDirection newDir = CombineTransformDirections(dir,
347                fileTransform.getDirection());
348            if(newDir == TRANSFORM_DIR_UNKNOWN)
349            {
350                std::ostringstream os;
351                os << "Cannot build file format transform,";
352                os << " unspecified transform direction.";
353                throw Exception(os.str().c_str());
354            }
355            
356            // TODO: INTERP_LINEAR should not be hard-coded.
357            // Instead query 'highest' interpolation?
358            // (right now, it's linear). If cubic is added, consider
359            // using it
360            
361            if(newDir == TRANSFORM_DIR_FORWARD)
362            {
363                if(cachedFile->has1D)
364                {
365                    CreateLut1DOp(ops, cachedFile->lut1D,
366                                  INTERP_LINEAR, newDir);
367                }
368                if(cachedFile->has3D)
369                {
370                    CreateLut3DOp(ops, cachedFile->lut3D,
371                                  fileTransform.getInterpolation(), newDir);
372                }
373            }
374            else if(newDir == TRANSFORM_DIR_INVERSE)
375            {
376                if(cachedFile->has3D)
377                {
378                    CreateLut3DOp(ops, cachedFile->lut3D,
379                                  fileTransform.getInterpolation(), newDir);
380                }
381                if(cachedFile->has1D)
382                {
383                    CreateLut1DOp(ops, cachedFile->lut1D,
384                                  INTERP_LINEAR, newDir);
385                }
386            }
387        }
388    }
389    
390    FileFormat * CreateFileFormatIridasCube()
391    {
392        return new LocalFileFormat();
393    }
394}
395OCIO_NAMESPACE_EXIT
396
397
398///////////////////////////////////////////////////////////////////////////////