summaryrefslogtreecommitdiff
path: root/src/core/FileFormatIridasCube.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/FileFormatIridasCube.cpp')
-rw-r--r--src/core/FileFormatIridasCube.cpp398
1 files changed, 398 insertions, 0 deletions
diff --git a/src/core/FileFormatIridasCube.cpp b/src/core/FileFormatIridasCube.cpp
new file mode 100644
index 0000000..6b2a496
--- /dev/null
+++ b/src/core/FileFormatIridasCube.cpp
@@ -0,0 +1,398 @@
+/*
+Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
+All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+* Neither the name of Sony Pictures Imageworks nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <cstdio>
+#include <cstring>
+#include <iterator>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Lut1DOp.h"
+#include "Lut3DOp.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+/*
+
+http://doc.iridas.com/index.php/LUT_Formats
+
+#comments start with '#'
+#title is currently ignored, but it's not an error to enter one
+TITLE "title"
+
+#LUT_1D_SIZE M or
+#LUT_3D_SIZE M
+#where M is the size of the texture
+#a 3D texture has the size M x M x M
+#e.g. LUT_3D_SIZE 16 creates a 16 x 16 x 16 3D texture
+LUT_3D_SIZE 2
+
+#Default input value range (domain) is 0.0 (black) to 1.0 (white)
+#Specify other min/max values to map the cube to any custom input
+#range you wish to use, for example if you're working with HDR data
+DOMAIN_MIN 0.0 0.0 0.0
+DOMAIN_MAX 1.0 1.0 1.0
+
+#for 1D textures, the data is simply a list of floating point values,
+#three per line, in RGB order
+#for 3D textures, the data is also RGB, and ordered in such a way
+#that the red coordinate changes fastest, then the green coordinate,
+#and finally, the blue coordinate changes slowest:
+0.0 0.0 0.0
+1.0 0.0 0.0
+0.0 1.0 0.0
+1.0 1.0 0.0
+0.0 0.0 1.0
+1.0 0.0 1.0
+0.0 1.0 1.0
+1.0 1.0 1.0
+
+#Note that the LUT data is not limited to any particular range
+#and can contain values under 0.0 and over 1.0
+#The processing application might however still clip the
+#output values to the 0.0 - 1.0 range, depending on the internal
+#precision of that application's pipeline
+#IRIDAS applications generally use a floating point pipeline
+#with little or no clipping
+
+
+*/
+
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ class LocalCachedFile : public CachedFile
+ {
+ public:
+ LocalCachedFile () :
+ has1D(false),
+ has3D(false)
+ {
+ lut1D = Lut1D::Create();
+ lut3D = Lut3D::Create();
+ };
+ ~LocalCachedFile() {};
+
+ bool has1D;
+ bool has3D;
+ Lut1DRcPtr lut1D;
+ Lut3DRcPtr lut3D;
+ };
+
+ typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr;
+
+
+
+ class LocalFileFormat : public FileFormat
+ {
+ public:
+
+ ~LocalFileFormat() {};
+
+ virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const;
+
+ virtual CachedFileRcPtr Read(std::istream & istream) const;
+
+ virtual void BuildFileOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ CachedFileRcPtr untypedCachedFile,
+ const FileTransform& fileTransform,
+ TransformDirection dir) const;
+ };
+
+ void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const
+ {
+ FormatInfo info;
+ info.name = "iridas_cube";
+ info.extension = "cube";
+ info.capabilities = FORMAT_CAPABILITY_READ;
+ formatInfoVec.push_back(info);
+ }
+
+ CachedFileRcPtr
+ LocalFileFormat::Read(std::istream & istream) const
+ {
+ // this shouldn't happen
+ if(!istream)
+ {
+ throw Exception ("File stream empty when trying to read Iridas .cube lut");
+ }
+
+ // Parse the file
+ std::vector<float> raw;
+
+ int size3d[] = { 0, 0, 0 };
+ int size1d = 0;
+
+ bool in1d = false;
+ bool in3d = false;
+
+ float domain_min[] = { 0.0f, 0.0f, 0.0f };
+ float domain_max[] = { 1.0f, 1.0f, 1.0f };
+
+ {
+ std::string line;
+ std::vector<std::string> parts;
+ std::vector<float> tmpfloats;
+
+ while(nextline(istream, line))
+ {
+ // All lines starting with '#' are comments
+ if(pystring::startswith(line,"#")) continue;
+
+ // Strip, lowercase, and split the line
+ pystring::split(pystring::lower(pystring::strip(line)), parts);
+ if(parts.empty()) continue;
+
+ if(pystring::lower(parts[0]) == "title")
+ {
+ // Optional, and currently unhandled
+ }
+ else if(pystring::lower(parts[0]) == "lut_1d_size")
+ {
+ if(parts.size() != 2 || !StringToInt( &size1d, parts[1].c_str()))
+ {
+ throw Exception("Malformed LUT_1D_SIZE tag in Iridas .cube lut.");
+ }
+
+ raw.reserve(3*size1d);
+ in1d = true;
+ }
+ else if(pystring::lower(parts[0]) == "lut_2d_size")
+ {
+ throw Exception("Unsupported Iridas .cube lut tag: 'LUT_2D_SIZE'.");
+ }
+ else if(pystring::lower(parts[0]) == "lut_3d_size")
+ {
+ int size = 0;
+
+ if(parts.size() != 2 || !StringToInt( &size, parts[1].c_str()))
+ {
+ throw Exception("Malformed LUT_3D_SIZE tag in Iridas .cube lut.");
+ }
+ size3d[0] = size;
+ size3d[1] = size;
+ size3d[2] = size;
+
+ raw.reserve(3*size3d[0]*size3d[1]*size3d[2]);
+ in3d = true;
+ }
+ else if(pystring::lower(parts[0]) == "domain_min")
+ {
+ if(parts.size() != 4 ||
+ !StringToFloat( &domain_min[0], parts[1].c_str()) ||
+ !StringToFloat( &domain_min[1], parts[2].c_str()) ||
+ !StringToFloat( &domain_min[2], parts[3].c_str()))
+ {
+ throw Exception("Malformed DOMAIN_MIN tag in Iridas .cube lut.");
+ }
+ }
+ else if(pystring::lower(parts[0]) == "domain_max")
+ {
+ if(parts.size() != 4 ||
+ !StringToFloat( &domain_max[0], parts[1].c_str()) ||
+ !StringToFloat( &domain_max[1], parts[2].c_str()) ||
+ !StringToFloat( &domain_max[2], parts[3].c_str()))
+ {
+ throw Exception("Malformed DOMAIN_MAX tag in Iridas .cube lut.");
+ }
+ }
+ else
+ {
+ // It must be a float triple!
+
+ if(!StringVecToFloatVec(tmpfloats, parts) || tmpfloats.size() != 3)
+ {
+ std::ostringstream os;
+ os << "Malformed color triples specified in Iridas .cube lut:";
+ os << "'" << line << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ for(int i=0; i<3; ++i)
+ {
+ raw.push_back(tmpfloats[i]);
+ }
+ }
+ }
+ }
+
+ // Interpret the parsed data, validate lut sizes
+
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+
+ if(in1d)
+ {
+ if(size1d != static_cast<int>(raw.size()/3))
+ {
+ std::ostringstream os;
+ os << "Parse error in Iridas .cube lut. ";
+ os << "Incorrect number of lut1d entries. ";
+ os << "Found " << raw.size()/3 << ", expected " << size1d << ".";
+ throw Exception(os.str().c_str());
+ }
+
+ // Reformat 1D data
+ if(size1d>0)
+ {
+ cachedFile->has1D = true;
+ memcpy(cachedFile->lut1D->from_min, domain_min, 3*sizeof(float));
+ memcpy(cachedFile->lut1D->from_max, domain_max, 3*sizeof(float));
+
+ for(int channel=0; channel<3; ++channel)
+ {
+ cachedFile->lut1D->luts[channel].resize(size1d);
+ for(int i=0; i<size1d; ++i)
+ {
+ cachedFile->lut1D->luts[channel][i] = raw[3*i+channel];
+ }
+ }
+
+ // 1e-5 rel error is a good threshold when float numbers near 0
+ // are written out with 6 decimal places of precision. This is
+ // a bit aggressive, I.e., changes in the 6th decimal place will
+ // be considered roundoff error, but changes in the 5th decimal
+ // will be considered lut 'intent'.
+ // 1.0
+ // 1.000005 equal to 1.0
+ // 1.000007 equal to 1.0
+ // 1.000010 not equal
+ // 0.0
+ // 0.000001 not equal
+
+ cachedFile->lut1D->maxerror = 1e-5f;
+ cachedFile->lut1D->errortype = ERROR_RELATIVE;
+ }
+ }
+ else if(in3d)
+ {
+ cachedFile->has3D = true;
+
+ if(size3d[0]*size3d[1]*size3d[2] != static_cast<int>(raw.size()/3))
+ {
+ std::ostringstream os;
+ os << "Parse error in Iridas .cube lut. ";
+ os << "Incorrect number of lut3d entries. ";
+ os << "Found " << raw.size()/3 << ", expected " << size3d[0]*size3d[1]*size3d[2] << ".";
+ throw Exception(os.str().c_str());
+ }
+
+ // Reformat 3D data
+ memcpy(cachedFile->lut3D->from_min, domain_min, 3*sizeof(float));
+ memcpy(cachedFile->lut3D->from_max, domain_max, 3*sizeof(float));
+ cachedFile->lut3D->size[0] = size3d[0];
+ cachedFile->lut3D->size[1] = size3d[1];
+ cachedFile->lut3D->size[2] = size3d[2];
+ cachedFile->lut3D->lut = raw;
+ }
+ else
+ {
+ std::ostringstream os;
+ os << "Parse error in Iridas .cube lut. ";
+ os << "Lut type (1D/3D) unspecified.";
+ throw Exception(os.str().c_str());
+ }
+
+ return cachedFile;
+ }
+
+ void
+ LocalFileFormat::BuildFileOps(OpRcPtrVec & ops,
+ const Config& /*config*/,
+ const ConstContextRcPtr & /*context*/,
+ CachedFileRcPtr untypedCachedFile,
+ const FileTransform& fileTransform,
+ TransformDirection dir) const
+ {
+ LocalCachedFileRcPtr cachedFile = DynamicPtrCast<LocalCachedFile>(untypedCachedFile);
+
+ // This should never happen.
+ if(!cachedFile)
+ {
+ std::ostringstream os;
+ os << "Cannot build Iridas .cube Op. Invalid cache type.";
+ throw Exception(os.str().c_str());
+ }
+
+ TransformDirection newDir = CombineTransformDirections(dir,
+ fileTransform.getDirection());
+ if(newDir == TRANSFORM_DIR_UNKNOWN)
+ {
+ std::ostringstream os;
+ os << "Cannot build file format transform,";
+ os << " unspecified transform direction.";
+ throw Exception(os.str().c_str());
+ }
+
+ // TODO: INTERP_LINEAR should not be hard-coded.
+ // Instead query 'highest' interpolation?
+ // (right now, it's linear). If cubic is added, consider
+ // using it
+
+ if(newDir == TRANSFORM_DIR_FORWARD)
+ {
+ if(cachedFile->has1D)
+ {
+ CreateLut1DOp(ops, cachedFile->lut1D,
+ INTERP_LINEAR, newDir);
+ }
+ if(cachedFile->has3D)
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ }
+ else if(newDir == TRANSFORM_DIR_INVERSE)
+ {
+ if(cachedFile->has3D)
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ if(cachedFile->has1D)
+ {
+ CreateLut1DOp(ops, cachedFile->lut1D,
+ INTERP_LINEAR, newDir);
+ }
+ }
+ }
+ }
+
+ FileFormat * CreateFileFormatIridasCube()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+///////////////////////////////////////////////////////////////////////////////