summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/AllocationOp.cpp128
-rw-r--r--src/core/AllocationOp.h45
-rw-r--r--src/core/AllocationTransform.cpp182
-rw-r--r--src/core/Baker.cpp297
-rw-r--r--src/core/CDLTransform.cpp776
-rw-r--r--src/core/CDLTransform.h53
-rw-r--r--src/core/CMakeLists.txt93
-rw-r--r--src/core/Caching.cpp47
-rw-r--r--src/core/ColorSpace.cpp270
-rw-r--r--src/core/ColorSpaceTransform.cpp246
-rw-r--r--src/core/Config.cpp2223
-rw-r--r--src/core/Context.cpp364
-rw-r--r--src/core/DisplayTransform.cpp410
-rw-r--r--src/core/Exception.cpp74
-rw-r--r--src/core/ExponentOps.cpp372
-rw-r--r--src/core/ExponentOps.h50
-rw-r--r--src/core/ExponentTransform.cpp151
-rw-r--r--src/core/FileFormat3DL.cpp638
-rw-r--r--src/core/FileFormatCC.cpp151
-rw-r--r--src/core/FileFormatCCC.cpp195
-rw-r--r--src/core/FileFormatCSP.cpp1112
-rw-r--r--src/core/FileFormatHDL.cpp1481
-rw-r--r--src/core/FileFormatIridasCube.cpp398
-rw-r--r--src/core/FileFormatIridasItx.cpp336
-rw-r--r--src/core/FileFormatIridasLook.cpp1356
-rw-r--r--src/core/FileFormatPandora.cpp286
-rw-r--r--src/core/FileFormatSpi1D.cpp261
-rw-r--r--src/core/FileFormatSpi3D.cpp209
-rw-r--r--src/core/FileFormatSpiMtx.cpp195
-rw-r--r--src/core/FileFormatTruelight.cpp620
-rw-r--r--src/core/FileFormatVF.cpp297
-rw-r--r--src/core/FileTransform.cpp579
-rw-r--r--src/core/FileTransform.h156
-rw-r--r--src/core/GpuShaderDesc.cpp131
-rw-r--r--src/core/GpuShaderUtils.cpp193
-rw-r--r--src/core/GpuShaderUtils.h58
-rw-r--r--src/core/GroupTransform.cpp196
-rw-r--r--src/core/HashUtils.cpp71
-rw-r--r--src/core/HashUtils.h48
-rw-r--r--src/core/ImageDesc.cpp392
-rw-r--r--src/core/ImagePacking.cpp407
-rw-r--r--src/core/ImagePacking.h71
-rw-r--r--src/core/LogOps.cpp499
-rw-r--r--src/core/LogOps.h57
-rw-r--r--src/core/LogTransform.cpp157
-rw-r--r--src/core/Logging.cpp156
-rw-r--r--src/core/Logging.h47
-rw-r--r--src/core/Look.cpp163
-rw-r--r--src/core/LookParse.cpp309
-rw-r--r--src/core/LookParse.h79
-rw-r--r--src/core/LookTransform.cpp405
-rw-r--r--src/core/Lut1DOp.cpp1038
-rw-r--r--src/core/Lut1DOp.h107
-rw-r--r--src/core/Lut3DOp.cpp982
-rw-r--r--src/core/Lut3DOp.h114
-rw-r--r--src/core/MathUtils.cpp603
-rw-r--r--src/core/MathUtils.h186
-rw-r--r--src/core/MatrixOps.cpp788
-rw-r--r--src/core/MatrixOps.h75
-rw-r--r--src/core/MatrixTransform.cpp386
-rw-r--r--src/core/Mutex.h115
-rw-r--r--src/core/NoOps.cpp641
-rw-r--r--src/core/NoOps.h67
-rw-r--r--src/core/OCIOYaml.cpp1218
-rw-r--r--src/core/OCIOYaml.h125
-rw-r--r--src/core/Op.cpp126
-rw-r--r--src/core/Op.h136
-rw-r--r--src/core/OpBuilders.h125
-rw-r--r--src/core/OpOptimizers.cpp364
-rw-r--r--src/core/ParseUtils.cpp438
-rw-r--r--src/core/ParseUtils.h89
-rw-r--r--src/core/PathUtils.cpp229
-rw-r--r--src/core/PathUtils.h95
-rw-r--r--src/core/Platform.h202
-rw-r--r--src/core/PrivateTypes.h54
-rw-r--r--src/core/Processor.cpp640
-rw-r--r--src/core/Processor.h132
-rw-r--r--src/core/SSE.h37
-rw-r--r--src/core/ScanlineHelper.cpp123
-rw-r--r--src/core/ScanlineHelper.h78
-rw-r--r--src/core/Transform.cpp175
-rw-r--r--src/core/TruelightOp.cpp395
-rw-r--r--src/core/TruelightOp.h44
-rw-r--r--src/core/TruelightTransform.cpp365
-rw-r--r--src/core/UnitTest.cpp39
-rw-r--r--src/core/UnitTest.h38
-rw-r--r--src/core/md5/md5.cpp391
-rw-r--r--src/core/md5/md5.h97
-rw-r--r--src/core/pystring/pystring.cpp1658
-rw-r--r--src/core/pystring/pystring.h438
90 files changed, 30443 insertions, 0 deletions
diff --git a/src/core/AllocationOp.cpp b/src/core/AllocationOp.cpp
new file mode 100644
index 0000000..ed4c9db
--- /dev/null
+++ b/src/core/AllocationOp.cpp
@@ -0,0 +1,128 @@
+/*
+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 "AllocationOp.h"
+#include "LogOps.h"
+#include "MatrixOps.h"
+#include "Op.h"
+
+#include <OpenColorIO/OpenColorIO.h>
+
+OCIO_NAMESPACE_ENTER
+{
+
+ void CreateAllocationOps(OpRcPtrVec & ops,
+ const AllocationData & data,
+ TransformDirection dir)
+ {
+ if(data.allocation == ALLOCATION_UNIFORM)
+ {
+ float oldmin[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ float oldmax[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ float newmin[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ float newmax[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+
+ if(data.vars.size() >= 2)
+ {
+ for(int i=0; i<3; ++i)
+ {
+ oldmin[i] = data.vars[0];
+ oldmax[i] = data.vars[1];
+ }
+ }
+
+ CreateFitOp(ops,
+ oldmin, oldmax,
+ newmin, newmax,
+ dir);
+ }
+ else if(data.allocation == ALLOCATION_LG2)
+ {
+ float oldmin[4] = { -10.0f, -10.0f, -10.0f, 0.0f };
+ float oldmax[4] = { 6.0f, 6.0f, 6.0f, 1.0f };
+ float newmin[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ float newmax[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+
+ if(data.vars.size() >= 2)
+ {
+ for(int i=0; i<3; ++i)
+ {
+ oldmin[i] = data.vars[0];
+ oldmax[i] = data.vars[1];
+ }
+ }
+
+
+ // Log Settings
+ // output = k * log(mx+b, base) + kb
+
+ float k[3] = { 1.0f, 1.0f, 1.0f };
+ float m[3] = { 1.0f, 1.0f, 1.0f };
+ float b[3] = { 0.0f, 0.0f, 0.0f };
+ float base[3] = { 2.0f, 2.0f, 2.0f };
+ float kb[3] = { 0.0f, 0.0f, 0.0f };
+
+ if(data.vars.size() >= 3)
+ {
+ for(int i=0; i<3; ++i)
+ {
+ b[i] = data.vars[2];
+ }
+ }
+
+ if(dir == TRANSFORM_DIR_FORWARD)
+ {
+ CreateLogOp(ops, k, m, b, base, kb, dir);
+
+ CreateFitOp(ops,
+ oldmin, oldmax,
+ newmin, newmax,
+ dir);
+ }
+ else if(dir == TRANSFORM_DIR_INVERSE)
+ {
+ CreateFitOp(ops,
+ oldmin, oldmax,
+ newmin, newmax,
+ dir);
+
+ CreateLogOp(ops, k, m, b, base, kb, dir);
+ }
+ else
+ {
+ throw Exception("Cannot BuildAllocationOps, unspecified transform direction.");
+ }
+ }
+ else
+ {
+ throw Exception("Unsupported Allocation Type.");
+ }
+ }
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/AllocationOp.h b/src/core/AllocationOp.h
new file mode 100644
index 0000000..8fd491f
--- /dev/null
+++ b/src/core/AllocationOp.h
@@ -0,0 +1,45 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_ALLOCATIONOP_H
+#define INCLUDED_OCIO_ALLOCATIONOP_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Op.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ void CreateAllocationOps(OpRcPtrVec & ops,
+ const AllocationData & data,
+ TransformDirection dir);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/AllocationTransform.cpp b/src/core/AllocationTransform.cpp
new file mode 100644
index 0000000..592b36e
--- /dev/null
+++ b/src/core/AllocationTransform.cpp
@@ -0,0 +1,182 @@
+/*
+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 <cstring>
+#include <sstream>
+#include <vector>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "AllocationOp.h"
+#include "OpBuilders.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ AllocationTransformRcPtr AllocationTransform::Create()
+ {
+ return AllocationTransformRcPtr(new AllocationTransform(), &deleter);
+ }
+
+ void AllocationTransform::deleter(AllocationTransform* t)
+ {
+ delete t;
+ }
+
+
+ class AllocationTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+ Allocation allocation_;
+ std::vector<float> vars_;
+
+ Impl() :
+ dir_(TRANSFORM_DIR_FORWARD),
+ allocation_(ALLOCATION_UNIFORM)
+ { }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+ allocation_ = rhs.allocation_;
+ vars_ = rhs.vars_;
+ return *this;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ AllocationTransform::AllocationTransform()
+ : m_impl(new AllocationTransform::Impl)
+ {
+ }
+
+ TransformRcPtr AllocationTransform::createEditableCopy() const
+ {
+ AllocationTransformRcPtr transform = AllocationTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ AllocationTransform::~AllocationTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ AllocationTransform& AllocationTransform::operator= (const AllocationTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection AllocationTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void AllocationTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+
+ Allocation AllocationTransform::getAllocation() const
+ {
+ return getImpl()->allocation_;
+ }
+
+ void AllocationTransform::setAllocation(Allocation allocation)
+ {
+ getImpl()->allocation_ = allocation;
+ }
+
+ int AllocationTransform::getNumVars() const
+ {
+ return static_cast<int>(getImpl()->vars_.size());
+ }
+
+ void AllocationTransform::getVars(float * vars) const
+ {
+ if(!getImpl()->vars_.empty())
+ {
+ memcpy(vars,
+ &getImpl()->vars_[0],
+ getImpl()->vars_.size()*sizeof(float));
+ }
+ }
+
+ void AllocationTransform::setVars(int numvars, const float * vars)
+ {
+ getImpl()->vars_.resize(numvars);
+
+ if(!getImpl()->vars_.empty())
+ {
+ memcpy(&getImpl()->vars_[0],
+ vars,
+ numvars*sizeof(float));
+ }
+ }
+
+ std::ostream& operator<< (std::ostream& os, const AllocationTransform& t)
+ {
+ os << "<AllocationTransform ";
+ os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
+ os << ">\n";
+
+ return os;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ void BuildAllocationOps(OpRcPtrVec & ops,
+ const Config& /*config*/,
+ const AllocationTransform& allocationTransform,
+ TransformDirection dir)
+ {
+ TransformDirection combinedDir = CombineTransformDirections(dir,
+ allocationTransform.getDirection());
+
+ AllocationData data;
+ data.allocation = allocationTransform.getAllocation();
+ data.vars.resize(allocationTransform.getNumVars());
+ if(!data.vars.empty())
+ {
+ allocationTransform.getVars(&data.vars[0]);
+ }
+
+ CreateAllocationOps(ops, data, combinedDir);
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/Baker.cpp b/src/core/Baker.cpp
new file mode 100644
index 0000000..581df5f
--- /dev/null
+++ b/src/core/Baker.cpp
@@ -0,0 +1,297 @@
+/*
+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 <vector>
+#include <iostream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "MathUtils.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+
+ BakerRcPtr Baker::Create()
+ {
+ return BakerRcPtr(new Baker(), &deleter);
+ }
+
+ void Baker::deleter(Baker* c)
+ {
+ delete c;
+ }
+
+ class Baker::Impl
+ {
+ public:
+
+ ConfigRcPtr config_;
+ std::string formatName_;
+ std::string type_;
+ std::string metadata_;
+ std::string inputSpace_;
+ std::string shaperSpace_;
+ std::string looks_;
+ std::string targetSpace_;
+ int shapersize_;
+ int cubesize_;
+
+ Impl() :
+ shapersize_(-1),
+ cubesize_(-1)
+ {
+ }
+
+ ~Impl()
+ {
+ }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ config_ = rhs.config_;
+ formatName_ = rhs.formatName_;
+ inputSpace_ = rhs.inputSpace_;
+ shaperSpace_ = rhs.shaperSpace_;
+ looks_ = rhs.looks_;
+ targetSpace_ = rhs.targetSpace_;
+ shapersize_ = rhs.shapersize_;
+ cubesize_ = rhs.cubesize_;
+ return *this;
+ }
+ };
+
+ Baker::Baker()
+ : m_impl(new Baker::Impl)
+ {
+ }
+
+ Baker::~Baker()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ BakerRcPtr Baker::createEditableCopy() const
+ {
+ BakerRcPtr oven = Baker::Create();
+ *oven->m_impl = *m_impl;
+ return oven;
+ }
+
+ void Baker::setConfig(const ConstConfigRcPtr & config)
+ {
+ getImpl()->config_ = config->createEditableCopy();
+ }
+
+ ConstConfigRcPtr Baker::getConfig() const
+ {
+ return getImpl()->config_;
+ }
+
+ int Baker::getNumFormats()
+ {
+ return FormatRegistry::GetInstance().getNumFormats(FORMAT_CAPABILITY_WRITE);
+ }
+
+ const char * Baker::getFormatNameByIndex(int index)
+ {
+ return FormatRegistry::GetInstance().getFormatNameByIndex(FORMAT_CAPABILITY_WRITE, index);
+ }
+
+ const char * Baker::getFormatExtensionByIndex(int index)
+ {
+ return FormatRegistry::GetInstance().getFormatExtensionByIndex(FORMAT_CAPABILITY_WRITE, index);
+ }
+
+ void Baker::setFormat(const char * formatName)
+ {
+ getImpl()->formatName_ = formatName;
+ }
+
+ const char * Baker::getFormat() const
+ {
+ return getImpl()->formatName_.c_str();
+ }
+
+ void Baker::setType(const char * type)
+ {
+ getImpl()->type_ = type;
+ }
+
+ const char * Baker::getType() const
+ {
+ return getImpl()->type_.c_str();
+ }
+
+ void Baker::setMetadata(const char * metadata)
+ {
+ getImpl()->metadata_ = metadata;
+ }
+
+ const char * Baker::getMetadata() const
+ {
+ return getImpl()->metadata_.c_str();
+ }
+
+ void Baker::setInputSpace(const char * inputSpace)
+ {
+ getImpl()->inputSpace_ = inputSpace;
+ }
+
+ const char * Baker::getInputSpace() const
+ {
+ return getImpl()->inputSpace_.c_str();
+ }
+
+ void Baker::setShaperSpace(const char * shaperSpace)
+ {
+ getImpl()->shaperSpace_ = shaperSpace;
+ }
+
+ const char * Baker::getShaperSpace() const
+ {
+ return getImpl()->shaperSpace_.c_str();
+ }
+
+ void Baker::setLooks(const char * looks)
+ {
+ getImpl()->looks_ = looks;
+ }
+
+ const char * Baker::getLooks() const
+ {
+ return getImpl()->looks_.c_str();
+ }
+
+ void Baker::setTargetSpace(const char * targetSpace)
+ {
+ getImpl()->targetSpace_ = targetSpace;
+ }
+
+ const char * Baker::getTargetSpace() const
+ {
+ return getImpl()->targetSpace_.c_str();
+ }
+
+ void Baker::setShaperSize(int shapersize)
+ {
+ getImpl()->shapersize_ = shapersize;
+ }
+
+ int Baker::getShaperSize() const
+ {
+ return getImpl()->shapersize_;
+ }
+
+ void Baker::setCubeSize(int cubesize)
+ {
+ getImpl()->cubesize_ = cubesize;
+ }
+
+ int Baker::getCubeSize() const
+ {
+ return getImpl()->cubesize_;
+ }
+
+ void Baker::bake(std::ostream & os) const
+ {
+ FileFormat* fmt = FormatRegistry::GetInstance().getFileFormatByName(getImpl()->formatName_);
+
+ if(!fmt)
+ {
+ std::ostringstream err;
+ err << "The format named '" << getImpl()->formatName_;
+ err << "' could not be found. ";
+ throw Exception(err.str().c_str());
+ }
+
+ try
+ {
+ fmt->Write(*this, getImpl()->formatName_, os);
+ }
+ catch(std::exception & e)
+ {
+ std::ostringstream err;
+ err << "Error baking " << getImpl()->formatName_ << ":";
+ err << e.what();
+ throw Exception(err.str().c_str());
+ }
+
+ //
+ // TODO:
+ //
+ // - throw exception when we don't have inputSpace and targetSpace
+ // at least set
+ // - check limits of shaper and target, throw exception if we can't
+ // write that much data in x format
+ // - check that the shaper is 1D transform only, throw excpetion
+ // - check the file format supports shapers, 1D and 3D
+ // - add some checks to make sure we are monotonic
+ // - deal with the case of writing out non cube formats (1D only)
+ // - do a compare between ocio transform and output lut transform
+ // throw error if we going beyond tolerance
+ //
+ }
+
+}
+OCIO_NAMESPACE_EXIT
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+/*
+OIIO_ADD_TEST(Baker_Unit_Tests, test_listlutwriters)
+{
+
+ std::vector<std::string> current_writers;
+ current_writers.push_back("cinespace");
+ current_writers.push_back("houdini");
+
+ OCIO::BakerRcPtr baker = OCIO::Baker::Create();
+
+ OIIO_CHECK_EQUAL(baker->getNumFormats(), (int)current_writers.size());
+
+ std::vector<std::string> test;
+ for(int i = 0; i < baker->getNumFormats(); ++i)
+ test.push_back(baker->getFormatNameByIndex(i));
+
+ for(unsigned int i = 0; i < current_writers.size(); ++i)
+ OIIO_CHECK_EQUAL(current_writers[i], test[i]);
+
+}
+*/
+
+#endif // OCIO_BUILD_TESTS
+
+
diff --git a/src/core/CDLTransform.cpp b/src/core/CDLTransform.cpp
new file mode 100644
index 0000000..0e321ff
--- /dev/null
+++ b/src/core/CDLTransform.cpp
@@ -0,0 +1,776 @@
+/*
+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 <fstream>
+#include <tinyxml.h>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "CDLTransform.h"
+#include "ExponentOps.h"
+#include "MatrixOps.h"
+#include "MathUtils.h"
+#include "Mutex.h"
+#include "OpBuilders.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ /*
+ "<ColorCorrection id=''>"
+ " <SOPNode>"
+ " <Description/> "
+ " <Slope>1 1 1</Slope> "
+ " <Offset>0 0 0</Offset> "
+ " <Power>1 1 1</Power> "
+ " </SOPNode> "
+ " <SatNode>"
+ " <Saturation> 1 </Saturation> "
+ " </SatNode> "
+ " </ColorCorrection>";
+
+ */
+
+ // http://ticpp.googlecode.com/svn/docs/ticpp_8h-source.html#l01670
+
+ void SetTiXmlText( TiXmlElement* element, const char * value)
+ {
+ if ( element->NoChildren() )
+ {
+ element->LinkEndChild( new TiXmlText( value ) );
+ }
+ else
+ {
+ if ( 0 == element->GetText() )
+ {
+ element->InsertBeforeChild( element->FirstChild(), TiXmlText( value ) );
+ }
+ else
+ {
+ // There already is text, so change it
+ element->FirstChild()->SetValue( value );
+ }
+ }
+ }
+
+ std::string BuildXML(const CDLTransform & cdl)
+ {
+ TiXmlDocument doc;
+
+ // TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );
+ TiXmlElement * root = new TiXmlElement( "ColorCorrection" );
+ doc.LinkEndChild( root );
+ root->SetAttribute("id", cdl.getID());
+
+ TiXmlElement * sop = new TiXmlElement( "SOPNode" );
+ root->LinkEndChild( sop );
+
+ TiXmlElement * desc = new TiXmlElement( "Description" );
+ sop->LinkEndChild( desc );
+ SetTiXmlText(desc, cdl.getDescription());
+
+ TiXmlElement * slope = new TiXmlElement( "Slope" );
+ sop->LinkEndChild( slope );
+ float slopeval[3];
+ cdl.getSlope(slopeval);
+ SetTiXmlText(slope, FloatVecToString(slopeval, 3).c_str());
+
+ TiXmlElement * offset = new TiXmlElement( "Offset" );
+ sop->LinkEndChild( offset );
+ float offsetval[3];
+ cdl.getOffset(offsetval);
+ SetTiXmlText(offset, FloatVecToString(offsetval, 3).c_str());
+
+ TiXmlElement * power = new TiXmlElement( "Power" );
+ sop->LinkEndChild( power );
+ float powerval[3];
+ cdl.getPower(powerval);
+ SetTiXmlText(power, FloatVecToString(powerval, 3).c_str());
+
+ TiXmlElement * sat = new TiXmlElement( "SatNode" );
+ root->LinkEndChild( sat );
+
+ TiXmlElement * saturation = new TiXmlElement( "Saturation" );
+ sat->LinkEndChild( saturation );
+ SetTiXmlText(saturation, FloatToString(cdl.getSat()).c_str());
+
+ TiXmlPrinter printer;
+ printer.SetStreamPrinting();
+ doc.Accept( &printer );
+ return printer.Str();
+ }
+ }
+
+ void LoadCDL(CDLTransform * cdl, TiXmlElement * root)
+ {
+ if(!cdl) return;
+
+ if(!root)
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml. ";
+ os << "Null root element.";
+ throw Exception(os.str().c_str());
+ }
+
+ if(std::string(root->Value()) != "ColorCorrection")
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml. ";
+ os << "Root element is type '" << root->Value() << "', ";
+ os << "ColorCorrection expected.";
+ throw Exception(os.str().c_str());
+ }
+
+ TiXmlHandle handle( root );
+
+ const char * id = root->Attribute("id");
+ if(!id) id = "";
+
+ cdl->setID(id);
+
+ TiXmlElement* desc = handle.FirstChild( "SOPNode" ).FirstChild("Description").ToElement();
+ if(desc)
+ {
+ const char * text = desc->GetText();
+ if(text) cdl->setDescription(text);
+ }
+
+ std::vector<std::string> lineParts;
+ std::vector<float> floatArray;
+
+ TiXmlElement* slope = handle.FirstChild( "SOPNode" ).FirstChild("Slope").ToElement();
+ if(slope)
+ {
+ const char * text = slope->GetText();
+ if(text)
+ {
+ pystring::split(pystring::strip(text), lineParts);
+ if((lineParts.size() != 3) || (!StringVecToFloatVec(floatArray, lineParts)))
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml. ";
+ os << id << ".SOPNode.Slope text '";
+ os << text << "' is not convertible to 3 floats.";
+ throw Exception(os.str().c_str());
+ }
+ cdl->setSlope(&floatArray[0]);
+ }
+ }
+
+ TiXmlElement* offset = handle.FirstChild( "SOPNode" ).FirstChild("Offset").ToElement();
+ if(offset)
+ {
+ const char * text = offset->GetText();
+ if(text)
+ {
+ pystring::split(pystring::strip(text), lineParts);
+ if((lineParts.size() != 3) || (!StringVecToFloatVec(floatArray, lineParts)))
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml. ";
+ os << id << ".SOPNode.Offset text '";
+ os << text << "' is not convertible to 3 floats.";
+ throw Exception(os.str().c_str());
+ }
+ cdl->setOffset(&floatArray[0]);
+ }
+ }
+
+ TiXmlElement* power = handle.FirstChild( "SOPNode" ).FirstChild("Power").ToElement();
+ if(power)
+ {
+ const char * text = power->GetText();
+ if(text)
+ {
+ pystring::split(pystring::strip(text), lineParts);
+ if((lineParts.size() != 3) || (!StringVecToFloatVec(floatArray, lineParts)))
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml. ";
+ os << id << ".SOPNode.Power text '";
+ os << text << "' is not convertible to 3 floats.";
+ throw Exception(os.str().c_str());
+ }
+ cdl->setPower(&floatArray[0]);
+ }
+ }
+
+ TiXmlElement* sat = handle.FirstChild( "SatNode" ).FirstChild("Saturation").ToElement();
+ if(sat)
+ {
+ const char * text = sat->GetText();
+ if(text)
+ {
+ float satval = 1.0f;
+ if(!StringToFloat(&satval, text))
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml. ";
+ os << id << ".SatNode.Saturation text '";
+ os << text << "' is not convertible to float.";
+ throw Exception(os.str().c_str());
+ }
+ cdl->setSat(satval);
+ }
+ }
+ }
+
+
+
+ void GetCDLTransforms(CDLTransformMap & transforms,
+ TiXmlElement * cccRootElement)
+ {
+ if(std::string(cccRootElement->Value()) != "ColorCorrectionCollection")
+ {
+ std::ostringstream os;
+ os << "GetCDLTransforms Error. ";
+ os << "Root element is type '" << cccRootElement->Value() << "', ";
+ os << "ColorCorrectionCollection expected.";
+ throw Exception(os.str().c_str());
+ }
+
+ TiXmlNode * child = cccRootElement->FirstChild("ColorCorrection");
+ while(child)
+ {
+ CDLTransformRcPtr transform = CDLTransform::Create();
+ LoadCDL(transform.get(), child->ToElement());
+
+ std::string id = transform->getID();
+ if(id.empty())
+ {
+ std::ostringstream os;
+ os << "Error loading ccc xml, ";
+ os << "All ASC ColorCorrections must specify an 'id' value.";
+ throw Exception(os.str().c_str());
+ }
+
+ CDLTransformMap::iterator iter = transforms.find(id);
+ if(iter != transforms.end())
+ {
+ std::ostringstream os;
+ os << "Error loading ccc xml. ";
+ os << "All ASC ColorCorrections must specify a unique 'id' value. ";
+ os << "Duplicate elements with '" << id << "' found.";
+ throw Exception(os.str().c_str());
+ }
+
+ transforms[id] = transform;
+
+ child = child->NextSibling("ColorCorrection");
+ }
+ }
+
+ void LoadCDL(CDLTransform * cdl, const char * xml)
+ {
+ if(!xml || (strlen(xml) == 0))
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml. ";
+ os << "Null string provided.";
+ throw Exception(os.str().c_str());
+ }
+
+ TiXmlDocument doc;
+ doc.Parse(xml);
+
+ if(doc.Error())
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml. ";
+ os << doc.ErrorDesc() << " (line ";
+ os << doc.ErrorRow() << ", character ";
+ os << doc.ErrorCol() << ")";
+ throw Exception(os.str().c_str());
+ }
+
+ if(!doc.RootElement())
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml, ";
+ os << "please confirm the xml is valid.";
+ throw Exception(os.str().c_str());
+ }
+
+ LoadCDL(cdl, doc.RootElement()->ToElement());
+ }
+
+ CDLTransformRcPtr CDLTransform::Create()
+ {
+ return CDLTransformRcPtr(new CDLTransform(), &deleter);
+ }
+
+ namespace
+ {
+ std::string GetCDLLocalCacheKey(const std::string & src,
+ const std::string & cccid)
+ {
+ return src + " : " + cccid;
+ }
+
+ CDLTransformMap g_cache;
+ Mutex g_cacheMutex;
+ }
+
+ void ClearCDLTransformFileCache()
+ {
+ AutoMutex lock(g_cacheMutex);
+ g_cache.clear();
+ }
+
+ // TODO: Expose functions for introspecting in ccc file
+ // TODO: Share caching with normal cdl pathway
+
+ CDLTransformRcPtr CDLTransform::CreateFromFile(const char * src, const char * cccid)
+ {
+ if(!src || (strlen(src) == 0) || !cccid)
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml. ";
+ os << "Source file not specified.";
+ throw Exception(os.str().c_str());
+ }
+
+ // Check cache
+ AutoMutex lock(g_cacheMutex);
+ {
+ CDLTransformMap::iterator iter =
+ g_cache.find(GetCDLLocalCacheKey(src,cccid));
+ if(iter != g_cache.end())
+ {
+ return iter->second;
+ }
+ }
+
+ std::ifstream istream(src);
+ if(istream.fail()) {
+ std::ostringstream os;
+ os << "Error could not read CDL source file '" << src;
+ os << "'. Please verify the file exists and appropriate ";
+ os << "permissions are set.";
+ throw Exception (os.str().c_str());
+ }
+
+ // Read the file into a string.
+ std::ostringstream rawdata;
+ rawdata << istream.rdbuf();
+ std::string xml = rawdata.str();
+
+ if(xml.empty())
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml. ";
+ os << "The specified source file, '";
+ os << src << "' appears to be empty.";
+ throw Exception(os.str().c_str());
+ }
+
+ TiXmlDocument doc;
+ doc.Parse(xml.c_str());
+
+ if(doc.Error())
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml from file '";
+ os << src << "'. ";
+ os << doc.ErrorDesc() << " (line ";
+ os << doc.ErrorRow() << ", character ";
+ os << doc.ErrorCol() << ")";
+ throw Exception(os.str().c_str());
+ }
+
+ if(!doc.RootElement())
+ {
+ std::ostringstream os;
+ os << "Error loading CDL xml from file '";
+ os << src << "'. ";
+ os << "Please confirm the xml is valid.";
+ throw Exception(os.str().c_str());
+ }
+
+ std::string rootValue = doc.RootElement()->Value();
+ if(rootValue == "ColorCorrection")
+ {
+ // Load a single ColorCorrection into the cache
+ CDLTransformRcPtr cdl = CDLTransform::Create();
+ LoadCDL(cdl.get(), doc.RootElement()->ToElement());
+ g_cache[GetCDLLocalCacheKey(src,cccid)] = cdl;
+ return cdl;
+ }
+ else if(rootValue == "ColorCorrectionCollection")
+ {
+ // Load all CCs from the ColorCorrectionCollection
+ // into the cache
+
+ CDLTransformMap transforms;
+ GetCDLTransforms(transforms, doc.RootElement());
+
+ if(transforms.empty())
+ {
+ std::ostringstream os;
+ os << "Error loading ccc xml. ";
+ os << "No ColorCorrection elements found in file '";
+ os << src << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ for(CDLTransformMap::iterator iter = transforms.begin();
+ iter != transforms.end();
+ ++iter)
+ {
+ g_cache[GetCDLLocalCacheKey(src,iter->first)] = iter->second;
+ }
+
+ CDLTransformMap::iterator cciter = g_cache.find(GetCDLLocalCacheKey(src,cccid));
+ if(cciter == g_cache.end())
+ {
+ std::ostringstream os;
+ os << "Error loading ccc xml. ";
+ os << "The specified cccid " << cccid << " ";
+ os << "could not be found in file '";
+ os << src << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ return cciter->second;
+ }
+
+ std::ostringstream os;
+ os << "Error loading CDL xml from file '";
+ os << src << "'. ";
+ os << "Root xml element is type '" << rootValue << "', ";
+ os << "ColorCorrection or ColorCorrectionCollection expected.";
+ throw Exception(os.str().c_str());
+ }
+
+ void CDLTransform::deleter(CDLTransform* t)
+ {
+ delete t;
+ }
+
+ class CDLTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+
+ float sop_[9];
+ float sat_;
+ std::string id_;
+ std::string description_;
+
+ mutable std::string xml_;
+
+ Impl() :
+ dir_(TRANSFORM_DIR_FORWARD),
+ sat_(1.0f)
+ {
+ sop_[0] = 1.0f;
+ sop_[1] = 1.0f;
+ sop_[2] = 1.0f;
+ sop_[3] = 0.0f;
+ sop_[4] = 0.0f;
+ sop_[5] = 0.0f;
+ sop_[6] = 1.0f;
+ sop_[7] = 1.0f;
+ sop_[8] = 1.0f;
+ }
+
+ ~Impl()
+ {
+
+ }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+
+ memcpy(sop_, rhs.sop_, sizeof(float)*9);
+ sat_ = rhs.sat_;
+ id_ = rhs.id_;
+ description_ = rhs.description_;
+
+ return *this;
+ }
+
+ };
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ CDLTransform::CDLTransform()
+ : m_impl(new CDLTransform::Impl)
+ {
+ }
+
+ TransformRcPtr CDLTransform::createEditableCopy() const
+ {
+ CDLTransformRcPtr transform = CDLTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ CDLTransform::~CDLTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ CDLTransform& CDLTransform::operator= (const CDLTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection CDLTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void CDLTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+ const char * CDLTransform::getXML() const
+ {
+ getImpl()->xml_ = BuildXML(*this);
+ return getImpl()->xml_.c_str();
+ }
+
+ void CDLTransform::setXML(const char * xml)
+ {
+ LoadCDL(this, xml);
+ }
+
+ // We use this approach, rather than comparing XML to get around the
+ // case where setXML with extra data was provided.
+
+ bool CDLTransform::equals(const ConstCDLTransformRcPtr & other) const
+ {
+ if(!other) return false;
+
+ if(getImpl()->dir_ != other->getImpl()->dir_) return false;
+
+ const float abserror = 1e-9f;
+
+ for(int i=0; i<9; ++i)
+ {
+ if(!equalWithAbsError(getImpl()->sop_[i], other->getImpl()->sop_[i], abserror))
+ {
+ return false;
+ }
+ }
+
+ if(!equalWithAbsError(getImpl()->sat_, other->getImpl()->sat_, abserror))
+ {
+ return false;
+ }
+
+ if(getImpl()->id_ != other->getImpl()->id_)
+ {
+ return false;
+ }
+
+ if(getImpl()->description_ != other->getImpl()->description_)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ void CDLTransform::setSlope(const float * rgb)
+ {
+ memcpy(&getImpl()->sop_[0], rgb, sizeof(float)*3);
+ }
+
+ void CDLTransform::getSlope(float * rgb) const
+ {
+ memcpy(rgb, &getImpl()->sop_[0], sizeof(float)*3);
+ }
+
+ void CDLTransform::setOffset(const float * rgb)
+ {
+ memcpy(&getImpl()->sop_[3], rgb, sizeof(float)*3);
+ }
+
+ void CDLTransform::getOffset(float * rgb) const
+ {
+ memcpy(rgb, &getImpl()->sop_[3], sizeof(float)*3);
+ }
+
+ void CDLTransform::setPower(const float * rgb)
+ {
+ memcpy(&getImpl()->sop_[6], rgb, sizeof(float)*3);
+ }
+
+ void CDLTransform::getPower(float * rgb) const
+ {
+ memcpy(rgb, &getImpl()->sop_[6], sizeof(float)*3);
+ }
+
+ void CDLTransform::setSOP(const float * vec9)
+ {
+ memcpy(&getImpl()->sop_, vec9, sizeof(float)*9);
+ }
+
+ void CDLTransform::getSOP(float * vec9) const
+ {
+ memcpy(vec9, &getImpl()->sop_, sizeof(float)*9);
+ }
+
+ void CDLTransform::setSat(float sat)
+ {
+ getImpl()->sat_ = sat;
+ }
+
+ float CDLTransform::getSat() const
+ {
+ return getImpl()->sat_;
+ }
+
+ void CDLTransform::getSatLumaCoefs(float * rgb) const
+ {
+ if(!rgb) return;
+ rgb[0] = 0.2126f;
+ rgb[1] = 0.7152f;
+ rgb[2] = 0.0722f;
+ }
+
+ void CDLTransform::setID(const char * id)
+ {
+ if(id)
+ {
+ getImpl()->id_ = id;
+ }
+ else
+ {
+ getImpl()->id_ = "";
+ }
+ }
+
+ const char * CDLTransform::getID() const
+ {
+ return getImpl()->id_.c_str();
+ }
+
+ void CDLTransform::setDescription(const char * desc)
+ {
+ if(desc)
+ {
+ getImpl()->description_ = desc;
+ }
+ else
+ {
+ getImpl()->description_ = "";
+ }
+ }
+
+ const char * CDLTransform::getDescription() const
+ {
+ return getImpl()->description_.c_str();
+ }
+
+ std::ostream& operator<< (std::ostream& os, const CDLTransform& t)
+ {
+ float sop[9];
+ t.getSOP(sop);
+
+ os << "<CDLTransform ";
+ os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
+ os << "sop=";
+ for (unsigned int i=0; i<9; ++i)
+ {
+ if(i!=0) os << " ";
+ os << sop[i];
+ }
+ os << ", ";
+ os << "sat=" << t.getSat() << ",";
+ os << TransformDirectionToString(t.getDirection()) << ", ";
+ os << ">\n";
+ return os;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ void BuildCDLOps(OpRcPtrVec & ops,
+ const Config & /*config*/,
+ const CDLTransform & cdlTransform,
+ TransformDirection dir)
+ {
+ float scale4[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ cdlTransform.getSlope(scale4);
+
+ float offset4[] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ cdlTransform.getOffset(offset4);
+
+ float power4[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ cdlTransform.getPower(power4);
+
+ float lumaCoef3[] = { 1.0f, 1.0f, 1.0f };
+ cdlTransform.getSatLumaCoefs(lumaCoef3);
+
+ float sat = cdlTransform.getSat();
+
+ TransformDirection combinedDir = CombineTransformDirections(dir,
+ cdlTransform.getDirection());
+
+ // TODO: Confirm ASC Sat math is correct.
+ // TODO: Handle Clamping conditions more explicitly
+
+ if(combinedDir == TRANSFORM_DIR_FORWARD)
+ {
+ // 1) Scale + Offset
+ CreateScaleOffsetOp(ops, scale4, offset4, TRANSFORM_DIR_FORWARD);
+
+ // 2) Power + Clamp
+ CreateExponentOp(ops, power4, TRANSFORM_DIR_FORWARD);
+
+ // 3) Saturation + Clamp
+ CreateSaturationOp(ops, sat, lumaCoef3, TRANSFORM_DIR_FORWARD);
+ }
+ else if(combinedDir == TRANSFORM_DIR_INVERSE)
+ {
+ // 3) Saturation + Clamp
+ CreateSaturationOp(ops, sat, lumaCoef3, TRANSFORM_DIR_INVERSE);
+
+ // 2) Power + Clamp
+ CreateExponentOp(ops, power4, TRANSFORM_DIR_INVERSE);
+
+ // 1) Scale + Offset
+ CreateScaleOffsetOp(ops, scale4, offset4, TRANSFORM_DIR_INVERSE);
+ }
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/CDLTransform.h b/src/core/CDLTransform.h
new file mode 100644
index 0000000..8b945ad
--- /dev/null
+++ b/src/core/CDLTransform.h
@@ -0,0 +1,53 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_CDLTRANSFORM_H
+#define INCLUDED_OCIO_CDLTRANSFORM_H
+
+#include <map>
+#include <tinyxml.h>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+OCIO_NAMESPACE_ENTER
+{
+ typedef std::map<std::string,CDLTransformRcPtr> CDLTransformMap;
+
+ void ClearCDLTransformFileCache();
+
+ void LoadCDL(CDLTransform * cdl, const char * xml);
+ void LoadCDL(CDLTransform * cdl, TiXmlElement * root);
+
+ // Get a map of transform cccid : cdl transform
+ void GetCDLTransforms(CDLTransformMap & transforms,
+ TiXmlElement * cccroot);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
new file mode 100644
index 0000000..50194f3
--- /dev/null
+++ b/src/core/CMakeLists.txt
@@ -0,0 +1,93 @@
+###############################################################################
+### OCIO CORE ###
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}/export/
+ ${CMAKE_BINARY_DIR}/export/
+ ${EXTERNAL_INCLUDE_DIRS}
+ ${CMAKE_SOURCE_DIR}/ext/oiio/src/include
+)
+
+file(GLOB_RECURSE core_src_files "${CMAKE_SOURCE_DIR}/src/core/*.cpp")
+file(GLOB_RECURSE core_export_headers "${CMAKE_SOURCE_DIR}/export/OpenColorIO/*.h")
+
+message(STATUS "Create OpenColorABI.h from OpenColorABI.h.in")
+configure_file(${CMAKE_SOURCE_DIR}/export/OpenColorIO/OpenColorABI.h.in
+ ${CMAKE_BINARY_DIR}/export/OpenColorABI.h @ONLY)
+list(APPEND core_export_headers ${CMAKE_BINARY_DIR}/export/OpenColorABI.h)
+
+# SHARED
+
+if(OCIO_BUILD_SHARED)
+ add_library(OpenColorIO SHARED ${core_src_files})
+
+ if(USE_EXTERNAL_TINYXML)
+ target_link_libraries(OpenColorIO ${TINYXML_LIBRARIES})
+ else(USE_EXTERNAL_TINYXML)
+ add_dependencies(OpenColorIO tinyxml)
+ endif(USE_EXTERNAL_TINYXML)
+
+ if(USE_EXTERNAL_YAML)
+ target_link_libraries(OpenColorIO ${YAML_CPP_LIBRARIES})
+ else(USE_EXTERNAL_YAML)
+ add_dependencies(OpenColorIO YAML_CPP_LOCAL)
+ endif()
+
+ if(WIN32)
+ target_link_libraries(OpenColorIO
+ debug ${EXTERNAL_DEBUG_LIBRARIES}
+ optimized ${EXTERNAL_OPTIMIZED_LIBRARIES}
+ general ${EXTERNAL_GENERAL_LIBRARIES})
+ else()
+ target_link_libraries(OpenColorIO ${EXTERNAL_GENERAL_LIBRARIES})
+ endif()
+ set_target_properties(OpenColorIO PROPERTIES
+ OUTPUT_NAME OpenColorIO
+ COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}"
+ LINK_FLAGS "${EXTERNAL_LINK_FLAGS}")
+
+ message(STATUS "Setting OCIO SOVERSION to: ${SOVERSION}")
+ set_target_properties(OpenColorIO PROPERTIES
+ VERSION ${OCIO_VERSION}
+ SOVERSION ${SOVERSION})
+
+ install(TARGETS OpenColorIO DESTINATION ${CMAKE_INSTALL_EXEC_PREFIX}/lib${LIB_SUFFIX})
+endif()
+
+# STATIC
+
+if(OCIO_BUILD_STATIC)
+ list(REMOVE_ITEM core_src_files ${CMAKE_SOURCE_DIR}/src/core/UnitTest.cpp)
+ add_library(OpenColorIO_STATIC STATIC ${core_src_files})
+ add_dependencies(OpenColorIO_STATIC tinyxml YAML_CPP_LOCAL)
+ if(WIN32)
+ target_link_libraries(OpenColorIO_STATIC
+ debug ${EXTERNAL_DEBUG_LIBRARIES}
+ optimized ${EXTERNAL_OPTIMIZED_LIBRARIES}
+ general ${EXTERNAL_GENERAL_LIBRARIES})
+ else()
+ target_link_libraries(OpenColorIO_STATIC ${EXTERNAL_GENERAL_LIBRARIES})
+ endif()
+ set_target_properties(OpenColorIO_STATIC PROPERTIES
+ OUTPUT_NAME OpenColorIO
+ COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}"
+ LINK_FLAGS "${EXTERNAL_LINK_FLAGS}")
+
+ message(STATUS "Setting OCIO SOVERSION to: ${SOVERSION}")
+ set_target_properties(OpenColorIO_STATIC PROPERTIES
+ VERSION ${OCIO_VERSION}
+ SOVERSION ${SOVERSION})
+
+ install(TARGETS OpenColorIO_STATIC DESTINATION ${CMAKE_INSTALL_EXEC_PREFIX}/lib)
+endif()
+
+# public interface
+install(FILES ${core_export_headers}
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/include/OpenColorIO/)
+
+# pkg-config
+message(STATUS "Create OpenColorIO.pc from OpenColorIO.pc.in")
+configure_file(${CMAKE_SOURCE_DIR}/export/pkgconfig/OpenColorIO.pc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/OpenColorIO.pc @ONLY)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpenColorIO.pc
+ DESTINATION ${CMAKE_INSTALL_EXEC_PREFIX}/lib${LIB_SUFFIX}/pkgconfig/)
diff --git a/src/core/Caching.cpp b/src/core/Caching.cpp
new file mode 100644
index 0000000..afb0e7c
--- /dev/null
+++ b/src/core/Caching.cpp
@@ -0,0 +1,47 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "CDLTransform.h"
+#include "PathUtils.h"
+#include "FileTransform.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ // TODO: Processors which the user hangs onto have local caches.
+ // Should these be cleared?
+
+ void ClearAllCaches()
+ {
+ ClearPathCaches();
+ ClearFileTransformCaches();
+ ClearCDLTransformFileCache();
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/ColorSpace.cpp b/src/core/ColorSpace.cpp
new file mode 100644
index 0000000..ed2a473
--- /dev/null
+++ b/src/core/ColorSpace.cpp
@@ -0,0 +1,270 @@
+/*
+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 <cstring>
+#include <sstream>
+#include <vector>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+OCIO_NAMESPACE_ENTER
+{
+ ColorSpaceRcPtr ColorSpace::Create()
+ {
+ return ColorSpaceRcPtr(new ColorSpace(), &deleter);
+ }
+
+ void ColorSpace::deleter(ColorSpace* c)
+ {
+ delete c;
+ }
+
+
+ class ColorSpace::Impl
+ {
+ public:
+ std::string name_;
+ std::string family_;
+ std::string equalityGroup_;
+ std::string description_;
+
+ BitDepth bitDepth_;
+ bool isData_;
+
+ Allocation allocation_;
+ std::vector<float> allocationVars_;
+
+ TransformRcPtr toRefTransform_;
+ TransformRcPtr fromRefTransform_;
+
+ bool toRefSpecified_;
+ bool fromRefSpecified_;
+
+ Impl() :
+ bitDepth_(BIT_DEPTH_UNKNOWN),
+ isData_(false),
+ allocation_(ALLOCATION_UNIFORM),
+ toRefSpecified_(false),
+ fromRefSpecified_(false)
+ { }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ name_ = rhs.name_;
+ family_ = rhs.family_;
+ equalityGroup_ = rhs.equalityGroup_;
+ description_ = rhs.description_;
+ bitDepth_ = rhs.bitDepth_;
+ isData_ = rhs.isData_;
+ allocation_ = rhs.allocation_;
+ allocationVars_ = rhs.allocationVars_;
+
+ toRefTransform_ = rhs.toRefTransform_;
+ if(toRefTransform_) toRefTransform_ = toRefTransform_->createEditableCopy();
+
+ fromRefTransform_ = rhs.fromRefTransform_;
+ if(fromRefTransform_) fromRefTransform_ = fromRefTransform_->createEditableCopy();
+
+ toRefSpecified_ = rhs.toRefSpecified_;
+ fromRefSpecified_ = rhs.fromRefSpecified_;
+ return *this;
+ }
+ };
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ ColorSpace::ColorSpace()
+ : m_impl(new ColorSpace::Impl)
+ {
+ }
+
+ ColorSpace::~ColorSpace()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ ColorSpaceRcPtr ColorSpace::createEditableCopy() const
+ {
+ ColorSpaceRcPtr cs = ColorSpace::Create();
+ *cs->m_impl = *m_impl;
+ return cs;
+ }
+
+ const char * ColorSpace::getName() const
+ {
+ return getImpl()->name_.c_str();
+ }
+
+ void ColorSpace::setName(const char * name)
+ {
+ getImpl()->name_ = name;
+ }
+ const char * ColorSpace::getFamily() const
+ {
+ return getImpl()->family_.c_str();
+ }
+
+ void ColorSpace::setFamily(const char * family)
+ {
+ getImpl()->family_ = family;
+ }
+
+ const char * ColorSpace::getEqualityGroup() const
+ {
+ return getImpl()->equalityGroup_.c_str();
+ }
+
+ void ColorSpace::setEqualityGroup(const char * equalityGroup)
+ {
+ getImpl()->equalityGroup_ = equalityGroup;
+ }
+
+ const char * ColorSpace::getDescription() const
+ {
+ return getImpl()->description_.c_str();
+ }
+
+ void ColorSpace::setDescription(const char * description)
+ {
+ getImpl()->description_ = description;
+ }
+
+ BitDepth ColorSpace::getBitDepth() const
+ {
+ return getImpl()->bitDepth_;
+ }
+
+ void ColorSpace::setBitDepth(BitDepth bitDepth)
+ {
+ getImpl()->bitDepth_ = bitDepth;
+ }
+
+ bool ColorSpace::isData() const
+ {
+ return getImpl()->isData_;
+ }
+
+ void ColorSpace::setIsData(bool val)
+ {
+ getImpl()->isData_ = val;
+ }
+
+ Allocation ColorSpace::getAllocation() const
+ {
+ return getImpl()->allocation_;
+ }
+
+ void ColorSpace::setAllocation(Allocation allocation)
+ {
+ getImpl()->allocation_ = allocation;
+ }
+
+ int ColorSpace::getAllocationNumVars() const
+ {
+ return static_cast<int>(getImpl()->allocationVars_.size());
+ }
+
+ void ColorSpace::getAllocationVars(float * vars) const
+ {
+ if(!getImpl()->allocationVars_.empty())
+ {
+ memcpy(vars,
+ &getImpl()->allocationVars_[0],
+ getImpl()->allocationVars_.size()*sizeof(float));
+ }
+ }
+
+ void ColorSpace::setAllocationVars(int numvars, const float * vars)
+ {
+ getImpl()->allocationVars_.resize(numvars);
+
+ if(!getImpl()->allocationVars_.empty())
+ {
+ memcpy(&getImpl()->allocationVars_[0],
+ vars,
+ numvars*sizeof(float));
+ }
+ }
+
+ ConstTransformRcPtr ColorSpace::getTransform(ColorSpaceDirection dir) const
+ {
+ if(dir == COLORSPACE_DIR_TO_REFERENCE)
+ return getImpl()->toRefTransform_;
+ else if(dir == COLORSPACE_DIR_FROM_REFERENCE)
+ return getImpl()->fromRefTransform_;
+
+ throw Exception("Unspecified ColorSpaceDirection");
+ }
+
+ void ColorSpace::setTransform(const ConstTransformRcPtr & transform,
+ ColorSpaceDirection dir)
+ {
+ TransformRcPtr transformCopy;
+ if(transform) transformCopy = transform->createEditableCopy();
+
+ if(dir == COLORSPACE_DIR_TO_REFERENCE)
+ getImpl()->toRefTransform_ = transformCopy;
+ else if(dir == COLORSPACE_DIR_FROM_REFERENCE)
+ getImpl()->fromRefTransform_ = transformCopy;
+ else
+ throw Exception("Unspecified ColorSpaceDirection");
+ }
+
+ std::ostream& operator<< (std::ostream& os, const ColorSpace& cs)
+ {
+ os << "<ColorSpace ";
+ os << "name=" << cs.getName() << ", ";
+ os << "family=" << cs.getFamily() << ", ";
+ os << "equalityGroup=" << cs.getEqualityGroup() << ", ";
+ os << "bitDepth=" << BitDepthToString(cs.getBitDepth()) << ", ";
+ os << "isData=" << BoolToString(cs.isData()) << ", ";
+ os << "allocation=" << AllocationToString(cs.getAllocation()) << ", ";
+ os << ">\n";
+
+ if(cs.getTransform(COLORSPACE_DIR_TO_REFERENCE))
+ {
+ os << "\t" << cs.getName() << " --> Reference\n";
+ os << cs.getTransform(COLORSPACE_DIR_TO_REFERENCE);
+ }
+
+ if(cs.getTransform(COLORSPACE_DIR_FROM_REFERENCE))
+ {
+ os << "\tReference --> " << cs.getName() << "\n";
+ os << cs.getTransform(COLORSPACE_DIR_FROM_REFERENCE);
+ }
+ return os;
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/ColorSpaceTransform.cpp b/src/core/ColorSpaceTransform.cpp
new file mode 100644
index 0000000..92f3878
--- /dev/null
+++ b/src/core/ColorSpaceTransform.cpp
@@ -0,0 +1,246 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "NoOps.h"
+#include "OpBuilders.h"
+
+
+OCIO_NAMESPACE_ENTER
+{
+ ColorSpaceTransformRcPtr ColorSpaceTransform::Create()
+ {
+ return ColorSpaceTransformRcPtr(new ColorSpaceTransform(), &deleter);
+ }
+
+ void ColorSpaceTransform::deleter(ColorSpaceTransform* t)
+ {
+ delete t;
+ }
+
+
+ class ColorSpaceTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+ std::string src_;
+ std::string dst_;
+
+ Impl() :
+ dir_(TRANSFORM_DIR_FORWARD)
+ { }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+ src_ = rhs.src_;
+ dst_ = rhs.dst_;
+ return *this;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ ColorSpaceTransform::ColorSpaceTransform()
+ : m_impl(new ColorSpaceTransform::Impl)
+ {
+ }
+
+ TransformRcPtr ColorSpaceTransform::createEditableCopy() const
+ {
+ ColorSpaceTransformRcPtr transform = ColorSpaceTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ ColorSpaceTransform::~ColorSpaceTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ ColorSpaceTransform& ColorSpaceTransform::operator= (const ColorSpaceTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection ColorSpaceTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void ColorSpaceTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+ const char * ColorSpaceTransform::getSrc() const
+ {
+ return getImpl()->src_.c_str();
+ }
+
+ void ColorSpaceTransform::setSrc(const char * src)
+ {
+ getImpl()->src_ = src;
+ }
+
+ const char * ColorSpaceTransform::getDst() const
+ {
+ return getImpl()->dst_.c_str();
+ }
+
+ void ColorSpaceTransform::setDst(const char * dst)
+ {
+ getImpl()->dst_ = dst;
+ }
+
+ std::ostream& operator<< (std::ostream& os, const ColorSpaceTransform& t)
+ {
+ os << "<ColorSpaceTransform ";
+ os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
+ os << ">\n";
+ return os;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ void BuildColorSpaceOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const ColorSpaceTransform & colorSpaceTransform,
+ TransformDirection dir)
+ {
+ TransformDirection combinedDir = CombineTransformDirections(dir,
+ colorSpaceTransform.getDirection());
+
+ ConstColorSpaceRcPtr src, dst;
+
+ if(combinedDir == TRANSFORM_DIR_FORWARD)
+ {
+ src = config.getColorSpace( colorSpaceTransform.getSrc() );
+ dst = config.getColorSpace( colorSpaceTransform.getDst() );
+ }
+ else if(combinedDir == TRANSFORM_DIR_INVERSE)
+ {
+ dst = config.getColorSpace( colorSpaceTransform.getSrc() );
+ src = config.getColorSpace( colorSpaceTransform.getDst() );
+ }
+
+ BuildColorSpaceOps(ops, config, context, src, dst);
+ }
+
+ namespace
+ {
+ bool AreColorSpacesInSameEqualityGroup(const ConstColorSpaceRcPtr & csa,
+ const ConstColorSpaceRcPtr & csb)
+ {
+ std::string a = csa->getEqualityGroup();
+ std::string b = csb->getEqualityGroup();
+
+ if(!a.empty()) return (a==b);
+ return false;
+ }
+ }
+
+ void BuildColorSpaceOps(OpRcPtrVec & ops,
+ const Config & config,
+ const ConstContextRcPtr & context,
+ const ConstColorSpaceRcPtr & srcColorSpace,
+ const ConstColorSpaceRcPtr & dstColorSpace)
+ {
+ if(!srcColorSpace)
+ throw Exception("BuildColorSpaceOps failed, null srcColorSpace.");
+ if(!dstColorSpace)
+ throw Exception("BuildColorSpaceOps failed, null dstColorSpace.");
+
+ if(AreColorSpacesInSameEqualityGroup(srcColorSpace, dstColorSpace))
+ return;
+ if(dstColorSpace->isData() || srcColorSpace->isData())
+ return;
+
+ // Consider dt8 -> vd8?
+ // One would have to explode the srcColorSpace->getTransform(COLORSPACE_DIR_TO_REFERENCE);
+ // result, and walk through it step by step. If the dstColorspace family were
+ // ever encountered in transit, we'd want to short circuit the result.
+
+ AllocationData srcAllocation;
+ srcAllocation.allocation = srcColorSpace->getAllocation();
+ srcAllocation.vars.resize( srcColorSpace->getAllocationNumVars());
+ if(srcAllocation.vars.size() > 0)
+ {
+ srcColorSpace->getAllocationVars(&srcAllocation.vars[0]);
+ }
+
+ CreateGpuAllocationNoOp(ops, srcAllocation);
+
+ // Go to the reference space, either by using
+ // * cs->ref in the forward direction
+ // * ref->cs in the inverse direction
+ if(srcColorSpace->getTransform(COLORSPACE_DIR_TO_REFERENCE))
+ {
+ BuildOps(ops, config, context, srcColorSpace->getTransform(COLORSPACE_DIR_TO_REFERENCE), TRANSFORM_DIR_FORWARD);
+ }
+ else if(srcColorSpace->getTransform(COLORSPACE_DIR_FROM_REFERENCE))
+ {
+ BuildOps(ops, config, context, srcColorSpace->getTransform(COLORSPACE_DIR_FROM_REFERENCE), TRANSFORM_DIR_INVERSE);
+ }
+ // Otherwise, both are not defined so its a no-op. This is not an error condition.
+
+ // Go from the reference space, either by using
+ // * ref->cs in the forward direction
+ // * cs->ref in the inverse direction
+ if(dstColorSpace->getTransform(COLORSPACE_DIR_FROM_REFERENCE))
+ {
+ BuildOps(ops, config, context, dstColorSpace->getTransform(COLORSPACE_DIR_FROM_REFERENCE), TRANSFORM_DIR_FORWARD);
+ }
+ else if(dstColorSpace->getTransform(COLORSPACE_DIR_TO_REFERENCE))
+ {
+ BuildOps(ops, config, context, dstColorSpace->getTransform(COLORSPACE_DIR_TO_REFERENCE), TRANSFORM_DIR_INVERSE);
+ }
+ // Otherwise, both are not defined so its a no-op. This is not an error condition.
+
+ AllocationData dstAllocation;
+ dstAllocation.allocation = dstColorSpace->getAllocation();
+ dstAllocation.vars.resize( dstColorSpace->getAllocationNumVars());
+ if(dstAllocation.vars.size() > 0)
+ {
+ dstColorSpace->getAllocationVars(&dstAllocation.vars[0]);
+ }
+
+ CreateGpuAllocationNoOp(ops, dstAllocation);
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/Config.cpp b/src/core/Config.cpp
new file mode 100644
index 0000000..985990f
--- /dev/null
+++ b/src/core/Config.cpp
@@ -0,0 +1,2223 @@
+/*
+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 <cstdlib>
+#include <cstring>
+#include <set>
+#include <sstream>
+#include <fstream>
+#include <utility>
+#include <vector>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "HashUtils.h"
+#include "Logging.h"
+#include "LookParse.h"
+#include "MathUtils.h"
+#include "Mutex.h"
+#include "OpBuilders.h"
+#include "PathUtils.h"
+#include "ParseUtils.h"
+#include "Processor.h"
+#include "PrivateTypes.h"
+#include "pystring/pystring.h"
+#include "OCIOYaml.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ const char * OCIO_CONFIG_ENVVAR = "OCIO";
+ const char * OCIO_ACTIVE_DISPLAYS_ENVVAR = "OCIO_ACTIVE_DISPLAYS";
+ const char * OCIO_ACTIVE_VIEWS_ENVVAR = "OCIO_ACTIVE_VIEWS";
+
+ enum Sanity
+ {
+ SANITY_UNKNOWN = 0,
+ SANITY_SANE,
+ SANITY_INSANE
+ };
+
+ // These are the 709 primaries specified by the ASC.
+ const float DEFAULT_LUMA_COEFF_R = 0.2126f;
+ const float DEFAULT_LUMA_COEFF_G = 0.7152f;
+ const float DEFAULT_LUMA_COEFF_B = 0.0722f;
+
+ const char * INTERNAL_RAW_PROFILE =
+ "ocio_profile_version: 1\n"
+ "strictparsing: false\n"
+ "roles:\n"
+ " default: raw\n"
+ "displays:\n"
+ " sRGB:\n"
+ " - !<View> {name: Raw, colorspace: raw}\n"
+ "colorspaces:\n"
+ " - !<ColorSpace>\n"
+ " name: raw\n"
+ " family: raw\n"
+ " equalitygroup:\n"
+ " bitdepth: 32f\n"
+ " isdata: true\n"
+ " allocation: uniform\n"
+ " description: 'A raw color space. Conversions to and from this space are no-ops.'\n";
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ const char * GetVersion()
+ {
+ return OCIO_VERSION;
+ }
+
+ int GetVersionHex()
+ {
+ return OCIO_VERSION_HEX;
+ }
+
+ namespace
+ {
+ ConstConfigRcPtr g_currentConfig;
+ Mutex g_currentConfigLock;
+ }
+
+ ConstConfigRcPtr GetCurrentConfig()
+ {
+ AutoMutex lock(g_currentConfigLock);
+
+ if(!g_currentConfig)
+ {
+ g_currentConfig = Config::CreateFromEnv();
+ }
+
+ return g_currentConfig;
+ }
+
+ void SetCurrentConfig(const ConstConfigRcPtr & config)
+ {
+ AutoMutex lock(g_currentConfigLock);
+
+ g_currentConfig = config->createEditableCopy();
+ }
+
+ namespace
+ {
+
+ // Roles
+ // (lower case role name: colorspace name)
+ std::string LookupRole(const StringMap & roles, const std::string & rolename)
+ {
+ StringMap::const_iterator iter = roles.find(pystring::lower(rolename));
+ if(iter == roles.end()) return "";
+ return iter->second;
+ }
+
+
+ void GetFileReferences(std::set<std::string> & files,
+ const ConstTransformRcPtr & transform)
+ {
+ if(!transform) return;
+
+ if(ConstGroupTransformRcPtr groupTransform = \
+ DynamicPtrCast<const GroupTransform>(transform))
+ {
+ for(int i=0; i<groupTransform->size(); ++i)
+ {
+ GetFileReferences(files, groupTransform->getTransform(i));
+ }
+ }
+ else if(ConstFileTransformRcPtr fileTransform = \
+ DynamicPtrCast<const FileTransform>(transform))
+ {
+ files.insert(fileTransform->getSrc());
+ }
+ }
+
+ void GetColorSpaceReferences(std::set<std::string> & colorSpaceNames,
+ const ConstTransformRcPtr & transform)
+ {
+ if(!transform) return;
+
+ if(ConstGroupTransformRcPtr groupTransform = \
+ DynamicPtrCast<const GroupTransform>(transform))
+ {
+ for(int i=0; i<groupTransform->size(); ++i)
+ {
+ GetColorSpaceReferences(colorSpaceNames, groupTransform->getTransform(i));
+ }
+ }
+ else if(ConstColorSpaceTransformRcPtr colorSpaceTransform = \
+ DynamicPtrCast<const ColorSpaceTransform>(transform))
+ {
+ colorSpaceNames.insert(colorSpaceTransform->getSrc());
+ colorSpaceNames.insert(colorSpaceTransform->getDst());
+ }
+ else if(ConstDisplayTransformRcPtr displayTransform = \
+ DynamicPtrCast<const DisplayTransform>(transform))
+ {
+ colorSpaceNames.insert(displayTransform->getInputColorSpaceName());
+ }
+ else if(ConstLookTransformRcPtr lookTransform = \
+ DynamicPtrCast<const LookTransform>(transform))
+ {
+ colorSpaceNames.insert(colorSpaceTransform->getSrc());
+ colorSpaceNames.insert(colorSpaceTransform->getDst());
+ }
+ }
+
+
+ bool FindColorSpaceIndex(int * index,
+ const ColorSpaceVec & colorspaces,
+ const std::string & csname)
+ {
+ if(csname.empty()) return false;
+
+ std::string csnamelower = pystring::lower(csname);
+
+ for(unsigned int i = 0; i < colorspaces.size(); ++i)
+ {
+ if(csnamelower == pystring::lower(colorspaces[i]->getName()))
+ {
+ if(index) *index = i;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ // Displays
+ struct View
+ {
+ std::string name;
+ std::string colorspace;
+ std::string looks;
+
+ View() { }
+
+ View(const std::string & name_,
+ const std::string & colorspace_,
+ const std::string & looksList_) :
+ name(name_),
+ colorspace(colorspace_),
+ looks(looksList_)
+ { }
+ };
+
+ typedef std::vector<View> ViewVec;
+ typedef std::map<std::string, ViewVec> DisplayMap; // (display name : ViewVec)
+
+ void operator >> (const YAML::Node& node, View& v)
+ {
+ if(node.Tag() != "View")
+ return;
+
+ std::string key, stringval;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "name")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ v.name = stringval;
+ }
+ else if(key == "colorspace")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ v.colorspace = stringval;
+ }
+ else if(key == "looks" || key == "look")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ v.looks = stringval;
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+
+ if(v.name.empty())
+ {
+ throw Exception("View does not specify 'name'.");
+ }
+ if(v.colorspace.empty())
+ {
+ std::ostringstream os;
+ os << "View '" << v.name << "' ";
+ os << "does not specify colorspace.";
+ throw Exception(os.str().c_str());
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, View view)
+ {
+ out << YAML::VerbatimTag("View");
+ out << YAML::Flow;
+ out << YAML::BeginMap;
+ out << YAML::Key << "name" << YAML::Value << view.name;
+ out << YAML::Key << "colorspace" << YAML::Value << view.colorspace;
+ if(!view.looks.empty()) out << YAML::Key << "looks" << YAML::Value << view.looks;
+ out << YAML::EndMap;
+ return out;
+ }
+
+ DisplayMap::iterator find_display(DisplayMap & displays, const std::string & display)
+ {
+ for(DisplayMap::iterator iter = displays.begin();
+ iter != displays.end();
+ ++iter)
+ {
+ if(StrEqualsCaseIgnore(display, iter->first)) return iter;
+ }
+ return displays.end();
+ }
+
+ DisplayMap::const_iterator find_display_const(const DisplayMap & displays, const std::string & display)
+ {
+ for(DisplayMap::const_iterator iter = displays.begin();
+ iter != displays.end();
+ ++iter)
+ {
+ if(StrEqualsCaseIgnore(display, iter->first)) return iter;
+ }
+ return displays.end();
+ }
+
+ int find_view(const ViewVec & vec, const std::string & name)
+ {
+ for(unsigned int i=0; i<vec.size(); ++i)
+ {
+ if(StrEqualsCaseIgnore(name, vec[i].name)) return i;
+ }
+ return -1;
+ }
+
+ void AddDisplay(DisplayMap & displays,
+ const std::string & display,
+ const std::string & view,
+ const std::string & colorspace,
+ const std::string & looks)
+ {
+ DisplayMap::iterator iter = find_display(displays, display);
+ if(iter == displays.end())
+ {
+ ViewVec views;
+ views.push_back( View(view, colorspace, looks) );
+ displays[display] = views;
+ }
+ else
+ {
+ ViewVec & views = iter->second;
+ int index = find_view(views, view);
+ if(index<0)
+ {
+ views.push_back( View(view, colorspace, looks) );
+ }
+ else
+ {
+ views[index].colorspace = colorspace;
+ views[index].looks = looks;
+ }
+ }
+ }
+
+ void ComputeDisplays(StringVec & displayCache,
+ const DisplayMap & displays,
+ const StringVec & activeDisplays,
+ const StringVec & activeDisplaysEnvOverride)
+ {
+ displayCache.clear();
+
+ StringVec displayMasterList;
+ for(DisplayMap::const_iterator iter = displays.begin();
+ iter != displays.end();
+ ++iter)
+ {
+ displayMasterList.push_back(iter->first);
+ }
+
+ // Apply the env override if it's not empty.
+ if(!activeDisplaysEnvOverride.empty())
+ {
+ displayCache = IntersectStringVecsCaseIgnore(displayMasterList, activeDisplaysEnvOverride);
+ if(!displayCache.empty()) return;
+ }
+ // Otherwise, aApply the active displays if it's not empty.
+ else if(!activeDisplays.empty())
+ {
+ displayCache = IntersectStringVecsCaseIgnore(displayMasterList, activeDisplays);
+ if(!displayCache.empty()) return;
+ }
+
+ displayCache = displayMasterList;
+ }
+
+
+
+ } // namespace
+
+ class Config::Impl
+ {
+ public:
+ ContextRcPtr context_;
+ std::string description_;
+ ColorSpaceVec colorspaces_;
+ StringMap roles_;
+ LookVec looksList_;
+
+ DisplayMap displays_;
+ StringVec activeDisplays_;
+ StringVec activeDisplaysEnvOverride_;
+ StringVec activeViews_;
+ StringVec activeViewsEnvOverride_;
+
+ mutable std::string activeDisplaysStr_;
+ mutable std::string activeViewsStr_;
+ mutable StringVec displayCache_;
+
+ // Misc
+ std::vector<float> defaultLumaCoefs_;
+ bool strictParsing_;
+
+ mutable Sanity sanity_;
+ mutable std::string sanitytext_;
+
+ mutable Mutex cacheidMutex_;
+ mutable StringMap cacheids_;
+ mutable std::string cacheidnocontext_;
+
+ Impl() :
+ context_(Context::Create()),
+ strictParsing_(true),
+ sanity_(SANITY_UNKNOWN)
+ {
+ context_->loadEnvironment();
+
+ char* activeDisplays = std::getenv(OCIO_ACTIVE_DISPLAYS_ENVVAR);
+ SplitStringEnvStyle(activeDisplaysEnvOverride_, activeDisplays);
+
+ char * activeViews = std::getenv(OCIO_ACTIVE_VIEWS_ENVVAR);
+ SplitStringEnvStyle(activeViewsEnvOverride_, activeViews);
+
+ defaultLumaCoefs_.resize(3);
+ defaultLumaCoefs_[0] = DEFAULT_LUMA_COEFF_R;
+ defaultLumaCoefs_[1] = DEFAULT_LUMA_COEFF_G;
+ defaultLumaCoefs_[2] = DEFAULT_LUMA_COEFF_B;
+ }
+
+ ~Impl()
+ {
+
+ }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ context_ = rhs.context_->createEditableCopy();
+ description_ = rhs.description_;
+
+ // Deep copy the colorspaces
+ colorspaces_.clear();
+ colorspaces_.reserve(rhs.colorspaces_.size());
+ for(unsigned int i=0; i<rhs.colorspaces_.size(); ++i)
+ {
+ colorspaces_.push_back(rhs.colorspaces_[i]->createEditableCopy());
+ }
+
+ // Deep copy the looks
+ looksList_.clear();
+ looksList_.reserve(rhs.looksList_.size());
+ for(unsigned int i=0; i<rhs.looksList_.size(); ++i)
+ {
+ looksList_.push_back(rhs.looksList_[i]->createEditableCopy());
+ }
+
+ // Assignment operator will suffice for these
+ roles_ = rhs.roles_;
+
+ displays_ = rhs.displays_;
+ activeDisplays_ = rhs.activeDisplays_;
+ activeViews_ = rhs.activeViews_;
+ activeViewsEnvOverride_ = rhs.activeViewsEnvOverride_;
+ activeDisplaysEnvOverride_ = rhs.activeDisplaysEnvOverride_;
+ activeDisplaysStr_ = rhs.activeDisplaysStr_;
+ displayCache_ = rhs.displayCache_;
+
+ defaultLumaCoefs_ = rhs.defaultLumaCoefs_;
+ strictParsing_ = rhs.strictParsing_;
+
+ sanity_ = rhs.sanity_;
+ sanitytext_ = rhs.sanitytext_;
+
+ cacheids_ = rhs.cacheids_;
+ cacheidnocontext_ = cacheidnocontext_;
+ return *this;
+ }
+
+ void load(std::istream & istream, const char * name);
+
+ // Any time you modify the state of the config, you must call this
+ // to reset internal cache states. You also should do this in a
+ // thread safe manner by acquiring the cacheidMutex_;
+ void resetCacheIDs();
+
+ // Get all internal transforms (to generate cacheIDs, validation, etc).
+ // This currently crawls colorspaces + looks
+ void getAllIntenalTransforms(ConstTransformVec & transformVec) const;
+ };
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ConfigRcPtr Config::Create()
+ {
+ return ConfigRcPtr(new Config(), &deleter);
+ }
+
+ void Config::deleter(Config* c)
+ {
+ delete c;
+ }
+
+ ConstConfigRcPtr Config::CreateFromEnv()
+ {
+ char* file = std::getenv(OCIO_CONFIG_ENVVAR);
+ if(file) return CreateFromFile(file);
+
+ std::ostringstream os;
+ os << "Color management disabled. ";
+ os << "(Specify the $OCIO environment variable to enable.)";
+ LogInfo(os.str());
+
+ std::istringstream istream;
+ istream.str(INTERNAL_RAW_PROFILE);
+
+ ConfigRcPtr config = Config::Create();
+ config->getImpl()->load(istream, "");
+ return config;
+ }
+
+ ConstConfigRcPtr Config::CreateFromFile(const char * filename)
+ {
+ std::ifstream istream(filename);
+ if(istream.fail()) {
+ std::ostringstream os;
+ os << "Error could not read '" << filename;
+ os << "' OCIO profile.";
+ throw Exception (os.str().c_str());
+ }
+
+ ConfigRcPtr config = Config::Create();
+ config->getImpl()->load(istream, filename);
+ return config;
+ }
+
+ ConstConfigRcPtr Config::CreateFromStream(std::istream & istream)
+ {
+ ConfigRcPtr config = Config::Create();
+ config->getImpl()->load(istream, "");
+ return config;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ Config::Config()
+ : m_impl(new Config::Impl)
+ {
+ }
+
+ Config::~Config()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ ConfigRcPtr Config::createEditableCopy() const
+ {
+ ConfigRcPtr config = Config::Create();
+ *config->m_impl = *m_impl;
+ return config;
+ }
+
+ void Config::sanityCheck() const
+ {
+ if(getImpl()->sanity_ == SANITY_SANE) return;
+ if(getImpl()->sanity_ == SANITY_INSANE)
+ {
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ getImpl()->sanity_ = SANITY_INSANE;
+ getImpl()->sanitytext_ = "";
+
+
+ ///// COLORSPACES
+ StringSet existingColorSpaces;
+
+ // Confirm all ColorSpaces are valid
+ for(unsigned int i=0; i<getImpl()->colorspaces_.size(); ++i)
+ {
+ if(!getImpl()->colorspaces_[i])
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The colorspace at index " << i << " is null.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ const char * name = getImpl()->colorspaces_[i]->getName();
+ if(!name || strlen(name) == 0)
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The colorspace at index " << i << " is not named.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ std::string namelower = pystring::lower(name);
+ StringSet::const_iterator it = existingColorSpaces.find(namelower);
+ if(it != existingColorSpaces.end())
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "Two colorspaces are defined with the same name, '";
+ os << namelower << "'.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ existingColorSpaces.insert(namelower);
+ }
+
+ // Confirm all roles are valid
+ {
+ for(StringMap::const_iterator iter = getImpl()->roles_.begin(),
+ end = getImpl()->roles_.end(); iter!=end; ++iter)
+ {
+ int csindex = -1;
+ if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, iter->second))
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The role '" << iter->first << "' ";
+ os << "refers to a colorspace, '" << iter->second << "', ";
+ os << "which is not defined.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ // Confirm no name conflicts between colorspaces and roles
+ if(FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, iter->first))
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The role '" << iter->first << "' ";
+ os << " is in conflict with a colorspace of the same name.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+ }
+ }
+
+ ///// DISPLAYS
+
+ int numviews = 0;
+
+ // Confirm all Displays transforms refer to colorspaces that exit
+ for(DisplayMap::const_iterator iter = getImpl()->displays_.begin();
+ iter != getImpl()->displays_.end();
+ ++iter)
+ {
+ std::string display = iter->first;
+ const ViewVec & views = iter->second;
+ if(views.empty())
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The display '" << display << "' ";
+ os << "does not define any views.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ for(unsigned int i=0; i<views.size(); ++i)
+ {
+ if(views[i].name.empty() || views[i].colorspace.empty())
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The display '" << display << "' ";
+ os << "defines a view with an empty name and/or colorspace.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ int csindex = -1;
+ if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, views[i].colorspace))
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The display '" << display << "' ";
+ os << "refers to a colorspace, '" << views[i].colorspace << "', ";
+ os << "which is not defined.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ // Confirm looks references exist
+ LookParseResult looks;
+ const LookParseResult::Options & options = looks.parse(views[i].looks);
+
+ for(unsigned int optionindex=0;
+ optionindex<options.size();
+ ++optionindex)
+ {
+ for(unsigned int tokenindex=0;
+ tokenindex<options[optionindex].size();
+ ++tokenindex)
+ {
+ std::string look = options[optionindex][tokenindex].name;
+
+ if(!look.empty() && !getLook(look.c_str()))
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The display '" << display << "' ";
+ os << "refers to a look, '" << look << "', ";
+ os << "which is not defined.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+ }
+ }
+
+ ++numviews;
+ }
+ }
+
+ // Confirm at least one display entry exists.
+ if(numviews == 0)
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "No displays are specified.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ // Confirm for all Transforms that reference internal colorspaces,
+ // the named space exists
+ {
+ ConstTransformVec allTransforms;
+ getImpl()->getAllIntenalTransforms(allTransforms);
+
+ std::set<std::string> colorSpaceNames;
+ for(unsigned int i=0; i<colorSpaceNames.size(); ++i)
+ {
+ GetColorSpaceReferences(colorSpaceNames, allTransforms[i]);
+ }
+
+ for(std::set<std::string>::iterator iter = colorSpaceNames.begin();
+ iter != colorSpaceNames.end(); ++iter)
+ {
+ int csindex = -1;
+ if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, *iter))
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "This config references a ColorSpace, '" << *iter << "', ";
+ os << "which is not defined.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+ }
+ }
+
+ ///// LOOKS
+
+ // For all looks, confirm the process space exists and the look is named
+ for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
+ {
+ std::string name = getImpl()->looksList_[i]->getName();
+ if(name.empty())
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The look at index '" << i << "' ";
+ os << "does not specify a name.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ std::string processSpace = getImpl()->looksList_[i]->getProcessSpace();
+ if(processSpace.empty())
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The look '" << name << "' ";
+ os << "does not specify a process space.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+
+ int csindex=0;
+ if(!FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, processSpace))
+ {
+ std::ostringstream os;
+ os << "Config failed sanitycheck. ";
+ os << "The look '" << name << "' ";
+ os << "specifies a process color space, '";
+ os << processSpace << "', which is not defined.";
+ getImpl()->sanitytext_ = os.str();
+ throw Exception(getImpl()->sanitytext_.c_str());
+ }
+ }
+
+
+
+ // Everything is groovy.
+ getImpl()->sanity_ = SANITY_SANE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ const char * Config::getDescription() const
+ {
+ return getImpl()->description_.c_str();
+ }
+
+ void Config::setDescription(const char * description)
+ {
+ getImpl()->description_ = description;
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+
+ // RESOURCES //////////////////////////////////////////////////////////////
+
+ ConstContextRcPtr Config::getCurrentContext() const
+ {
+ return getImpl()->context_;
+ }
+
+ const char * Config::getSearchPath() const
+ {
+ return getImpl()->context_->getSearchPath();
+ }
+
+ void Config::setSearchPath(const char * path)
+ {
+ getImpl()->context_->setSearchPath(path);
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+ const char * Config::getWorkingDir() const
+ {
+ return getImpl()->context_->getWorkingDir();
+ }
+
+ void Config::setWorkingDir(const char * dirname)
+ {
+ getImpl()->context_->setWorkingDir(dirname);
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ int Config::getNumColorSpaces() const
+ {
+ return static_cast<int>(getImpl()->colorspaces_.size());
+ }
+
+ const char * Config::getColorSpaceNameByIndex(int index) const
+ {
+ if(index<0 || index >= (int)getImpl()->colorspaces_.size())
+ {
+ return "";
+ }
+
+ return getImpl()->colorspaces_[index]->getName();
+ }
+
+ ConstColorSpaceRcPtr Config::getColorSpace(const char * name) const
+ {
+ int index = getIndexForColorSpace(name);
+ if(index<0 || index >= (int)getImpl()->colorspaces_.size())
+ {
+ return ColorSpaceRcPtr();
+ }
+
+ return getImpl()->colorspaces_[index];
+ }
+
+ int Config::getIndexForColorSpace(const char * name) const
+ {
+ int csindex = -1;
+
+ // Check to see if the name is a color space
+ if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, name) )
+ {
+ return csindex;
+ }
+
+ // Check to see if the name is a role
+ std::string csname = LookupRole(getImpl()->roles_, name);
+ if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
+ {
+ return csindex;
+ }
+
+ // Is a default role defined?
+ // (And, are we allowed to use it)
+ if(!getImpl()->strictParsing_)
+ {
+ csname = LookupRole(getImpl()->roles_, ROLE_DEFAULT);
+ if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
+ {
+ return csindex;
+ }
+ }
+
+ return -1;
+ }
+
+ void Config::addColorSpace(const ConstColorSpaceRcPtr & original)
+ {
+ ColorSpaceRcPtr cs = original->createEditableCopy();
+
+ std::string name = cs->getName();
+ if(name.empty())
+ throw Exception("Cannot addColorSpace with an empty name.");
+
+ // Check to see if the colorspace already exists
+ int csindex = -1;
+ if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, name) )
+ {
+ getImpl()->colorspaces_[csindex] = cs;
+ }
+ else
+ {
+ // Otherwise, add it
+ getImpl()->colorspaces_.push_back( cs );
+ }
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+ void Config::clearColorSpaces()
+ {
+ getImpl()->colorspaces_.clear();
+ }
+
+
+
+
+
+
+ const char * Config::parseColorSpaceFromString(const char * str) const
+ {
+ if(!str) return "";
+
+ // Search the entire filePath, including directory name (if provided)
+ // convert the filename to lowercase.
+ std::string fullstr = pystring::lower(std::string(str));
+
+ // See if it matches a lut name.
+ // This is the position of the RIGHT end of the colorspace substring, not the left
+ int rightMostColorPos=-1;
+ std::string rightMostColorspace = "";
+ int rightMostColorSpaceIndex = -1;
+
+ // Find the right-most occcurance within the string for each colorspace.
+ for (unsigned int i=0; i<getImpl()->colorspaces_.size(); ++i)
+ {
+ std::string csname = pystring::lower(getImpl()->colorspaces_[i]->getName());
+
+ // find right-most extension matched in filename
+ int colorspacePos = pystring::rfind(fullstr, csname);
+ if(colorspacePos < 0)
+ continue;
+
+ // If we have found a match, move the pointer over to the right end of the substring
+ // This will allow us to find the longest name that matches the rightmost colorspace
+ colorspacePos += (int)csname.size();
+
+ if ( (colorspacePos > rightMostColorPos) ||
+ ((colorspacePos == rightMostColorPos) && (csname.size() > rightMostColorspace.size()))
+ )
+ {
+ rightMostColorPos = colorspacePos;
+ rightMostColorspace = csname;
+ rightMostColorSpaceIndex = i;
+ }
+ }
+
+ if(rightMostColorSpaceIndex>=0)
+ {
+ return getImpl()->colorspaces_[rightMostColorSpaceIndex]->getName();
+ }
+
+ if(!getImpl()->strictParsing_)
+ {
+ // Is a default role defined?
+ std::string csname = LookupRole(getImpl()->roles_, ROLE_DEFAULT);
+ if(!csname.empty())
+ {
+ int csindex = -1;
+ if( FindColorSpaceIndex(&csindex, getImpl()->colorspaces_, csname) )
+ {
+ // This is necessary to not return a reference to
+ // a local variable.
+ return getImpl()->colorspaces_[csindex]->getName();
+ }
+ }
+ }
+
+ return "";
+ }
+
+ bool Config::isStrictParsingEnabled() const
+ {
+ return getImpl()->strictParsing_;
+ }
+
+ void Config::setStrictParsingEnabled(bool enabled)
+ {
+ getImpl()->strictParsing_ = enabled;
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+ // Roles
+ void Config::setRole(const char * role, const char * colorSpaceName)
+ {
+ // Set the role
+ if(colorSpaceName)
+ {
+ getImpl()->roles_[pystring::lower(role)] = std::string(colorSpaceName);
+ }
+ // Unset the role
+ else
+ {
+ StringMap::iterator iter = getImpl()->roles_.find(pystring::lower(role));
+ if(iter != getImpl()->roles_.end())
+ {
+ getImpl()->roles_.erase(iter);
+ }
+ }
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+ int Config::getNumRoles() const
+ {
+ return static_cast<int>(getImpl()->roles_.size());
+ }
+
+ bool Config::hasRole(const char * role) const
+ {
+ return LookupRole(getImpl()->roles_, role) == "" ? false : true;
+ }
+
+ const char * Config::getRoleName(int index) const
+ {
+ if(index < 0 || index >= (int)getImpl()->roles_.size()) return "";
+ StringMap::const_iterator iter = getImpl()->roles_.begin();
+ for(int i = 0; i < index; ++i) ++iter;
+ return iter->first.c_str();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ //
+ // Display/View Registration
+
+
+ const char * Config::getDefaultDisplay() const
+ {
+ if(getImpl()->displayCache_.empty())
+ {
+ ComputeDisplays(getImpl()->displayCache_,
+ getImpl()->displays_,
+ getImpl()->activeDisplays_,
+ getImpl()->activeDisplaysEnvOverride_);
+ }
+
+ int index = -1;
+
+ if(!getImpl()->activeDisplaysEnvOverride_.empty())
+ {
+ StringVec orderedDisplays = IntersectStringVecsCaseIgnore(getImpl()->activeDisplaysEnvOverride_,
+ getImpl()->displayCache_);
+ if(!orderedDisplays.empty())
+ {
+ index = FindInStringVecCaseIgnore(getImpl()->displayCache_, orderedDisplays[0]);
+ }
+ }
+ else if(!getImpl()->activeDisplays_.empty())
+ {
+ StringVec orderedDisplays = IntersectStringVecsCaseIgnore(getImpl()->activeDisplays_,
+ getImpl()->displayCache_);
+ if(!orderedDisplays.empty())
+ {
+ index = FindInStringVecCaseIgnore(getImpl()->displayCache_, orderedDisplays[0]);
+ }
+ }
+
+ if(index >= 0)
+ {
+ return getImpl()->displayCache_[index].c_str();
+ }
+
+ if(!getImpl()->displayCache_.empty())
+ {
+ return getImpl()->displayCache_[0].c_str();
+ }
+
+ return "";
+ }
+
+
+ int Config::getNumDisplays() const
+ {
+ if(getImpl()->displayCache_.empty())
+ {
+ ComputeDisplays(getImpl()->displayCache_,
+ getImpl()->displays_,
+ getImpl()->activeDisplays_,
+ getImpl()->activeDisplaysEnvOverride_);
+ }
+
+ return static_cast<int>(getImpl()->displayCache_.size());
+ }
+
+ const char * Config::getDisplay(int index) const
+ {
+ if(getImpl()->displayCache_.empty())
+ {
+ ComputeDisplays(getImpl()->displayCache_,
+ getImpl()->displays_,
+ getImpl()->activeDisplays_,
+ getImpl()->activeDisplaysEnvOverride_);
+ }
+
+ if(index>=0 || index < static_cast<int>(getImpl()->displayCache_.size()))
+ {
+ return getImpl()->displayCache_[index].c_str();
+ }
+
+ return "";
+ }
+
+ const char * Config::getDefaultView(const char * display) const
+ {
+ if(getImpl()->displayCache_.empty())
+ {
+ ComputeDisplays(getImpl()->displayCache_,
+ getImpl()->displays_,
+ getImpl()->activeDisplays_,
+ getImpl()->activeDisplaysEnvOverride_);
+ }
+
+ if(!display) return "";
+
+ DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
+ if(iter == getImpl()->displays_.end()) return "";
+
+ const ViewVec & views = iter->second;
+
+ StringVec masterViews;
+ for(unsigned int i=0; i<views.size(); ++i)
+ {
+ masterViews.push_back(views[i].name);
+ }
+
+ int index = -1;
+
+ if(!getImpl()->activeViewsEnvOverride_.empty())
+ {
+ StringVec orderedViews = IntersectStringVecsCaseIgnore(getImpl()->activeViewsEnvOverride_,
+ masterViews);
+ if(!orderedViews.empty())
+ {
+ index = FindInStringVecCaseIgnore(masterViews, orderedViews[0]);
+ }
+ }
+ else if(!getImpl()->activeViews_.empty())
+ {
+ StringVec orderedViews = IntersectStringVecsCaseIgnore(getImpl()->activeViews_,
+ masterViews);
+ if(!orderedViews.empty())
+ {
+ index = FindInStringVecCaseIgnore(masterViews, orderedViews[0]);
+ }
+ }
+
+ if(index >= 0)
+ {
+ return views[index].name.c_str();
+ }
+
+ if(!views.empty())
+ {
+ return views[0].name.c_str();
+ }
+
+ return "";
+ }
+
+ int Config::getNumViews(const char * display) const
+ {
+ if(getImpl()->displayCache_.empty())
+ {
+ ComputeDisplays(getImpl()->displayCache_,
+ getImpl()->displays_,
+ getImpl()->activeDisplays_,
+ getImpl()->activeDisplaysEnvOverride_);
+ }
+
+ if(!display) return 0;
+
+ DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
+ if(iter == getImpl()->displays_.end()) return 0;
+
+ const ViewVec & views = iter->second;
+ return static_cast<int>(views.size());
+ }
+
+ const char * Config::getView(const char * display, int index) const
+ {
+ if(getImpl()->displayCache_.empty())
+ {
+ ComputeDisplays(getImpl()->displayCache_,
+ getImpl()->displays_,
+ getImpl()->activeDisplays_,
+ getImpl()->activeDisplaysEnvOverride_);
+ }
+
+ if(!display) return "";
+
+ DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
+ if(iter == getImpl()->displays_.end()) return "";
+
+ const ViewVec & views = iter->second;
+ return views[index].name.c_str();
+ }
+
+ const char * Config::getDisplayColorSpaceName(const char * display, const char * view) const
+ {
+ if(!display || !view) return "";
+
+ DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
+ if(iter == getImpl()->displays_.end()) return "";
+
+ const ViewVec & views = iter->second;
+ int index = find_view(views, view);
+ if(index<0) return "";
+
+ return views[index].colorspace.c_str();
+ }
+
+ const char * Config::getDisplayLooks(const char * display, const char * view) const
+ {
+ if(!display || !view) return "";
+
+ DisplayMap::const_iterator iter = find_display_const(getImpl()->displays_, display);
+ if(iter == getImpl()->displays_.end()) return "";
+
+ const ViewVec & views = iter->second;
+ int index = find_view(views, view);
+ if(index<0) return "";
+
+ return views[index].looks.c_str();
+ }
+
+ void Config::addDisplay(const char * display, const char * view,
+ const char * colorSpaceName, const char * lookName)
+ {
+
+ if(!display || !view || !colorSpaceName || !lookName) return;
+
+ AddDisplay(getImpl()->displays_,
+ display, view, colorSpaceName, lookName);
+ getImpl()->displayCache_.clear();
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+ void Config::clearDisplays()
+ {
+ getImpl()->displays_.clear();
+ getImpl()->displayCache_.clear();
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+ void Config::setActiveDisplays(const char * displays)
+ {
+ getImpl()->activeDisplays_.clear();
+ SplitStringEnvStyle(getImpl()->activeDisplays_, displays);
+
+ getImpl()->displayCache_.clear();
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+ const char * Config::getActiveDisplays() const
+ {
+ getImpl()->activeDisplaysStr_ = JoinStringEnvStyle(getImpl()->activeDisplays_);
+ return getImpl()->activeDisplaysStr_.c_str();
+ }
+
+ void Config::setActiveViews(const char * views)
+ {
+ getImpl()->activeViews_.clear();
+ SplitStringEnvStyle(getImpl()->activeViews_, views);
+
+ getImpl()->displayCache_.clear();
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+ const char * Config::getActiveViews() const
+ {
+ getImpl()->activeViewsStr_ = JoinStringEnvStyle(getImpl()->activeViews_);
+ return getImpl()->activeViewsStr_.c_str();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ void Config::getDefaultLumaCoefs(float * c3) const
+ {
+ memcpy(c3, &getImpl()->defaultLumaCoefs_[0], 3*sizeof(float));
+ }
+
+ void Config::setDefaultLumaCoefs(const float * c3)
+ {
+ memcpy(&getImpl()->defaultLumaCoefs_[0], c3, 3*sizeof(float));
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+
+ ConstLookRcPtr Config::getLook(const char * name) const
+ {
+ std::string namelower = pystring::lower(name);
+
+ for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
+ {
+ if(pystring::lower(getImpl()->looksList_[i]->getName()) == namelower)
+ {
+ return getImpl()->looksList_[i];
+ }
+ }
+
+ return ConstLookRcPtr();
+ }
+
+ int Config::getNumLooks() const
+ {
+ return static_cast<int>(getImpl()->looksList_.size());
+ }
+
+ const char * Config::getLookNameByIndex(int index) const
+ {
+ if(index<0 || index>=static_cast<int>(getImpl()->looksList_.size()))
+ {
+ return "";
+ }
+
+ return getImpl()->looksList_[index]->getName();
+ }
+
+ void Config::addLook(const ConstLookRcPtr & look)
+ {
+ std::string name = look->getName();
+ if(name.empty())
+ throw Exception("Cannot addLook with an empty name.");
+
+ std::string namelower = pystring::lower(name);
+
+ // If the look exists, replace it
+ for(unsigned int i=0; i<getImpl()->looksList_.size(); ++i)
+ {
+ if(pystring::lower(getImpl()->looksList_[i]->getName()) == namelower)
+ {
+ getImpl()->looksList_[i] = look->createEditableCopy();
+ return;
+ }
+ }
+
+ // Otherwise, add it
+ getImpl()->looksList_.push_back(look->createEditableCopy());
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+ void Config::clearLooks()
+ {
+ getImpl()->looksList_.clear();
+
+ AutoMutex lock(getImpl()->cacheidMutex_);
+ getImpl()->resetCacheIDs();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ ConstProcessorRcPtr Config::getProcessor(const ConstColorSpaceRcPtr & src,
+ const ConstColorSpaceRcPtr & dst) const
+ {
+ ConstContextRcPtr context = getCurrentContext();
+ return getProcessor(context, src, dst);
+ }
+
+ ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
+ const ConstColorSpaceRcPtr & src,
+ const ConstColorSpaceRcPtr & dst) const
+ {
+ if(!src)
+ {
+ throw Exception("Config::GetProcessor failed. Source colorspace is null.");
+ }
+ if(!dst)
+ {
+ throw Exception("Config::GetProcessor failed. Destination colorspace is null.");
+ }
+
+ ProcessorRcPtr processor = Processor::Create();
+ processor->getImpl()->addColorSpaceConversion(*this, context, src, dst);
+ processor->getImpl()->finalize();
+ return processor;
+ }
+
+ ConstProcessorRcPtr Config::getProcessor(const char * srcName,
+ const char * dstName) const
+ {
+ ConstContextRcPtr context = getCurrentContext();
+ return getProcessor(context, srcName, dstName);
+ }
+
+ //! Names can be colorspace name or role name
+ ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
+ const char * srcName,
+ const char * dstName) const
+ {
+ ConstColorSpaceRcPtr src = getColorSpace(srcName);
+ if(!src)
+ {
+ std::ostringstream os;
+ os << "Could not find colorspace '" << srcName << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ ConstColorSpaceRcPtr dst = getColorSpace(dstName);
+ if(!dst)
+ {
+ std::ostringstream os;
+ os << "Could not find colorspace '" << dstName << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ return getProcessor(context, src, dst);
+ }
+
+
+ ConstProcessorRcPtr Config::getProcessor(const ConstTransformRcPtr& transform) const
+ {
+ return getProcessor(transform, TRANSFORM_DIR_FORWARD);
+ }
+
+
+ ConstProcessorRcPtr Config::getProcessor(const ConstTransformRcPtr& transform,
+ TransformDirection direction) const
+ {
+ ConstContextRcPtr context = getCurrentContext();
+ return getProcessor(context, transform, direction);
+ }
+
+ ConstProcessorRcPtr Config::getProcessor(const ConstContextRcPtr & context,
+ const ConstTransformRcPtr& transform,
+ TransformDirection direction) const
+ {
+ ProcessorRcPtr processor = Processor::Create();
+ processor->getImpl()->addTransform(*this, context, transform, direction);
+ processor->getImpl()->finalize();
+ return processor;
+ }
+
+ std::ostream& operator<< (std::ostream& os, const Config& config)
+ {
+ config.serialize(os);
+ return os;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // CacheID
+
+ const char * Config::getCacheID() const
+ {
+ return getCacheID(getCurrentContext());
+ }
+
+ const char * Config::getCacheID(const ConstContextRcPtr & context) const
+ {
+ AutoMutex lock(getImpl()->cacheidMutex_);
+
+ // A null context will use the empty cacheid
+ std::string contextcacheid = "";
+ if(context) contextcacheid = context->getCacheID();
+
+ StringMap::const_iterator cacheiditer = getImpl()->cacheids_.find(contextcacheid);
+ if(cacheiditer != getImpl()->cacheids_.end())
+ {
+ return cacheiditer->second.c_str();
+ }
+
+ // Include the hash of the yaml config serialization
+ if(getImpl()->cacheidnocontext_.empty())
+ {
+ std::stringstream cacheid;
+ serialize(cacheid);
+ std::string fullstr = cacheid.str();
+ getImpl()->cacheidnocontext_ = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
+ }
+
+ // Also include all file references, using the context (if specified)
+ std::string fileReferencesFashHash = "";
+ if(context)
+ {
+ std::ostringstream filehash;
+
+ ConstTransformVec allTransforms;
+ getImpl()->getAllIntenalTransforms(allTransforms);
+
+ std::set<std::string> files;
+ for(unsigned int i=0; i<allTransforms.size(); ++i)
+ {
+ GetFileReferences(files, allTransforms[i]);
+ }
+
+ for(std::set<std::string>::iterator iter = files.begin();
+ iter != files.end(); ++iter)
+ {
+ if(iter->empty()) continue;
+ filehash << *iter << "=";
+
+ try
+ {
+ std::string resolvedLocation = context->resolveFileLocation(iter->c_str());
+ filehash << GetFastFileHash(resolvedLocation) << " ";
+ }
+ catch(...)
+ {
+ filehash << "? ";
+ continue;
+ }
+ }
+
+ std::string fullstr = filehash.str();
+ fileReferencesFashHash = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
+ }
+
+ getImpl()->cacheids_[contextcacheid] = getImpl()->cacheidnocontext_ + ":" + fileReferencesFashHash;
+ return getImpl()->cacheids_[contextcacheid].c_str();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Serialization
+
+ void Config::serialize(std::ostream& os) const
+ {
+ try
+ {
+ YAML::Emitter out;
+ out << YAML::Block;
+ out << YAML::BeginMap;
+ out << YAML::Key << "ocio_profile_version" << YAML::Value << 1;
+ out << YAML::Newline;
+
+ out << YAML::Key << "search_path" << YAML::Value << getImpl()->context_->getSearchPath();
+ out << YAML::Key << "strictparsing" << YAML::Value << getImpl()->strictParsing_;
+ out << YAML::Key << "luma" << YAML::Value << YAML::Flow << getImpl()->defaultLumaCoefs_;
+
+ if(getImpl()->description_ != "")
+ {
+ out << YAML::Newline;
+ out << YAML::Key << "description";
+ out << YAML::Value << getImpl()->description_;
+ }
+
+ // Roles
+ out << YAML::Newline;
+ out << YAML::Key << "roles";
+ out << YAML::Value << getImpl()->roles_;
+
+ // Displays
+ out << YAML::Newline;
+ out << YAML::Key << "displays";
+ out << YAML::Value << getImpl()->displays_;
+ out << YAML::Newline;
+ out << YAML::Key << "active_displays";
+ out << YAML::Value << YAML::Flow << getImpl()->activeDisplays_;
+ out << YAML::Key << "active_views";
+ out << YAML::Value << YAML::Flow << getImpl()->activeViews_;
+
+ // Looks
+ if(!getImpl()->looksList_.empty())
+ {
+ out << YAML::Newline;
+ out << YAML::Key << "looks";
+ out << YAML::Value << getImpl()->looksList_;
+ }
+
+ // ColorSpaces
+ {
+ out << YAML::Newline;
+ out << YAML::Key << "colorspaces";
+ out << YAML::Value << getImpl()->colorspaces_;
+ }
+
+ out << YAML::EndMap;
+
+ os << out.c_str();
+ }
+ catch( const std::exception & e)
+ {
+ std::ostringstream error;
+ error << "Error building YAML: " << e.what();
+ throw Exception(error.str().c_str());
+ }
+ }
+
+ void Config::Impl::load(std::istream & istream, const char * filename)
+ {
+ try
+ {
+ YAML::Parser parser(istream);
+ YAML::Node node;
+ parser.GetNextDocument(node);
+
+ // check profile version
+ int profile_version = 0;
+ if(node.FindValue("ocio_profile_version") == NULL)
+ {
+ std::ostringstream os;
+ os << "The specified file ";
+ os << "does not appear to be an OCIO configuration.";
+ throw Exception (os.str().c_str());
+ }
+
+ node["ocio_profile_version"] >> profile_version;
+ if(profile_version > 1)
+ {
+ std::ostringstream os;
+ os << "This .ocio config ";
+ if(filename && *filename)
+ {
+ os << " '" << filename << "' ";
+ }
+ os << "is version " << profile_version << ". ";
+ os << "This version of the OpenColorIO library (" << OCIO_VERSION ") ";
+ os << "is not known to be able to load this profile. ";
+ os << "An attempt will be made, but there are no guarantees that the ";
+ os << "results will be accurate. Continue at your own risk.";
+ LogWarning(os.str());
+ }
+
+
+ std::string key, stringval;
+ bool boolval = false;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "ocio_profile_version") { } // Already handled above.
+ else if(key == "search_path" || key == "resource_path")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ context_->setSearchPath(stringval.c_str());
+ }
+ else if(key == "strictparsing")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<bool>(boolval))
+ strictParsing_ = boolval;
+ }
+ else if(key == "description")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ description_ = stringval;
+ }
+ else if(key == "luma")
+ {
+ std::vector<float> val;
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> val;
+ if(val.size() != 3)
+ {
+ std::ostringstream os;
+ os << "'luma' field must be 3 ";
+ os << "floats. Found '" << val.size() << "'.";
+ throw Exception(os.str().c_str());
+ }
+ defaultLumaCoefs_ = val;
+ }
+ }
+ else if(key == "roles")
+ {
+ const YAML::Node& roles = iter.second();
+ if(roles.Type() != YAML::NodeType::Map)
+ {
+ std::ostringstream os;
+ os << "'roles' field needs to be a (name: key) map.";
+ throw Exception(os.str().c_str());
+ }
+ for (YAML::Iterator it = roles.begin();
+ it != roles.end(); ++it)
+ {
+ std::string k, v;
+ it.first() >> k;
+ it.second() >> v;
+ roles_[pystring::lower(k)] = v;
+ }
+ }
+ else if(key == "displays")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> displays_;
+ }
+ }
+ else if(key == "active_displays")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> activeDisplays_;
+ }
+ }
+ else if(key == "active_views")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> activeViews_;
+ }
+ }
+ else if(key == "colorspaces")
+ {
+ const YAML::Node& colorspaces = iter.second();
+
+ if(colorspaces.Type() != YAML::NodeType::Sequence)
+ {
+ std::ostringstream os;
+ os << "'colorspaces' field needs to be a (- !<ColorSpace>) list.";
+ throw Exception(os.str().c_str());
+ }
+
+ for(unsigned i = 0; i < colorspaces.size(); ++i)
+ {
+ if(colorspaces[i].Tag() == "ColorSpace")
+ {
+ ColorSpaceRcPtr cs = ColorSpace::Create();
+ colorspaces[i] >> cs;
+ colorspaces_.push_back( cs );
+ }
+ else
+ {
+ std::ostringstream os;
+ os << "Unknown element found in colorspaces:";
+ os << colorspaces[i].Tag() << ". Only ColorSpace(s)";
+ os << " currently handled.";
+ LogWarning(os.str());
+ }
+ }
+ }
+ else if(key == "looks")
+ {
+ const YAML::Node& looks = iter.second();
+
+ if(looks.Type() != YAML::NodeType::Sequence)
+ {
+ std::ostringstream os;
+ os << "'looks' field needs to be a (- !<Look>) list.";
+ throw Exception(os.str().c_str());
+ }
+
+ for(unsigned i = 0; i < looks.size(); ++i)
+ {
+ if(looks[i].Tag() == "Look")
+ {
+ LookRcPtr look = Look::Create();
+ looks[i] >> look;
+ looksList_.push_back( look );
+ }
+ else
+ {
+ std::ostringstream os;
+ os << "Unknown element found in looks:";
+ os << looks[i].Tag() << ". Only Look(s)";
+ os << " currently handled.";
+ LogWarning(os.str());
+ }
+ }
+ }
+ else
+ {
+ LogUnknownKeyWarning("profile", iter.first());
+ }
+ }
+
+ if(filename)
+ {
+ std::string realfilename = pystring::os::path::abspath(filename);
+ std::string configrootdir = pystring::os::path::dirname(realfilename);
+ context_->setWorkingDir(configrootdir.c_str());
+ }
+ }
+ catch( const std::exception & e)
+ {
+ std::ostringstream os;
+ os << "Error: Loading the OCIO profile ";
+ if(filename) os << "'" << filename << "' ";
+ os << "failed. " << e.what();
+ throw Exception(os.str().c_str());
+ }
+ }
+
+ void Config::Impl::resetCacheIDs()
+ {
+ cacheids_.clear();
+ cacheidnocontext_ = "";
+ sanity_ = SANITY_UNKNOWN;
+ sanitytext_ = "";
+ }
+
+ void Config::Impl::getAllIntenalTransforms(ConstTransformVec & transformVec) const
+ {
+ // Grab all transforms from the ColorSpaces
+ for(unsigned int i=0; i<colorspaces_.size(); ++i)
+ {
+ if(colorspaces_[i]->getTransform(COLORSPACE_DIR_TO_REFERENCE))
+ transformVec.push_back(colorspaces_[i]->getTransform(COLORSPACE_DIR_TO_REFERENCE));
+ if(colorspaces_[i]->getTransform(COLORSPACE_DIR_FROM_REFERENCE))
+ transformVec.push_back(colorspaces_[i]->getTransform(COLORSPACE_DIR_FROM_REFERENCE));
+ }
+
+ // Grab all transforms from the Looks
+ for(unsigned int i=0; i<looksList_.size(); ++i)
+ {
+ if(looksList_[i]->getTransform())
+ transformVec.push_back(looksList_[i]->getTransform());
+ if(looksList_[i]->getInverseTransform())
+ transformVec.push_back(looksList_[i]->getInverseTransform());
+ }
+
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+#include <sys/stat.h>
+#include "pystring/pystring.h"
+
+#if 0
+OIIO_ADD_TEST(Config, test_searchpath_filesystem)
+{
+
+ OCIO::EnvMap env = OCIO::GetEnvMap();
+ std::string OCIO_TEST_AREA("$OCIO_TEST_AREA");
+ EnvExpand(&OCIO_TEST_AREA, &env);
+
+ OCIO::ConfigRcPtr config = OCIO::Config::Create();
+
+ // basic get/set/expand
+ config->setSearchPath("."
+ ":$OCIO_TEST1"
+ ":/$OCIO_JOB/${OCIO_SEQ}/$OCIO_SHOT/ocio");
+
+ OIIO_CHECK_ASSERT(strcmp(config->getSearchPath(),
+ ".:$OCIO_TEST1:/$OCIO_JOB/${OCIO_SEQ}/$OCIO_SHOT/ocio") == 0);
+ OIIO_CHECK_ASSERT(strcmp(config->getSearchPath(true),
+ ".:foobar:/meatballs/cheesecake/mb-cc-001/ocio") == 0);
+
+ // find some files
+ config->setSearchPath(".."
+ ":$OCIO_TEST1"
+ ":${OCIO_TEST_AREA}/test_search/one"
+ ":$OCIO_TEST_AREA/test_search/two");
+
+ // setup for search test
+ std::string base_dir("$OCIO_TEST_AREA/test_search/");
+ EnvExpand(&base_dir, &env);
+ mkdir(base_dir.c_str(), 0777);
+
+ std::string one_dir("$OCIO_TEST_AREA/test_search/one/");
+ EnvExpand(&one_dir, &env);
+ mkdir(one_dir.c_str(), 0777);
+
+ std::string two_dir("$OCIO_TEST_AREA/test_search/two/");
+ EnvExpand(&two_dir, &env);
+ mkdir(two_dir.c_str(), 0777);
+
+ std::string lut1(one_dir+"somelut1.lut");
+ std::ofstream somelut1(lut1.c_str());
+ somelut1.close();
+
+ std::string lut2(two_dir+"somelut2.lut");
+ std::ofstream somelut2(lut2.c_str());
+ somelut2.close();
+
+ std::string lut3(two_dir+"somelut3.lut");
+ std::ofstream somelut3(lut3.c_str());
+ somelut3.close();
+
+ std::string lutdotdot(OCIO_TEST_AREA+"/lutdotdot.lut");
+ std::ofstream somelutdotdot(lutdotdot.c_str());
+ somelutdotdot.close();
+
+ // basic search test
+ OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut1.lut"),
+ lut1.c_str()) == 0);
+ OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut2.lut"),
+ lut2.c_str()) == 0);
+ OIIO_CHECK_ASSERT(strcmp(config->findFile("somelut3.lut"),
+ lut3.c_str()) == 0);
+ OIIO_CHECK_ASSERT(strcmp(config->findFile("lutdotdot.lut"),
+ lutdotdot.c_str()) == 0);
+
+}
+#endif
+
+OIIO_ADD_TEST(Config, InternalRawProfile)
+{
+ std::istringstream is;
+ is.str(OCIO::INTERNAL_RAW_PROFILE);
+ OIIO_CHECK_NO_THOW(OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromStream(is));
+}
+
+OIIO_ADD_TEST(Config, SimpleConfig)
+{
+
+ std::string SIMPLE_PROFILE =
+ "ocio_profile_version: 1\n"
+ "resource_path: luts\n"
+ "strictparsing: false\n"
+ "luma: [0.2126, 0.7152, 0.0722]\n"
+ "roles:\n"
+ " compositing_log: lgh\n"
+ " default: raw\n"
+ " scene_linear: lnh\n"
+ "displays:\n"
+ " sRGB:\n"
+ " - !<View> {name: Film1D, colorspace: vd8}\n"
+ " - !<View> {name: Log, colorspace: lg10}\n"
+ " - !<View> {name: Raw, colorspace: raw}\n"
+ "colorspaces:\n"
+ " - !<ColorSpace>\n"
+ " name: raw\n"
+ " family: raw\n"
+ " equalitygroup: \n"
+ " bitdepth: 32f\n"
+ " description: |\n"
+ " A raw color space. Conversions to and from this space are no-ops.\n"
+ " isdata: true\n"
+ " allocation: uniform\n"
+ " - !<ColorSpace>\n"
+ " name: lnh\n"
+ " family: ln\n"
+ " equalitygroup: \n"
+ " bitdepth: 16f\n"
+ " description: |\n"
+ " The show reference space. This is a sensor referred linear\n"
+ " representation of the scene with primaries that correspond to\n"
+ " scanned film. 0.18 in this space corresponds to a properly\n"
+ " exposed 18% grey card.\n"
+ " isdata: false\n"
+ " allocation: lg2\n"
+ " - !<ColorSpace>\n"
+ " name: loads_of_transforms\n"
+ " family: vd8\n"
+ " equalitygroup: \n"
+ " bitdepth: 8ui\n"
+ " description: 'how many transforms can we use?'\n"
+ " isdata: false\n"
+ " allocation: uniform\n"
+ " to_reference: !<GroupTransform>\n"
+ " direction: forward\n"
+ " children:\n"
+ " - !<FileTransform>\n"
+ " src: diffusemult.spimtx\n"
+ " interpolation: unknown\n"
+ " - !<ColorSpaceTransform>\n"
+ " src: vd8\n"
+ " dst: lnh\n"
+ " - !<ExponentTransform>\n"
+ " value: [2.2, 2.2, 2.2, 1]\n"
+ " - !<MatrixTransform>\n"
+ " matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]\n"
+ " offset: [0, 0, 0, 0]\n"
+ " - !<CDLTransform>\n"
+ " slope: [1, 1, 1]\n"
+ " offset: [0, 0, 0]\n"
+ " power: [1, 1, 1]\n"
+ " saturation: 1\n"
+ "\n";
+
+ std::istringstream is;
+ is.str(SIMPLE_PROFILE);
+ OCIO::ConstConfigRcPtr config;
+ OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is));
+}
+
+OIIO_ADD_TEST(Config, Roles)
+{
+
+ std::string SIMPLE_PROFILE =
+ "ocio_profile_version: 1\n"
+ "strictparsing: false\n"
+ "roles:\n"
+ " compositing_log: lgh\n"
+ " default: raw\n"
+ " scene_linear: lnh\n"
+ "colorspaces:\n"
+ " - !<ColorSpace>\n"
+ " name: raw\n"
+ " - !<ColorSpace>\n"
+ " name: lnh\n"
+ " - !<ColorSpace>\n"
+ " name: lgh\n"
+ "\n";
+
+ std::istringstream is;
+ is.str(SIMPLE_PROFILE);
+ OCIO::ConstConfigRcPtr config;
+ OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is));
+
+ OIIO_CHECK_EQUAL(config->getNumRoles(), 3);
+
+ OIIO_CHECK_ASSERT(config->hasRole("compositing_log") == true);
+ OIIO_CHECK_ASSERT(config->hasRole("cheese") == false);
+ OIIO_CHECK_ASSERT(config->hasRole("") == false);
+
+ OIIO_CHECK_ASSERT(strcmp(config->getRoleName(2), "scene_linear") == 0);
+ OIIO_CHECK_ASSERT(strcmp(config->getRoleName(0), "compositing_log") == 0);
+ OIIO_CHECK_ASSERT(strcmp(config->getRoleName(1), "default") == 0);
+ OIIO_CHECK_ASSERT(strcmp(config->getRoleName(10), "") == 0);
+ OIIO_CHECK_ASSERT(strcmp(config->getRoleName(-4), "") == 0);
+
+}
+
+OIIO_ADD_TEST(Config, Serialize)
+{
+
+ OCIO::ConfigRcPtr config = OCIO::Config::Create();
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("testing");
+ cs->setFamily("test");
+ OCIO::FileTransformRcPtr transform1 = \
+ OCIO::FileTransform::Create();
+ OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create();
+ groupTransform->push_back(transform1);
+ cs->setTransform(groupTransform, OCIO::COLORSPACE_DIR_TO_REFERENCE);
+ config->addColorSpace(cs);
+ config->setRole( OCIO::ROLE_COMPOSITING_LOG, cs->getName() );
+ }
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("testing2");
+ cs->setFamily("test");
+ OCIO::ExponentTransformRcPtr transform1 = \
+ OCIO::ExponentTransform::Create();
+ OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create();
+ groupTransform->push_back(transform1);
+ cs->setTransform(groupTransform, OCIO::COLORSPACE_DIR_TO_REFERENCE);
+ config->addColorSpace(cs);
+ config->setRole( OCIO::ROLE_COMPOSITING_LOG, cs->getName() );
+ }
+
+ // for testing
+ //std::ofstream outfile("/tmp/test.ocio");
+ //config->serialize(outfile);
+ //outfile.close();
+
+ std::ostringstream os;
+ config->serialize(os);
+
+ std::string PROFILE_OUT =
+ "ocio_profile_version: 1\n"
+ "\n"
+ "search_path: \"\"\n"
+ "strictparsing: true\n"
+ "luma: [0.2126, 0.7152, 0.0722]\n"
+ "\n"
+ "roles:\n"
+ " compositing_log: testing2\n"
+ "\n"
+ "displays:\n"
+ " {}\n"
+ "\n"
+ "active_displays: []\n"
+ "active_views: []\n"
+ "\n"
+ "colorspaces:\n"
+ " - !<ColorSpace>\n"
+ " name: testing\n"
+ " family: test\n"
+ " equalitygroup: \"\"\n"
+ " bitdepth: unknown\n"
+ " isdata: false\n"
+ " allocation: uniform\n"
+ " to_reference: !<GroupTransform>\n"
+ " children:\n"
+ " - !<FileTransform> {src: \"\", interpolation: unknown}\n"
+ "\n"
+ " - !<ColorSpace>\n"
+ " name: testing2\n"
+ " family: test\n"
+ " equalitygroup: \"\"\n"
+ " bitdepth: unknown\n"
+ " isdata: false\n"
+ " allocation: uniform\n"
+ " to_reference: !<GroupTransform>\n"
+ " children:\n"
+ " - !<ExponentTransform> {value: [1, 1, 1, 1]}\n";
+
+ std::vector<std::string> osvec;
+ OCIO::pystring::splitlines(os.str(), osvec);
+ std::vector<std::string> PROFILE_OUTvec;
+ OCIO::pystring::splitlines(PROFILE_OUT, PROFILE_OUTvec);
+
+ OIIO_CHECK_EQUAL(osvec.size(), PROFILE_OUTvec.size());
+ for(unsigned int i = 0; i < PROFILE_OUTvec.size(); ++i)
+ OIIO_CHECK_EQUAL(osvec[i], PROFILE_OUTvec[i]);
+}
+
+
+OIIO_ADD_TEST(Config, SanityCheck)
+{
+ {
+ std::string SIMPLE_PROFILE =
+ "ocio_profile_version: 1\n"
+ "colorspaces:\n"
+ " - !<ColorSpace>\n"
+ " name: raw\n"
+ " - !<ColorSpace>\n"
+ " name: raw\n"
+ "strictparsing: false\n"
+ "roles:\n"
+ " default: raw\n"
+ "displays:\n"
+ " sRGB:\n"
+ " - !<View> {name: Raw, colorspace: raw}\n"
+ "\n";
+
+ std::istringstream is;
+ is.str(SIMPLE_PROFILE);
+ OCIO::ConstConfigRcPtr config;
+ OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is));
+
+ OIIO_CHECK_THOW(config->sanityCheck(), OCIO::Exception);
+ }
+
+ {
+ std::string SIMPLE_PROFILE =
+ "ocio_profile_version: 1\n"
+ "colorspaces:\n"
+ " - !<ColorSpace>\n"
+ " name: raw\n"
+ "strictparsing: false\n"
+ "roles:\n"
+ " default: raw\n"
+ "displays:\n"
+ " sRGB:\n"
+ " - !<View> {name: Raw, colorspace: raw}\n"
+ "\n";
+
+ std::istringstream is;
+ is.str(SIMPLE_PROFILE);
+ OCIO::ConstConfigRcPtr config;
+ OIIO_CHECK_NO_THOW(config = OCIO::Config::CreateFromStream(is));
+
+ OIIO_CHECK_NO_THOW(config->sanityCheck());
+ }
+}
+
+
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/Context.cpp b/src/core/Context.cpp
new file mode 100644
index 0000000..25d4ea7
--- /dev/null
+++ b/src/core/Context.cpp
@@ -0,0 +1,364 @@
+/*
+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 <map>
+#include <string>
+#include <iostream>
+#include <sstream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "HashUtils.h"
+#include "Mutex.h"
+#include "PathUtils.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+
+namespace
+{
+ typedef std::map< std::string, std::string> StringMap;
+
+ void GetAbsoluteSearchPaths(std::vector<std::string> & searchpaths,
+ const std::string & pathString,
+ const std::string & configRootDir);
+}
+
+ class Context::Impl
+ {
+ public:
+ std::string searchPath_;
+ std::string workingDir_;
+ EnvMap envMap_;
+
+ mutable std::string cacheID_;
+ mutable StringMap resultsCache_;
+ mutable Mutex resultsCacheMutex_;
+
+ Impl()
+ {
+ }
+
+ ~Impl()
+ {
+
+ }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ AutoMutex lock1(resultsCacheMutex_);
+ AutoMutex lock2(rhs.resultsCacheMutex_);
+
+ searchPath_ = rhs.searchPath_;
+ workingDir_ = rhs.workingDir_;
+ envMap_ = rhs.envMap_;
+
+ resultsCache_ = rhs.resultsCache_;
+ cacheID_ = rhs.cacheID_;
+
+ return *this;
+ }
+ };
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ContextRcPtr Context::Create()
+ {
+ return ContextRcPtr(new Context(), &deleter);
+ }
+
+ void Context::deleter(Context* c)
+ {
+ delete c;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ Context::Context()
+ : m_impl(new Context::Impl)
+ {
+ }
+
+ Context::~Context()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ ContextRcPtr Context::createEditableCopy() const
+ {
+ ContextRcPtr context = Context::Create();
+ *context->m_impl = *getImpl();
+ return context;
+ }
+
+ const char * Context::getCacheID() const
+ {
+ AutoMutex lock(getImpl()->resultsCacheMutex_);
+
+ if(getImpl()->cacheID_.empty())
+ {
+ std::ostringstream cacheid;
+ cacheid << "Search Path " << getImpl()->searchPath_ << " ";
+ cacheid << "Working Dir " << getImpl()->workingDir_ << " ";
+
+ for (EnvMap::const_iterator iter = getImpl()->envMap_.begin(),
+ end = getImpl()->envMap_.end();
+ iter != end; ++iter)
+ {
+ cacheid << iter->first << "=" << iter->second << " ";
+ }
+
+ std::string fullstr = cacheid.str();
+ getImpl()->cacheID_ = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
+ }
+
+ return getImpl()->cacheID_.c_str();
+ }
+
+ void Context::setSearchPath(const char * path)
+ {
+ AutoMutex lock(getImpl()->resultsCacheMutex_);
+
+ getImpl()->searchPath_ = path;
+ getImpl()->resultsCache_.clear();
+ getImpl()->cacheID_ = "";
+ }
+
+ const char * Context::getSearchPath() const
+ {
+ return getImpl()->searchPath_.c_str();
+ }
+
+ void Context::setWorkingDir(const char * dirname)
+ {
+ AutoMutex lock(getImpl()->resultsCacheMutex_);
+
+ getImpl()->workingDir_ = dirname;
+ getImpl()->resultsCache_.clear();
+ getImpl()->cacheID_ = "";
+ }
+
+ const char * Context::getWorkingDir() const
+ {
+ return getImpl()->workingDir_.c_str();
+ }
+
+ void Context::loadEnvironment()
+ {
+ LoadEnvironment(getImpl()->envMap_);
+ }
+
+ void Context::setStringVar(const char * name, const char * value)
+ {
+ if(!name) return;
+
+ AutoMutex lock(getImpl()->resultsCacheMutex_);
+ getImpl()->resultsCache_.clear();
+ getImpl()->cacheID_ = "";
+
+ // Set the value if specified
+ if(value)
+ {
+ getImpl()->envMap_[name] = value;
+ }
+ // If a null value is specified, erase it
+ else
+ {
+ EnvMap::iterator iter = getImpl()->envMap_.find(name);
+ if(iter != getImpl()->envMap_.end())
+ {
+ getImpl()->envMap_.erase(iter);
+ }
+ }
+ }
+
+ const char * Context::getStringVar(const char * name) const
+ {
+ if(!name) return "";
+
+ EnvMap::const_iterator iter = getImpl()->envMap_.find(name);
+ if(iter != getImpl()->envMap_.end())
+ {
+ return iter->second.c_str();
+ }
+
+ return "";
+ }
+
+ int Context::getNumStringVars() const
+ {
+ return static_cast<int>(getImpl()->envMap_.size());
+ }
+
+ const char * Context::getStringVarNameByIndex(int index) const
+ {
+ if(index < 0 || index >= static_cast<int>(getImpl()->envMap_.size()))
+ return "";
+
+ EnvMap::const_iterator iter = getImpl()->envMap_.begin();
+ for(int count = 0; count<index; ++count) ++iter;
+
+ return iter->first.c_str();
+ }
+
+ const char * Context::resolveStringVar(const char * val) const
+ {
+ AutoMutex lock(getImpl()->resultsCacheMutex_);
+
+ if(!val || !*val)
+ {
+ return "";
+ }
+
+ StringMap::const_iterator iter = getImpl()->resultsCache_.find(val);
+ if(iter != getImpl()->resultsCache_.end())
+ {
+ return iter->second.c_str();
+ }
+
+
+ std::string resolvedval = EnvExpand(val, getImpl()->envMap_);
+
+ getImpl()->resultsCache_[val] = resolvedval;
+ return getImpl()->resultsCache_[val].c_str();
+ }
+
+
+
+ const char * Context::resolveFileLocation(const char * filename) const
+ {
+ AutoMutex lock(getImpl()->resultsCacheMutex_);
+
+ if(!filename || !*filename)
+ {
+ return "";
+ }
+
+ StringMap::const_iterator iter = getImpl()->resultsCache_.find(filename);
+ if(iter != getImpl()->resultsCache_.end())
+ {
+ return iter->second.c_str();
+ }
+
+ // Load an absolute file reference
+ if(pystring::os::path::isabs(filename))
+ {
+ std::string expandedfullpath = EnvExpand(filename, getImpl()->envMap_);
+ if(FileExists(expandedfullpath))
+ {
+ getImpl()->resultsCache_[filename] = expandedfullpath;
+ return getImpl()->resultsCache_[filename].c_str();
+ }
+
+ std::ostringstream errortext;
+ errortext << "The specified absolute file reference ";
+ errortext << "'" << expandedfullpath << "' could not be located. ";
+ throw Exception(errortext.str().c_str());
+ }
+
+ // Load a relative file reference
+ // Prep the search path vector
+ // TODO: Cache this prepped vector?
+ std::vector<std::string> searchpaths;
+ GetAbsoluteSearchPaths(searchpaths,
+ getImpl()->searchPath_,
+ getImpl()->workingDir_);
+
+ // Loop over each path, and try to find the file
+ std::ostringstream errortext;
+ errortext << "The specified file reference ";
+ errortext << " '" << filename << "' could not be located. ";
+ errortext << "The following attempts were made: ";
+
+ for (unsigned int i = 0; i < searchpaths.size(); ++i)
+ {
+ // Make an attempt to find the lut in one of the search paths
+ std::string fullpath = pystring::os::path::join(searchpaths[i], filename);
+ std::string expandedfullpath = EnvExpand(fullpath, getImpl()->envMap_);
+ if(FileExists(expandedfullpath))
+ {
+ getImpl()->resultsCache_[filename] = expandedfullpath;
+ return getImpl()->resultsCache_[filename].c_str();
+ }
+ if(i!=0) errortext << " : ";
+ errortext << expandedfullpath;
+ }
+
+ throw ExceptionMissingFile(errortext.str().c_str());
+ }
+
+ std::ostream& operator<< (std::ostream& os, const Context& context)
+ {
+ os << "Context:\n";
+ for(int i=0; i<context.getNumStringVars(); ++i)
+ {
+ const char * key = context.getStringVarNameByIndex(i);
+ os << key << "=" << context.getStringVar(key) << "\n";
+ }
+ return os;
+ }
+
+
+namespace
+{
+ void GetAbsoluteSearchPaths(std::vector<std::string> & searchpaths,
+ const std::string & pathString,
+ const std::string & workingDir)
+ {
+ if(pathString.empty())
+ {
+ searchpaths.push_back(workingDir);
+ return;
+ }
+
+ std::vector<std::string> parts;
+ pystring::split(pathString, parts, ":");
+
+ for (unsigned int i = 0; i < parts.size(); ++i)
+ {
+ // Remove trailing "/", and spaces
+ std::string dirname = pystring::rstrip(pystring::strip(parts[i]), "/");
+
+ if(!pystring::os::path::isabs(dirname))
+ {
+ dirname = pystring::os::path::join(workingDir, dirname);
+ }
+
+ searchpaths.push_back(pystring::os::path::normpath(dirname));
+ }
+ }
+}
+
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/DisplayTransform.cpp b/src/core/DisplayTransform.cpp
new file mode 100644
index 0000000..35216db
--- /dev/null
+++ b/src/core/DisplayTransform.cpp
@@ -0,0 +1,410 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "OpBuilders.h"
+
+#include <cmath>
+#include <cstring>
+#include <iterator>
+
+OCIO_NAMESPACE_ENTER
+{
+ DisplayTransformRcPtr DisplayTransform::Create()
+ {
+ return DisplayTransformRcPtr(new DisplayTransform(), &deleter);
+ }
+
+ void DisplayTransform::deleter(DisplayTransform* t)
+ {
+ delete t;
+ }
+
+ class DisplayTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+ std::string inputColorSpaceName_;
+ TransformRcPtr linearCC_;
+ TransformRcPtr colorTimingCC_;
+ TransformRcPtr channelView_;
+ std::string display_;
+ std::string view_;
+ TransformRcPtr displayCC_;
+
+ std::string looksOverride_;
+ bool looksOverrideEnabled_;
+
+ Impl() :
+ dir_(TRANSFORM_DIR_FORWARD),
+ looksOverrideEnabled_(false)
+ { }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+ inputColorSpaceName_ = rhs.inputColorSpaceName_;
+
+ linearCC_ = rhs.linearCC_;
+ if(linearCC_) linearCC_ = linearCC_->createEditableCopy();
+
+ colorTimingCC_ = rhs.colorTimingCC_;
+ if(colorTimingCC_) colorTimingCC_ = colorTimingCC_->createEditableCopy();
+
+ channelView_ = rhs.channelView_;
+ if(channelView_) channelView_ = channelView_->createEditableCopy();
+
+ display_ = rhs.display_;
+ view_ = rhs.view_;
+
+ displayCC_ = rhs.displayCC_;
+ if(displayCC_) displayCC_ = displayCC_->createEditableCopy();
+
+ looksOverride_ = rhs.looksOverride_;
+ looksOverrideEnabled_ = rhs.looksOverrideEnabled_;
+
+ return *this;
+ }
+ };
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ DisplayTransform::DisplayTransform()
+ : m_impl(new DisplayTransform::Impl)
+ {
+ }
+
+ TransformRcPtr DisplayTransform::createEditableCopy() const
+ {
+ DisplayTransformRcPtr transform = DisplayTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ DisplayTransform::~DisplayTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ DisplayTransform& DisplayTransform::operator= (const DisplayTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection DisplayTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void DisplayTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+ void DisplayTransform::setInputColorSpaceName(const char * name)
+ {
+ getImpl()->inputColorSpaceName_ = name;
+ }
+
+ const char * DisplayTransform::getInputColorSpaceName() const
+ {
+ return getImpl()->inputColorSpaceName_.c_str();
+ }
+
+ void DisplayTransform::setLinearCC(const ConstTransformRcPtr & cc)
+ {
+ getImpl()->linearCC_ = cc->createEditableCopy();
+ }
+
+ ConstTransformRcPtr DisplayTransform::getLinearCC() const
+ {
+ return getImpl()->linearCC_;
+ }
+
+ void DisplayTransform::setColorTimingCC(const ConstTransformRcPtr & cc)
+ {
+ getImpl()->colorTimingCC_ = cc->createEditableCopy();
+ }
+
+ ConstTransformRcPtr DisplayTransform::getColorTimingCC() const
+ {
+ return getImpl()->colorTimingCC_;
+ }
+
+ void DisplayTransform::setChannelView(const ConstTransformRcPtr & transform)
+ {
+ getImpl()->channelView_ = transform->createEditableCopy();
+ }
+
+ ConstTransformRcPtr DisplayTransform::getChannelView() const
+ {
+ return getImpl()->channelView_;
+ }
+
+ void DisplayTransform::setDisplay(const char * display)
+ {
+ getImpl()->display_ = display;
+ }
+
+ const char * DisplayTransform::getDisplay() const
+ {
+ return getImpl()->display_.c_str();
+ }
+
+ void DisplayTransform::setView(const char * view)
+ {
+ getImpl()->view_ = view;
+ }
+
+ const char * DisplayTransform::getView() const
+ {
+ return getImpl()->view_.c_str();
+ }
+
+ void DisplayTransform::setDisplayCC(const ConstTransformRcPtr & cc)
+ {
+ getImpl()->displayCC_ = cc->createEditableCopy();
+ }
+
+ ConstTransformRcPtr DisplayTransform::getDisplayCC() const
+ {
+ return getImpl()->displayCC_;
+ }
+
+ void DisplayTransform::setLooksOverride(const char * looks)
+ {
+ getImpl()->looksOverride_ = looks;
+ }
+
+ const char * DisplayTransform::getLooksOverride() const
+ {
+ return getImpl()->looksOverride_.c_str();
+ }
+
+ void DisplayTransform::setLooksOverrideEnabled(bool enabled)
+ {
+ getImpl()->looksOverrideEnabled_ = enabled;
+ }
+
+ bool DisplayTransform::getLooksOverrideEnabled() const
+ {
+ return getImpl()->looksOverrideEnabled_;
+ }
+
+ std::ostream& operator<< (std::ostream& os, const DisplayTransform& t)
+ {
+ os << "<DisplayTransform ";
+ os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
+ os << "inputColorSpace=" << t.getInputColorSpaceName() << ", ";
+ os << "display=" << t.getDisplay() << ", ";
+ os << "view=" << t.getView() << ", ";
+ os << ">\n";
+ return os;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ void BuildDisplayOps(OpRcPtrVec & ops,
+ const Config & config,
+ const ConstContextRcPtr & context,
+ const DisplayTransform & displayTransform,
+ TransformDirection dir)
+ {
+ TransformDirection combinedDir = CombineTransformDirections(dir,
+ displayTransform.getDirection());
+ if(combinedDir != TRANSFORM_DIR_FORWARD)
+ {
+ std::ostringstream os;
+ os << "DisplayTransform can only be applied in the forward direction.";
+ throw Exception(os.str().c_str());
+ }
+
+ std::string inputColorSpaceName = displayTransform.getInputColorSpaceName();
+ ConstColorSpaceRcPtr inputColorSpace = config.getColorSpace(inputColorSpaceName.c_str());
+ if(!inputColorSpace)
+ {
+ std::ostringstream os;
+ os << "DisplayTransform error.";
+ if(inputColorSpaceName.empty()) os << " InputColorSpaceName is unspecified.";
+ else os << " Cannot find inputColorSpace, named '" << inputColorSpaceName << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ std::string display = displayTransform.getDisplay();
+ std::string view = displayTransform.getView();
+
+ std::string displayColorSpaceName = config.getDisplayColorSpaceName(display.c_str(), view.c_str());
+ ConstColorSpaceRcPtr displayColorspace = config.getColorSpace(displayColorSpaceName.c_str());
+ if(!displayColorspace)
+ {
+ std::ostringstream os;
+ os << "DisplayTransform error.";
+ os << " Cannot find display colorspace, '" << displayColorSpaceName << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ bool skipColorSpaceConversions = (inputColorSpace->isData() || displayColorspace->isData());
+
+ // If we're viewing alpha, also skip all color space conversions.
+ // If the user does uses a different transform for the channel view,
+ // in place of a simple matrix, they run the risk that when viewing alpha
+ // the colorspace transforms will not be skipped. (I.e., filmlook will be applied
+ // to alpha.) If this ever becomes an issue, additional engineering will be
+ // added at that time.
+
+ ConstMatrixTransformRcPtr typedChannelView = DynamicPtrCast<const MatrixTransform>(
+ displayTransform.getChannelView());
+ if(typedChannelView)
+ {
+ float matrix44[16];
+ typedChannelView->getValue(matrix44, 0x0);
+
+ if((matrix44[3]>0.0f) || (matrix44[7]>0.0f) || (matrix44[11]>0.0f))
+ {
+ skipColorSpaceConversions = true;
+ }
+ }
+
+
+
+ ConstColorSpaceRcPtr currentColorSpace = inputColorSpace;
+
+
+
+ // Apply a transform in ROLE_SCENE_LINEAR
+ ConstTransformRcPtr linearCC = displayTransform.getLinearCC();
+ if(linearCC)
+ {
+ // Put the new ops into a temp array, to see if it's a no-op
+ // If it is a no-op, dont bother doing the colorspace conversion.
+ OpRcPtrVec tmpOps;
+ BuildOps(tmpOps, config, context, linearCC, TRANSFORM_DIR_FORWARD);
+
+ if(!IsOpVecNoOp(tmpOps))
+ {
+ ConstColorSpaceRcPtr targetColorSpace = config.getColorSpace(ROLE_SCENE_LINEAR);
+
+ if(!skipColorSpaceConversions)
+ {
+ BuildColorSpaceOps(ops, config, context,
+ currentColorSpace,
+ targetColorSpace);
+ currentColorSpace = targetColorSpace;
+ }
+
+ std::copy(tmpOps.begin(), tmpOps.end(), std::back_inserter(ops));
+ }
+ }
+
+
+ // Apply a color correction, in ROLE_COLOR_TIMING
+ ConstTransformRcPtr colorTimingCC = displayTransform.getColorTimingCC();
+ if(colorTimingCC)
+ {
+ // Put the new ops into a temp array, to see if it's a no-op
+ // If it is a no-op, dont bother doing the colorspace conversion.
+ OpRcPtrVec tmpOps;
+ BuildOps(tmpOps, config, context, colorTimingCC, TRANSFORM_DIR_FORWARD);
+
+ if(!IsOpVecNoOp(tmpOps))
+ {
+ ConstColorSpaceRcPtr targetColorSpace = config.getColorSpace(ROLE_COLOR_TIMING);
+
+ if(!skipColorSpaceConversions)
+ {
+ BuildColorSpaceOps(ops, config, context,
+ currentColorSpace,
+ targetColorSpace);
+ currentColorSpace = targetColorSpace;
+ }
+
+ std::copy(tmpOps.begin(), tmpOps.end(), std::back_inserter(ops));
+ }
+ }
+
+ // Apply a look, if specified
+ LookParseResult looks;
+ if(displayTransform.getLooksOverrideEnabled())
+ {
+ looks.parse(displayTransform.getLooksOverride());
+ }
+ else if(!skipColorSpaceConversions)
+ {
+ looks.parse(config.getDisplayLooks(display.c_str(), view.c_str()));
+ }
+
+ if(!looks.empty())
+ {
+ BuildLookOps(ops,
+ currentColorSpace,
+ skipColorSpaceConversions,
+ config,
+ context,
+ looks);
+ }
+
+ // Apply a channel view
+ ConstTransformRcPtr channelView = displayTransform.getChannelView();
+ if(channelView)
+ {
+ BuildOps(ops, config, context, channelView, TRANSFORM_DIR_FORWARD);
+ }
+
+
+ // Apply the conversion to the display color space
+ if(!skipColorSpaceConversions)
+ {
+ BuildColorSpaceOps(ops, config, context,
+ currentColorSpace,
+ displayColorspace);
+ currentColorSpace = displayColorspace;
+ }
+
+
+ // Apply a display cc
+ ConstTransformRcPtr displayCC = displayTransform.getDisplayCC();
+ if(displayCC)
+ {
+ BuildOps(ops, config, context, displayCC, TRANSFORM_DIR_FORWARD);
+ }
+
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/Exception.cpp b/src/core/Exception.cpp
new file mode 100644
index 0000000..b3d514c
--- /dev/null
+++ b/src/core/Exception.cpp
@@ -0,0 +1,74 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+OCIO_NAMESPACE_ENTER
+{
+
+ Exception::Exception(const char * msg) throw()
+ : std::exception(),
+ msg_(msg)
+ {}
+
+ Exception::Exception(const Exception& e) throw()
+ : std::exception(),
+ msg_(e.msg_)
+ {}
+
+ //*** operator=
+ Exception& Exception::operator=(const Exception& e) throw()
+ {
+ msg_ = e.msg_;
+ return *this;
+ }
+
+ //*** ~Exception
+ Exception::~Exception() throw()
+ {
+ }
+
+ //*** what
+ const char* Exception::what() const throw()
+ {
+ return msg_.c_str();
+ }
+
+
+
+
+ ExceptionMissingFile::ExceptionMissingFile(const char * msg) throw()
+ : Exception(msg)
+ {}
+
+ ExceptionMissingFile::ExceptionMissingFile(const ExceptionMissingFile& e) throw()
+ : Exception(e)
+ {}
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/ExponentOps.cpp b/src/core/ExponentOps.cpp
new file mode 100644
index 0000000..56e34d4
--- /dev/null
+++ b/src/core/ExponentOps.cpp
@@ -0,0 +1,372 @@
+/*
+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 <cmath>
+#include <cstring>
+#include <sstream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "ExponentOps.h"
+#include "GpuShaderUtils.h"
+#include "MathUtils.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ void ApplyClampExponent(float* rgbaBuffer, long numPixels,
+ const float* exp4)
+ {
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ rgbaBuffer[0] = powf( std::max(0.0f, rgbaBuffer[0]), exp4[0]);
+ rgbaBuffer[1] = powf( std::max(0.0f, rgbaBuffer[1]), exp4[1]);
+ rgbaBuffer[2] = powf( std::max(0.0f, rgbaBuffer[2]), exp4[2]);
+ rgbaBuffer[3] = powf( std::max(0.0f, rgbaBuffer[3]), exp4[3]);
+
+ rgbaBuffer += 4;
+ }
+ }
+
+ const int FLOAT_DECIMALS = 7;
+ }
+
+
+ namespace
+ {
+ class ExponentOp : public Op
+ {
+ public:
+ ExponentOp(const float * exp4,
+ TransformDirection direction);
+ virtual ~ExponentOp();
+
+ virtual OpRcPtr clone() const;
+
+ virtual std::string getInfo() const;
+ virtual std::string getCacheID() const;
+
+ virtual bool isNoOp() const;
+ virtual bool isSameType(const OpRcPtr & op) const;
+ virtual bool isInverse(const OpRcPtr & op) const;
+
+ virtual bool canCombineWith(const OpRcPtr & op) const;
+ virtual void combineWith(OpRcPtrVec & ops, const OpRcPtr & secondOp) const;
+
+ virtual bool hasChannelCrosstalk() const;
+ virtual void finalize();
+ virtual void apply(float* rgbaBuffer, long numPixels) const;
+
+ virtual bool supportsGpuShader() const;
+ virtual void writeGpuShader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc) const;
+ private:
+ float m_exp4[4];
+
+ // Set in finalize
+ std::string m_cacheID;
+ };
+
+ typedef OCIO_SHARED_PTR<ExponentOp> ExponentOpRcPtr;
+
+
+ ExponentOp::ExponentOp(const float * exp4,
+ TransformDirection direction):
+ Op()
+ {
+ if(direction == TRANSFORM_DIR_UNKNOWN)
+ {
+ throw Exception("Cannot create ExponentOp with unspecified transform direction.");
+ }
+
+ if(direction == TRANSFORM_DIR_INVERSE)
+ {
+ for(int i=0; i<4; ++i)
+ {
+ if(!IsScalarEqualToZero(exp4[i]))
+ {
+ m_exp4[i] = 1.0f / exp4[i];
+ }
+ else
+ {
+ throw Exception("Cannot apply ExponentOp op, Cannot apply 0.0 exponent in the inverse.");
+ }
+ }
+ }
+ else
+ {
+ memcpy(m_exp4, exp4, 4*sizeof(float));
+ }
+ }
+
+ OpRcPtr ExponentOp::clone() const
+ {
+ OpRcPtr op = OpRcPtr(new ExponentOp(m_exp4, TRANSFORM_DIR_FORWARD));
+ return op;
+ }
+
+ ExponentOp::~ExponentOp()
+ { }
+
+ std::string ExponentOp::getInfo() const
+ {
+ return "<ExponentOp>";
+ }
+
+ std::string ExponentOp::getCacheID() const
+ {
+ return m_cacheID;
+ }
+
+ bool ExponentOp::isNoOp() const
+ {
+ return IsVecEqualToOne(m_exp4, 4);
+ }
+
+ bool ExponentOp::isSameType(const OpRcPtr & op) const
+ {
+ ExponentOpRcPtr typedRcPtr = DynamicPtrCast<ExponentOp>(op);
+ if(!typedRcPtr) return false;
+ return true;
+ }
+
+ bool ExponentOp::isInverse(const OpRcPtr & op) const
+ {
+ ExponentOpRcPtr typedRcPtr = DynamicPtrCast<ExponentOp>(op);
+ if(!typedRcPtr) return false;
+
+ float combined[4] = { m_exp4[0]*typedRcPtr->m_exp4[0],
+ m_exp4[1]*typedRcPtr->m_exp4[1],
+ m_exp4[2]*typedRcPtr->m_exp4[2],
+ m_exp4[3]*typedRcPtr->m_exp4[3] };
+
+ return IsVecEqualToOne(combined, 4);
+ }
+
+ bool ExponentOp::canCombineWith(const OpRcPtr & op) const
+ {
+ return isSameType(op);
+ }
+
+
+ void ExponentOp::combineWith(OpRcPtrVec & ops, const OpRcPtr & secondOp) const
+ {
+ ExponentOpRcPtr typedRcPtr = DynamicPtrCast<ExponentOp>(secondOp);
+ if(!typedRcPtr)
+ {
+ std::ostringstream os;
+ os << "ExponentOp can only be combined with other ";
+ os << "ExponentOps. secondOp:" << secondOp->getInfo();
+ throw Exception(os.str().c_str());
+ }
+
+ float combined[4] = { m_exp4[0]*typedRcPtr->m_exp4[0],
+ m_exp4[1]*typedRcPtr->m_exp4[1],
+ m_exp4[2]*typedRcPtr->m_exp4[2],
+ m_exp4[3]*typedRcPtr->m_exp4[3] };
+
+ CreateExponentOp(ops, combined, TRANSFORM_DIR_FORWARD);
+ }
+
+ bool ExponentOp::hasChannelCrosstalk() const
+ {
+ return false;
+ }
+
+ void ExponentOp::finalize()
+ {
+ // Create the cacheID
+ std::ostringstream cacheIDStream;
+ cacheIDStream << "<ExponentOp ";
+ cacheIDStream.precision(FLOAT_DECIMALS);
+ for(int i=0; i<4; ++i)
+ {
+ cacheIDStream << m_exp4[i] << " ";
+ }
+ cacheIDStream << ">";
+ m_cacheID = cacheIDStream.str();
+ }
+
+ void ExponentOp::apply(float* rgbaBuffer, long numPixels) const
+ {
+ if(!rgbaBuffer) return;
+
+ ApplyClampExponent(rgbaBuffer, numPixels, m_exp4);
+ }
+
+ bool ExponentOp::supportsGpuShader() const
+ {
+ return true;
+ }
+
+ void ExponentOp::writeGpuShader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc) const
+ {
+ GpuLanguage lang = shaderDesc.getLanguage();
+ float zerovec[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+
+ shader << pixelName << " = pow(";
+ shader << "max(" << pixelName << ", " << GpuTextHalf4(zerovec, lang) << ")";
+ shader << ", " << GpuTextHalf4(m_exp4, lang) << ");\n";
+ }
+
+ } // Anon namespace
+
+
+
+ void CreateExponentOp(OpRcPtrVec & ops,
+ const float * exp4,
+ TransformDirection direction)
+ {
+ bool expIsIdentity = IsVecEqualToOne(exp4, 4);
+ if(expIsIdentity) return;
+
+ ops.push_back( ExponentOpRcPtr(new ExponentOp(exp4, direction)) );
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+#include "UnitTest.h"
+
+OCIO_NAMESPACE_USING
+
+OIIO_ADD_TEST(ExponentOps, Value)
+{
+ float exp1[4] = { 1.2f, 1.3f, 1.4f, 1.5f };
+
+ OpRcPtrVec ops;
+ CreateExponentOp(ops, exp1, TRANSFORM_DIR_FORWARD);
+ CreateExponentOp(ops, exp1, TRANSFORM_DIR_INVERSE);
+ OIIO_CHECK_EQUAL(ops.size(), 2);
+
+ for(unsigned int i=0; i<ops.size(); ++i)
+ {
+ ops[i]->finalize();
+ }
+
+ float error = 1e-6f;
+
+ const float source[] = { 0.5f, 0.5f, 0.5f, 0.5f, };
+
+ const float result1[] = { 0.43527528164806206f, 0.40612619817811774f,
+ 0.37892914162759955f, 0.35355339059327379f };
+
+ float tmp[4];
+ memcpy(tmp, source, 4*sizeof(float));
+ ops[0]->apply(tmp, 1);
+
+ for(unsigned int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(tmp[i], result1[i], error);
+ }
+
+ ops[1]->apply(tmp, 1);
+ for(unsigned int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(tmp[i], source[i], error);
+ }
+}
+
+OIIO_ADD_TEST(ExponentOps, Inverse)
+{
+ float exp1[4] = { 2.0f, 1.02345f, 5.651321f, 0.12345678910f };
+ float exp2[4] = { 2.0f, 2.0f, 2.0f, 2.0f };
+
+ OpRcPtrVec ops;
+
+ CreateExponentOp(ops, exp1, TRANSFORM_DIR_FORWARD);
+ CreateExponentOp(ops, exp1, TRANSFORM_DIR_INVERSE);
+ CreateExponentOp(ops, exp2, TRANSFORM_DIR_FORWARD);
+ CreateExponentOp(ops, exp2, TRANSFORM_DIR_INVERSE);
+
+ OIIO_CHECK_EQUAL(ops.size(), 4);
+
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[1]));
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[2]));
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[3]->clone()));
+
+ OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[0]), false);
+ OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[1]), true);
+ OIIO_CHECK_EQUAL(ops[1]->isInverse(ops[0]), true);
+ OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[2]), false);
+ OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[3]), false);
+ OIIO_CHECK_EQUAL(ops[3]->isInverse(ops[0]), false);
+ OIIO_CHECK_EQUAL(ops[2]->isInverse(ops[3]), true);
+ OIIO_CHECK_EQUAL(ops[3]->isInverse(ops[2]), true);
+ OIIO_CHECK_EQUAL(ops[3]->isInverse(ops[3]), false);
+}
+
+OIIO_ADD_TEST(ExponentOps, Combining)
+{
+ float exp1[4] = { 2.0f, 2.0f, 2.0f, 1.0f };
+ float exp2[4] = { 1.2f, 1.2f, 1.2f, 1.0f };
+
+ OpRcPtrVec ops;
+ CreateExponentOp(ops, exp1, TRANSFORM_DIR_FORWARD);
+ CreateExponentOp(ops, exp2, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(ops.size(), 2);
+
+ float error = 1e-6f;
+ const float source[] = { 0.5f, 0.5f, 0.5f, 0.5f, };
+ const float result[] = { 0.18946457081379978f, 0.18946457081379978f,
+ 0.18946457081379978f, 0.5f };
+
+ float tmp[4];
+ memcpy(tmp, source, 4*sizeof(float));
+ ops[0]->apply(tmp, 1);
+ ops[1]->apply(tmp, 1);
+
+ for(unsigned int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(tmp[i], result[i], error);
+ }
+
+
+ OpRcPtrVec combined;
+ ops[0]->combineWith(combined, ops[1]);
+ OIIO_CHECK_EQUAL(combined.size(), 1);
+
+ float tmp2[4];
+ memcpy(tmp2, source, 4*sizeof(float));
+ combined[0]->apply(tmp2, 1);
+
+ for(unsigned int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(tmp2[i], result[i], error);
+ }
+}
+
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/ExponentOps.h b/src/core/ExponentOps.h
new file mode 100644
index 0000000..95dd1f6
--- /dev/null
+++ b/src/core/ExponentOps.h
@@ -0,0 +1,50 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_EXPONENTOP_H
+#define INCLUDED_OCIO_EXPONENTOP_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Op.h"
+
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+ // If the exponent is 1.0, this will return without clamping
+ // Otherwise, will be clamped between [0.0, inf]
+
+ void CreateExponentOp(OpRcPtrVec & ops,
+ const float * exponent4,
+ TransformDirection direction);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/ExponentTransform.cpp b/src/core/ExponentTransform.cpp
new file mode 100644
index 0000000..7bc180f
--- /dev/null
+++ b/src/core/ExponentTransform.cpp
@@ -0,0 +1,151 @@
+/*
+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 <cstring>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "ExponentOps.h"
+#include "OpBuilders.h"
+
+
+OCIO_NAMESPACE_ENTER
+{
+ ExponentTransformRcPtr ExponentTransform::Create()
+ {
+ return ExponentTransformRcPtr(new ExponentTransform(), &deleter);
+ }
+
+ void ExponentTransform::deleter(ExponentTransform* t)
+ {
+ delete t;
+ }
+
+
+ class ExponentTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+ float value_[4];
+
+ Impl() :
+ dir_(TRANSFORM_DIR_FORWARD)
+ {
+ for(int i=0; i<4; ++i)
+ {
+ value_[i] = 1.0f;
+ }
+ }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+ memcpy(value_, rhs.value_, 4*sizeof(float));
+ return *this;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ ExponentTransform::ExponentTransform()
+ : m_impl(new ExponentTransform::Impl)
+ {
+ }
+
+ TransformRcPtr ExponentTransform::createEditableCopy() const
+ {
+ ExponentTransformRcPtr transform = ExponentTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ ExponentTransform::~ExponentTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ ExponentTransform& ExponentTransform::operator= (const ExponentTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection ExponentTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void ExponentTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+ void ExponentTransform::setValue(const float * vec4)
+ {
+ if(vec4) memcpy(getImpl()->value_, vec4, 4*sizeof(float));
+ }
+
+ void ExponentTransform::getValue(float * vec4) const
+ {
+ if(vec4) memcpy(vec4, getImpl()->value_, 4*sizeof(float));
+ }
+
+ std::ostream& operator<< (std::ostream& os, const ExponentTransform& t)
+ {
+ os << "<ExponentTransform ";
+ os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
+ os << ">\n";
+ return os;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ void BuildExponentOps(OpRcPtrVec & ops,
+ const Config& /*config*/,
+ const ExponentTransform & transform,
+ TransformDirection dir)
+ {
+ TransformDirection combinedDir = CombineTransformDirections(dir,
+ transform.getDirection());
+
+ float vec4[4];
+ transform.getValue(vec4);
+
+ CreateExponentOp(ops,
+ vec4,
+ combinedDir);
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/FileFormat3DL.cpp b/src/core/FileFormat3DL.cpp
new file mode 100644
index 0000000..9d7e131
--- /dev/null
+++ b/src/core/FileFormat3DL.cpp
@@ -0,0 +1,638 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Lut1DOp.h"
+#include "Lut3DOp.h"
+#include "MathUtils.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstdio>
+#include <sstream>
+
+/*
+// Discreet's Flame Lut Format
+// Use a loose interpretation of the format to allow other 3d luts that look
+// similar, but dont strictly adhere to the real definition.
+
+// If line starts with text or # skip it
+// If line is a bunch of ints (more than 3) , it's the 1d shaper lut
+
+// All remaining lines of size 3 int are data
+// cube size is determined from num entries
+// The bit depth of the shaper lut and the 3d lut need not be the same.
+
+Example 1, FLAME
+# Comment here
+0 64 128 192 256 320 384 448 512 576 640 704 768 832 896 960 1023
+
+0 0 0
+0 0 100
+0 0 200
+
+
+Example 2, LUSTRE
+#Tokens required by applications - do not edit
+3DMESH
+Mesh 4 12
+0 64 128 192 256 320 384 448 512 576 640 704 768 832 896 960 1023
+
+
+
+0 17 17
+0 0 88
+0 0 157
+9 101 197
+0 118 308
+...
+
+4092 4094 4094
+
+#Tokens required by applications - do not edit
+
+LUT8
+gamma 1.0
+
+In this example, the 3D LUT has an input bit depth of 4 bits and an output
+bit depth of 12 bits. You use the input value to calculate the RGB triplet
+to be 17*17*17 (where 17=(2 to the power of 4)+1, and 4 is the input bit
+depth). The first triplet is the output value at (0,0,0);(0,0,1);...;
+(0,0,16) r,g,b coordinates; the second triplet is the output value at
+(0,1,0);(0,1,1);...;(0,1,16) r,g,b coordinates; and so on. You use the output
+bit depth to set the output bit depth range (12 bits or 0-4095).
+NoteLustre supports an input and output depth of 16 bits for 3D LUTs; however,
+in the processing pipeline, the BLACK_LEVEL to WHITE_LEVEL range is only 14
+bits. This means that even if the 3D LUT is 16 bits, it is normalized to
+fit the BLACK_LEVEL to WHITE_LEVEL range of Lustre.
+In Lustre, 3D LUT files can contain grids of 17 cubed, 33 cubed, and 65 cubed;
+however, Lustre converts 17 cubed and 65 cubed grids to 33 cubed for internal
+processing on the output (for rendering and calibration), but not on the input
+3D LUT.
+*/
+
+
+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 Write(const Baker & baker,
+ const std::string & formatName,
+ std::ostream & ostream) const;
+
+ virtual void BuildFileOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ CachedFileRcPtr untypedCachedFile,
+ const FileTransform& fileTransform,
+ TransformDirection dir) const;
+ };
+
+
+
+
+
+
+ // We use the maximum value found in the lut to infer
+ // the bit depth. While this is fugly. We dont believe
+ // there is a better way, looking at the file, to
+ // determine this.
+ //
+ // Note: We allow for 2x overshoot in the luts.
+ // As we dont allow for odd bit depths, this isnt a big deal.
+ // So sizes from 1/2 max - 2x max are valid
+ //
+ // FILE EXPECTED MAX CORRECTLY DECODED IF MAX IN THIS RANGE
+ // 8-bit 255 [0, 511]
+ // 10-bit 1023 [512, 2047]
+ // 12-bit 4095 [2048, 8191]
+ // 14-bit 16383 [8192, 32767]
+ // 16-bit 65535 [32768, 131071+]
+
+ int GetLikelyLutBitDepth(int testval)
+ {
+ const int MIN_BIT_DEPTH = 8;
+ const int MAX_BIT_DEPTH = 16;
+
+ if(testval < 0) return -1;
+
+ // Only test even bit depths
+ for(int bitDepth = MIN_BIT_DEPTH;
+ bitDepth <= MAX_BIT_DEPTH; bitDepth+=2)
+ {
+ int maxcode = static_cast<int>(pow(2.0,bitDepth));
+ int adjustedMax = maxcode * 2 - 1;
+ if(testval<=adjustedMax) return bitDepth;
+ }
+
+ return MAX_BIT_DEPTH;
+ }
+
+ int GetMaxValueFromIntegerBitDepth(int bitDepth)
+ {
+ return static_cast<int>( pow(2.0, bitDepth) ) - 1;
+ }
+
+ int GetClampedIntFromNormFloat(float val, float scale)
+ {
+ val = std::min(std::max(0.0f, val), 1.0f) * scale;
+ return static_cast<int>(roundf(val));
+ }
+
+ void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const
+ {
+ FormatInfo info;
+ info.name = "flame";
+ info.extension = "3dl";
+ info.capabilities = (FORMAT_CAPABILITY_READ | FORMAT_CAPABILITY_WRITE);
+ formatInfoVec.push_back(info);
+
+ FormatInfo info2 = info;
+ info2.name = "lustre";
+ formatInfoVec.push_back(info2);
+ }
+
+ // Try and load the format
+ // Raise an exception if it can't be loaded.
+
+ CachedFileRcPtr LocalFileFormat::Read(std::istream & istream) const
+ {
+ std::vector<int> rawshaper;
+ std::vector<int> raw3d;
+
+ // Parse the file 3d lut data to an int array
+ {
+ const int MAX_LINE_SIZE = 4096;
+ char lineBuffer[MAX_LINE_SIZE];
+
+ std::vector<std::string> lineParts;
+ std::vector<int> tmpData;
+
+ while(istream.good())
+ {
+ istream.getline(lineBuffer, MAX_LINE_SIZE);
+
+ // Strip and split the line
+ pystring::split(pystring::strip(lineBuffer), lineParts);
+
+ if(lineParts.empty()) continue;
+ if((lineParts.size() > 0) && pystring::startswith(lineParts[0],"#")) continue;
+
+ // If we havent found a list of ints, continue
+ if(!StringVecToIntVec(tmpData, lineParts)) continue;
+
+ // If we've found more than 3 ints, and dont have
+ // a shaper lut yet, we've got it!
+ if(tmpData.size()>3 && rawshaper.empty())
+ {
+ for(unsigned int i=0; i<tmpData.size(); ++i)
+ {
+ rawshaper.push_back(tmpData[i]);
+ }
+ }
+
+ // If we've found 3 ints, add it to our 3dlut.
+ if(tmpData.size() == 3)
+ {
+ raw3d.push_back(tmpData[0]);
+ raw3d.push_back(tmpData[1]);
+ raw3d.push_back(tmpData[2]);
+ }
+ }
+ }
+
+ if(raw3d.empty() && rawshaper.empty())
+ {
+ std::ostringstream os;
+ os << "Error parsing .3dl file.";
+ os << "Does not appear to contain a valid shaper lut or a 3D lut.";
+ throw Exception(os.str().c_str());
+ }
+
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+
+ // If all we're doing to parse the format is to read in sets of 3 numbers,
+ // it's possible that other formats will accidentally be able to be read
+ // mistakenly as .3dl files. We can exclude a huge segement of these mis-reads
+ // by screening for files that use float represenations. I.e., if the MAX
+ // value of the lut is a small number (such as <128.0) it's likely not an integer
+ // format, and thus not a likely 3DL file.
+
+ const int FORMAT3DL_CODEVALUE_LOWEST_PLAUSIBLE_MAXINT = 128;
+
+ // Interpret the shaper lut
+ if(!rawshaper.empty())
+ {
+ cachedFile->has1D = true;
+
+ // Find the maximum shaper lut value to infer bit-depth
+ int shapermax = 0;
+ for(unsigned int i=0; i<rawshaper.size(); ++i)
+ {
+ shapermax = std::max(shapermax, rawshaper[i]);
+ }
+
+ if(shapermax<FORMAT3DL_CODEVALUE_LOWEST_PLAUSIBLE_MAXINT)
+ {
+ std::ostringstream os;
+ os << "Error parsing .3dl file.";
+ os << "The maximum shaper lut value, " << shapermax;
+ os << ", is unreasonably low. This lut is probably not a .3dl ";
+ os << "file, but instead a related format that shares a similar ";
+ os << "structure.";
+
+ throw Exception(os.str().c_str());
+ }
+
+ int shaperbitdepth = GetLikelyLutBitDepth(shapermax);
+ if(shaperbitdepth<0)
+ {
+ std::ostringstream os;
+ os << "Error parsing .3dl file.";
+ os << "The maximum shaper lut value, " << shapermax;
+ os << ", does not correspond to any likely bit depth. ";
+ os << "Please confirm source file is valid.";
+ throw Exception(os.str().c_str());
+ }
+
+ int bitdepthmax = GetMaxValueFromIntegerBitDepth(shaperbitdepth);
+ float scale = 1.0f / static_cast<float>(bitdepthmax);
+
+ for(int channel=0; channel<3; ++channel)
+ {
+ cachedFile->lut1D->luts[channel].resize(rawshaper.size());
+
+ for(unsigned int i=0; i<rawshaper.size(); ++i)
+ {
+ cachedFile->lut1D->luts[channel][i] = static_cast<float>(rawshaper[i])*scale;
+ }
+ }
+
+ // The error threshold will be 2 code values. This will allow
+ // shaper luts which use different int conversions (round vs. floor)
+ // to both be optimized.
+ // Required: Abs Tolerance
+
+ const int FORMAT3DL_SHAPER_CODEVALUE_TOLERANCE = 2;
+ cachedFile->lut1D->maxerror = FORMAT3DL_SHAPER_CODEVALUE_TOLERANCE*scale;
+ cachedFile->lut1D->errortype = ERROR_ABSOLUTE;
+ }
+
+
+
+ // Interpret the parsed data.
+ if(!raw3d.empty())
+ {
+ cachedFile->has3D = true;
+
+ // Find the maximum shaper lut value to infer bit-depth
+ int lut3dmax = 0;
+ for(unsigned int i=0; i<raw3d.size(); ++i)
+ {
+ lut3dmax = std::max(lut3dmax, raw3d[i]);
+ }
+
+ if(lut3dmax<FORMAT3DL_CODEVALUE_LOWEST_PLAUSIBLE_MAXINT)
+ {
+ std::ostringstream os;
+ os << "Error parsing .3dl file.";
+ os << "The maximum 3d lut value, " << lut3dmax;
+ os << ", is unreasonably low. This lut is probably not a .3dl ";
+ os << "file, but instead a related format that shares a similar ";
+ os << "structure.";
+
+ throw Exception(os.str().c_str());
+ }
+
+ int lut3dbitdepth = GetLikelyLutBitDepth(lut3dmax);
+ if(lut3dbitdepth<0)
+ {
+ std::ostringstream os;
+ os << "Error parsing .3dl file.";
+ os << "The maximum 3d lut value, " << lut3dmax;
+ os << ", does not correspond to any likely bit depth. ";
+ os << "Please confirm source file is valid.";
+ throw Exception(os.str().c_str());
+ }
+
+ int bitdepthmax = GetMaxValueFromIntegerBitDepth(lut3dbitdepth);
+ float scale = 1.0f / static_cast<float>(bitdepthmax);
+
+ // Interpret the int array as a 3dlut
+ int lutEdgeLen = Get3DLutEdgeLenFromNumPixels((int)raw3d.size()/3);
+
+ // Reformat 3D data
+ cachedFile->lut3D->size[0] = lutEdgeLen;
+ cachedFile->lut3D->size[1] = lutEdgeLen;
+ cachedFile->lut3D->size[2] = lutEdgeLen;
+ cachedFile->lut3D->lut.reserve(lutEdgeLen * lutEdgeLen * lutEdgeLen * 3);
+
+ for(int rIndex=0; rIndex<lutEdgeLen; ++rIndex)
+ {
+ for(int gIndex=0; gIndex<lutEdgeLen; ++gIndex)
+ {
+ for(int bIndex=0; bIndex<lutEdgeLen; ++bIndex)
+ {
+ int i = GetLut3DIndex_B(rIndex, gIndex, bIndex,
+ lutEdgeLen, lutEdgeLen, lutEdgeLen);
+
+ cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+0]) * scale);
+ cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+1]) * scale);
+ cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+2]) * scale);
+ }
+ }
+ }
+ }
+
+ return cachedFile;
+ }
+
+ // 65 -> 6
+ // 33 -> 5
+ // 17 -> 4
+
+ int CubeDimensionLenToLustreBitDepth(int size)
+ {
+ float logval = logf(static_cast<float>(size-1)) / logf(2.0);
+ return static_cast<int>(logval);
+ }
+
+ void LocalFileFormat::Write(const Baker & baker,
+ const std::string & formatName,
+ std::ostream & ostream) const
+ {
+ int DEFAULT_CUBE_SIZE = 0;
+ int SHAPER_BIT_DEPTH = 10;
+ int CUBE_BIT_DEPTH = 12;
+
+ if(formatName == "lustre")
+ {
+ DEFAULT_CUBE_SIZE = 33;
+ }
+ else if(formatName == "flame")
+ {
+ DEFAULT_CUBE_SIZE = 17;
+ }
+ else
+ {
+ std::ostringstream os;
+ os << "Unknown 3dl format name, '";
+ os << formatName << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ ConstConfigRcPtr config = baker.getConfig();
+
+ int cubeSize = baker.getCubeSize();
+ if(cubeSize==-1) cubeSize = DEFAULT_CUBE_SIZE;
+ cubeSize = std::max(2, cubeSize); // smallest cube is 2x2x2
+
+ int shaperSize = baker.getShaperSize();
+ if(shaperSize==-1) shaperSize = cubeSize;
+
+ std::vector<float> cubeData;
+ cubeData.resize(cubeSize*cubeSize*cubeSize*3);
+ GenerateIdentityLut3D(&cubeData[0], cubeSize, 3, LUT3DORDER_FAST_BLUE);
+ PackedImageDesc cubeImg(&cubeData[0], cubeSize*cubeSize*cubeSize, 1, 3);
+
+ // Apply our conversion from the input space to the output space.
+ ConstProcessorRcPtr inputToTarget;
+ std::string looks = baker.getLooks();
+ if (!looks.empty())
+ {
+ LookTransformRcPtr transform = LookTransform::Create();
+ transform->setLooks(looks.c_str());
+ transform->setSrc(baker.getInputSpace());
+ transform->setDst(baker.getTargetSpace());
+ inputToTarget = config->getProcessor(transform,
+ TRANSFORM_DIR_FORWARD);
+ }
+ else
+ {
+ inputToTarget = config->getProcessor(baker.getInputSpace(),
+ baker.getTargetSpace());
+ }
+ inputToTarget->apply(cubeImg);
+
+ // Write out the file.
+ // For for maximum compatibility with other apps, we will
+ // not utilize the shaper or output any metadata
+
+ if(formatName == "lustre")
+ {
+ int meshInputBitDepth = CubeDimensionLenToLustreBitDepth(cubeSize);
+ ostream << "3DMESH\n";
+ ostream << "Mesh " << meshInputBitDepth << " " << CUBE_BIT_DEPTH << "\n";
+ }
+
+ std::vector<float> shaperData(shaperSize);
+ GenerateIdentityLut1D(&shaperData[0], shaperSize, 1);
+
+ float shaperScale = static_cast<float>(
+ GetMaxValueFromIntegerBitDepth(SHAPER_BIT_DEPTH));
+
+ for(unsigned int i=0; i<shaperData.size(); ++i)
+ {
+ if(i != 0) ostream << " ";
+ int val = GetClampedIntFromNormFloat(shaperData[i], shaperScale);
+ ostream << val;
+ }
+ ostream << "\n";
+
+ // Write out the 3D Cube
+ float cubeScale = static_cast<float>(
+ GetMaxValueFromIntegerBitDepth(CUBE_BIT_DEPTH));
+ if(cubeSize < 2)
+ {
+ throw Exception("Internal cube size exception.");
+ }
+ for(int i=0; i<cubeSize*cubeSize*cubeSize; ++i)
+ {
+ int r = GetClampedIntFromNormFloat(cubeData[3*i+0], cubeScale);
+ int g = GetClampedIntFromNormFloat(cubeData[3*i+1], cubeScale);
+ int b = GetClampedIntFromNormFloat(cubeData[3*i+2], cubeScale);
+ ostream << r << " " << g << " " << b << "\n";
+ }
+ ostream << "\n";
+
+ if(formatName == "lustre")
+ {
+ ostream << "LUT8\n";
+ ostream << "gamma 1.0\n";
+ }
+ }
+
+ 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 .3dl 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 * CreateFileFormat3DL()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+// FILE EXPECTED MAX CORRECTLY DECODED IF MAX IN THIS RANGE
+// 8-bit 255 [0, 511]
+// 10-bit 1023 [512, 2047]
+// 12-bit 4095 [2048, 8191]
+// 14-bit 16383 [8192, 32767]
+// 16-bit 65535 [32768, 131071]
+
+OIIO_ADD_TEST(FileFormat3DL, GetLikelyLutBitDepth)
+{
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(-1), -1);
+
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(0), 8);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(1), 8);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(255), 8);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(256), 8);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(511), 8);
+
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(512), 10);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(1023), 10);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(1024), 10);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(2047), 10);
+
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(2048), 12);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(4095), 12);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(4096), 12);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(8191), 12);
+
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(16383), 14);
+
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(65535), 16);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(65536), 16);
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(131071), 16);
+
+ OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(131072), 16);
+}
+
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/FileFormatCC.cpp b/src/core/FileFormatCC.cpp
new file mode 100644
index 0000000..ade09a6
--- /dev/null
+++ b/src/core/FileFormatCC.cpp
@@ -0,0 +1,151 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "OpBuilders.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ ////////////////////////////////////////////////////////////////
+
+ namespace
+ {
+ class LocalCachedFile : public CachedFile
+ {
+ public:
+ LocalCachedFile ()
+ {
+ transform = CDLTransform::Create();
+ };
+
+ ~LocalCachedFile() {};
+
+ CDLTransformRcPtr transform;
+ };
+
+ 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 = "ColorCorrection";
+ info.extension = "cc";
+ info.capabilities = FORMAT_CAPABILITY_READ;
+ formatInfoVec.push_back(info);
+ }
+
+ // Try and load the format
+ // Raise an exception if it can't be loaded.
+
+ CachedFileRcPtr LocalFileFormat::Read(std::istream & istream) const
+ {
+ std::ostringstream rawdata;
+ rawdata << istream.rdbuf();
+
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+
+ try
+ {
+ cachedFile->transform->setXML(rawdata.str().c_str());
+ }
+ catch(Exception & e)
+ {
+ std::ostringstream os;
+ os << "Error parsing .cc file. ";
+ os << "Does not appear to contain a valid ASC CDL XML:";
+ os << e.what();
+ 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 .cc 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());
+ }
+
+ BuildCDLOps(ops,
+ config,
+ *cachedFile->transform,
+ newDir);
+ }
+ }
+
+ FileFormat * CreateFileFormatCC()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
+
diff --git a/src/core/FileFormatCCC.cpp b/src/core/FileFormatCCC.cpp
new file mode 100644
index 0000000..ec43803
--- /dev/null
+++ b/src/core/FileFormatCCC.cpp
@@ -0,0 +1,195 @@
+/*
+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 <map>
+#include <tinyxml.h>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "CDLTransform.h"
+#include "FileTransform.h"
+#include "OpBuilders.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ ////////////////////////////////////////////////////////////////
+
+ namespace
+ {
+ typedef std::map<std::string,CDLTransformRcPtr> CDLMap;
+
+ class LocalCachedFile : public CachedFile
+ {
+ public:
+ LocalCachedFile () {};
+
+ ~LocalCachedFile() {};
+
+ CDLMap transforms;
+ };
+
+ typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr;
+ typedef OCIO_SHARED_PTR<TiXmlDocument> TiXmlDocumentRcPtr;
+
+
+
+ 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 = "ColorCorrectionCollection";
+ info.extension = "ccc";
+ info.capabilities = FORMAT_CAPABILITY_READ;
+ formatInfoVec.push_back(info);
+ }
+
+ // Try and load the format
+ // Raise an exception if it can't be loaded.
+
+ CachedFileRcPtr LocalFileFormat::Read(std::istream & istream) const
+ {
+ std::ostringstream rawdata;
+ rawdata << istream.rdbuf();
+
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+
+ TiXmlDocumentRcPtr doc = TiXmlDocumentRcPtr(new TiXmlDocument());
+ doc->Parse(rawdata.str().c_str());
+
+ if(doc->Error())
+ {
+ std::ostringstream os;
+ os << "XML Parse Error. ";
+ os << doc->ErrorDesc() << " (line ";
+ os << doc->ErrorRow() << ", character ";
+ os << doc->ErrorCol() << ")";
+ throw Exception(os.str().c_str());
+ }
+
+ TiXmlElement* rootElement = doc->RootElement();
+ if(!rootElement)
+ {
+ std::ostringstream os;
+ os << "Error loading xml. Null root element.";
+ throw Exception(os.str().c_str());
+ }
+
+ if(std::string(rootElement->Value()) != "ColorCorrectionCollection")
+ {
+ std::ostringstream os;
+ os << "Error loading ccc xml. ";
+ os << "Root element is type '" << rootElement->Value() << "', ";
+ os << "ColorCorrectionCollection expected.";
+ throw Exception(os.str().c_str());
+ }
+
+ GetCDLTransforms(cachedFile->transforms, rootElement);
+
+ if(cachedFile->transforms.empty())
+ {
+ std::ostringstream os;
+ os << "Error loading ccc xml. ";
+ os << "No ColorCorrection elements found.";
+ 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 .ccc 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 ASC FileTransform,";
+ os << " unspecified transform direction.";
+ throw Exception(os.str().c_str());
+ }
+
+ std::string cccid = fileTransform.getCCCId();
+ cccid = context->resolveStringVar(cccid.c_str());
+
+ CDLMap::const_iterator iter = cachedFile->transforms.find(cccid);
+ if(iter == cachedFile->transforms.end())
+ {
+ std::ostringstream os;
+ os << "Cannot build ASC FileTransform, specified cccid '";
+ os << cccid << "' not found in " << fileTransform.getSrc();
+ throw Exception(os.str().c_str());
+ }
+
+ BuildCDLOps(ops,
+ config,
+ *(iter->second),
+ newDir);
+ }
+ }
+
+ FileFormat * CreateFileFormatCCC()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
+
diff --git a/src/core/FileFormatCSP.cpp b/src/core/FileFormatCSP.cpp
new file mode 100644
index 0000000..eae4d9d
--- /dev/null
+++ b/src/core/FileFormatCSP.cpp
@@ -0,0 +1,1112 @@
+/*
+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 <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Lut1DOp.h"
+#include "Lut3DOp.h"
+#include "MathUtils.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ const int NUM_PRELUT_SAMPLES = 65536; // 2**16 samples
+ // Always use linear interpolation for preluts to get the
+ // best precision
+ const Interpolation PRELUT_INTERPOLATION = INTERP_LINEAR;
+
+ typedef struct rsr_Interpolator1D_Raw_
+ {
+ float * stims;
+ float * values;
+ unsigned int length;
+ } rsr_Interpolator1D_Raw;
+
+ rsr_Interpolator1D_Raw * rsr_Interpolator1D_Raw_create( unsigned int prelutLength );
+ void rsr_Interpolator1D_Raw_destroy( rsr_Interpolator1D_Raw * prelut );
+
+
+ /* An opaque handle to the cineSpace 1D Interpolator object */
+ typedef struct rsr_Interpolator1D_ rsr_Interpolator1D;
+
+ rsr_Interpolator1D * rsr_Interpolator1D_createFromRaw( rsr_Interpolator1D_Raw * data );
+ void rsr_Interpolator1D_destroy( rsr_Interpolator1D * rawdata );
+ float rsr_Interpolator1D_interpolate( float x, rsr_Interpolator1D * data );
+
+
+ /*
+ * =========== INTERNAL HELPER FUNCTIONS ============
+ */
+ struct rsr_Interpolator1D_
+ {
+ int nSamplePoints;
+ float * stims;
+
+ /* 5 * (nSamplePoints-1) long, holding a sequence of
+ * 1.0/delta, a, b, c, d
+ * such that the curve in interval i is given by
+ * z = (stims[i] - x)* (1.0/delta)
+ * y = a + b*z + c*z^2 + d*z^3
+ */
+ float * parameters;
+
+ float minValue; /* = f( stims[0] ) */
+ float maxValue; /* = f(stims[nSamplePoints-1] ) */
+ };
+
+ static int rsr_internal_I1D_refineSegment( float x, float * data, int low, int high )
+ {
+ int midPoint;
+
+ // TODO: Change assert to an exception?
+ assert( x>= data[low] );
+ assert( x<= data[high] );
+ assert( (high-low) > 0);
+ if( high-low==1 ) return low;
+
+ midPoint = (low+high)/2;
+ if( x<data[midPoint] ) return rsr_internal_I1D_refineSegment(x, data, low, midPoint );
+ return rsr_internal_I1D_refineSegment(x, data, midPoint, high );
+ }
+
+ static int rsr_internal_I1D_findSegmentContaining( float x, float * data, int n )
+ {
+ return rsr_internal_I1D_refineSegment(x, data, 0, n-1);
+ }
+
+ /*
+ * =========== USER FUNCTIONS ============
+ */
+
+
+ void rsr_Interpolator1D_destroy( rsr_Interpolator1D * data )
+ {
+ if(data==NULL) return;
+ free(data->stims);
+ free(data->parameters);
+ free(data);
+ }
+
+
+
+ float rsr_Interpolator1D_interpolate( float x, rsr_Interpolator1D * data )
+ {
+ int segId;
+ float * segdata;
+ float invDelta;
+ float a,b,c,d,z;
+
+ assert(data!=NULL);
+
+ /* Is x in range? */
+ if( isnan(x) ) return x;
+
+ if( x<data->stims[0] ) return data->minValue;
+ if (x>data->stims[ data->nSamplePoints -1] ) return data->maxValue;
+
+ /* Ok so its between the begining and end .. lets find out where... */
+ segId = rsr_internal_I1D_findSegmentContaining( x, data->stims, data->nSamplePoints );
+
+ assert(data->parameters !=NULL );
+
+ segdata = data->parameters + 5 * segId;
+
+ invDelta = segdata[0];
+ a = segdata[1];
+ b = segdata[2];
+ c = segdata[3];
+ d = segdata[4];
+
+ z = ( x - data->stims[segId] ) * invDelta;
+
+ return a + z * ( b + z * ( c + d * z ) ) ;
+
+ }
+
+ rsr_Interpolator1D * rsr_Interpolator1D_createFromRaw( rsr_Interpolator1D_Raw * data )
+ {
+ rsr_Interpolator1D * retval = NULL;
+ /* Check the sanity of the data */
+ assert(data!=NULL);
+ assert(data->length>=2);
+ assert(data->stims!=NULL);
+ assert(data->values!=NULL);
+
+ /* Create the real data. */
+ retval = (rsr_Interpolator1D*)malloc( sizeof(rsr_Interpolator1D) ); // OCIO change: explicit cast
+ if(retval==NULL)
+ {
+ return NULL;
+ }
+
+ retval->stims = (float*)malloc( sizeof(float) * data->length ); // OCIO change: explicit cast
+ if(retval->stims==NULL)
+ {
+ free(retval);
+ return NULL;
+ }
+ memcpy( retval->stims, data->stims, sizeof(float) * data->length );
+
+ retval->parameters = (float*)malloc( 5*sizeof(float) * ( data->length - 1 ) ); // OCIO change: explicit cast
+ if(retval->parameters==NULL)
+ {
+ free(retval->stims);
+ free(retval);
+ return NULL;
+ }
+ retval->nSamplePoints = data->length;
+ retval->minValue = data->values[0];
+ retval->maxValue = data->values[ data->length -1];
+
+ /* Now the fun part .. filling in the coeficients. */
+ if(data->length==2)
+ {
+ retval->parameters[0] = 1.0f/(data->stims[1]-data->stims[0]);
+ retval->parameters[1] = data->values[0];
+ retval->parameters[2] = ( data->values[1] - data->values[0] );
+ retval->parameters[3] = 0;
+ retval->parameters[4] = 0;
+ }
+ else
+ {
+ unsigned int i;
+ float * params = retval->parameters;
+ for(i=0; i< data->length-1; ++i)
+ {
+ float f0 = data->values[i+0];
+ float f1 = data->values[i+1];
+
+ params[0] = 1.0f/(retval->stims[i+1]-retval->stims[i+0]);
+
+ if(i==0)
+ {
+ float delta = data->stims[i+1] - data->stims[i];
+ float delta2 = (data->stims[i+2] - data->stims[i+1])/delta;
+ float f2 = data->values[i+2];
+
+ float dfdx1 = (f2-f0)/(1+delta2);
+ params[1] = 1.0f * f0 + 0.0f * f1 + 0.0f * dfdx1;
+ params[2] = -2.0f * f0 + 2.0f * f1 - 1.0f * dfdx1;
+ params[3] = 1.0f * f0 - 1.0f * f1 + 1.0f * dfdx1;
+ params[4] = 0.0;
+ }
+ else if (i==data->length-2)
+ {
+ float delta = data->stims[i+1] - data->stims[i];
+ float delta1 = (data->stims[i]-data->stims[i-1])/delta;
+ float fn1 = data->values[i-1];
+ float dfdx0 = (f1-fn1)/(1+delta1);
+ params[1] = 1.0f * f0 + 0.0f * f1 + 0.0f * dfdx0;
+ params[2] = 0.0f * f0 + 0.0f * f1 + 1.0f * dfdx0;
+ params[3] = -1.0f * f0 + 1.0f * f1 - 1.0f * dfdx0;
+ params[4] = 0.0;
+ }
+ else
+ {
+ float delta = data->stims[i+1] - data->stims[i];
+ float fn1=data->values[i-1];
+ float delta1 = (data->stims[i] - data->stims[i-1])/delta;
+
+ float f2=data->values[i+2];
+ float delta2 = (data->stims[i+2] - data->stims[i+1])/delta;
+
+ float dfdx0 = (f1-fn1)/(1.0f+delta1);
+ float dfdx1 = (f2-f0)/(1.0f+delta2);
+
+ params[1] = 1.0f * f0 + 0.0f * dfdx0 + 0.0f * f1 + 0.0f * dfdx1;
+ params[2] = 0.0f * f0 + 1.0f * dfdx0 + 0.0f * f1 + 0.0f * dfdx1;
+ params[3] =-3.0f * f0 - 2.0f * dfdx0 + 3.0f * f1 - 1.0f * dfdx1;
+ params[4] = 2.0f * f0 + 1.0f * dfdx0 - 2.0f * f1 + 1.0f * dfdx1;
+ }
+
+ params+=5;
+ }
+ }
+ return retval;
+ }
+
+ rsr_Interpolator1D_Raw * rsr_Interpolator1D_Raw_create( unsigned int prelutLength)
+ {
+ unsigned int i;
+ rsr_Interpolator1D_Raw * prelut = (rsr_Interpolator1D_Raw*)malloc( sizeof(rsr_Interpolator1D_Raw) ); // OCIO change: explicit cast
+ if(prelut==NULL) return NULL;
+
+ prelut->stims = (float*)malloc( sizeof(float) * prelutLength ); // OCIO change: explicit cast
+ if(prelut->stims==NULL)
+ {
+ free(prelut);
+ return NULL;
+ }
+
+ prelut->values = (float*)malloc( sizeof(float) * prelutLength ); // OCIO change: explicit cast
+ if(prelut->values == NULL)
+ {
+ free(prelut->stims);
+ free(prelut);
+ return NULL;
+ }
+
+ prelut->length = prelutLength;
+
+ for( i=0; i<prelutLength; ++i )
+ {
+ prelut->stims[i] = 0.0;
+ prelut->values[i] = 0.0;
+ }
+
+ return prelut;
+ }
+
+ void rsr_Interpolator1D_Raw_destroy( rsr_Interpolator1D_Raw * prelut )
+ {
+ if(prelut==NULL) return;
+ free( prelut->stims );
+ free( prelut->values );
+ free( prelut );
+ }
+
+ } // End unnamed namespace for Interpolators.c
+
+ namespace
+ {
+ class CachedFileCSP : public CachedFile
+ {
+ public:
+ CachedFileCSP () :
+ hasprelut(false),
+ csptype("unknown"),
+ metadata("none")
+ {
+ prelut = Lut1D::Create();
+ lut1D = Lut1D::Create();
+ lut3D = Lut3D::Create();
+ };
+ ~CachedFileCSP() {};
+
+ bool hasprelut;
+ std::string csptype;
+ std::string metadata;
+ Lut1DRcPtr prelut;
+ Lut1DRcPtr lut1D;
+ Lut3DRcPtr lut3D;
+ };
+ typedef OCIO_SHARED_PTR<CachedFileCSP> CachedFileCSPRcPtr;
+
+
+
+ class LocalFileFormat : public FileFormat
+ {
+ public:
+
+ ~LocalFileFormat() {};
+
+ virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const;
+
+ virtual CachedFileRcPtr Read(std::istream & istream) const;
+
+ virtual void Write(const Baker & baker,
+ const std::string & formatName,
+ std::ostream & ostream) const;
+
+ virtual void BuildFileOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ CachedFileRcPtr untypedCachedFile,
+ const FileTransform& fileTransform,
+ TransformDirection dir) const;
+ };
+
+
+ // TODO: remove this when we don't need to debug
+ /*
+ template<class T>
+ std::ostream& operator<< (std::ostream& os, const std::vector<T>& v)
+ {
+ copy(v.begin(), v.end(), std::ostream_iterator<T>(std::cout, " "));
+ return os;
+ }
+ */
+
+ void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const
+ {
+ FormatInfo info;
+ info.name = "cinespace";
+ info.extension = "csp";
+ info.capabilities = (FORMAT_CAPABILITY_READ | FORMAT_CAPABILITY_WRITE);
+ 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 csp lut");
+ }
+
+ Lut1DRcPtr prelut_ptr = Lut1D::Create();
+ Lut1DRcPtr lut1d_ptr = Lut1D::Create();
+ Lut3DRcPtr lut3d_ptr = Lut3D::Create();
+
+ // try and read the lut header
+ std::string line;
+ nextline (istream, line);
+ if (line != "CSPLUTV100")
+ {
+ std::ostringstream os;
+ os << "Lut doesn't seem to be a csp file, expected 'CSPLUTV100'.";
+ os << "First line: '" << line << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ // next line tells us if we are reading a 1D or 3D lut
+ nextline (istream, line);
+ if (line != "1D" && line != "3D")
+ {
+ std::ostringstream os;
+ os << "Unsupported CSP lut type. Require 1D or 3D. ";
+ os << "Found, '" << line << "'.";
+ throw Exception(os.str().c_str());
+ }
+ std::string csptype = line;
+
+ // read meta data block
+ std::string metadata;
+ std::streampos curr_pos = istream.tellg ();
+ nextline (istream, line);
+ if (line == "BEGIN METADATA")
+ {
+ while (line != "END METADATA" || !istream)
+ {
+ nextline (istream, line);
+ if (line != "END METADATA")
+ metadata += line + "\n";
+ }
+ }
+ else
+ {
+ istream.seekg (curr_pos);
+ }
+
+ // Make 3 vectors of prelut inputs + output values
+ std::vector<float> prelut_in[3];
+ std::vector<float> prelut_out[3];
+ bool useprelut[3] = { false, false, false };
+
+ // Parse the prelut block
+ for (int c = 0; c < 3; ++c)
+ {
+ // how many points do we have for this channel
+ nextline (istream, line);
+ int cpoints = 0;
+
+ if(!StringToInt(&cpoints, line.c_str()) || cpoints<0)
+ {
+ std::ostringstream os;
+ os << "Prelut does not specify valid dimension size on channel '";
+ os << c << ": " << line;
+ throw Exception(os.str().c_str());
+ }
+
+ if(cpoints>=2)
+ {
+ std::vector<std::string> inputparts, outputparts;
+
+ nextline (istream, line);
+ pystring::split(pystring::strip(line), inputparts);
+
+ nextline (istream, line);
+ pystring::split(pystring::strip(line), outputparts);
+
+ if(static_cast<int>(inputparts.size()) != cpoints ||
+ static_cast<int>(outputparts.size()) != cpoints)
+ {
+ std::ostringstream os;
+ os << "Prelut does not specify the expected number of data points. ";
+ os << "Expected: " << cpoints << ".";
+ os << "Found: " << inputparts.size() << ", " << outputparts.size() << ".";
+ throw Exception(os.str().c_str());
+ }
+
+ if(!StringVecToFloatVec(prelut_in[c], inputparts) ||
+ !StringVecToFloatVec(prelut_out[c], outputparts))
+ {
+ std::ostringstream os;
+ os << "Prelut data is malformed, cannot to float array.";
+ throw Exception(os.str().c_str());
+ }
+
+
+ useprelut[c] = (!VecsEqualWithRelError(&(prelut_in[c][0]), static_cast<int>(prelut_in[c].size()),
+ &(prelut_out[c][0]), static_cast<int>(prelut_out[c].size()),
+ 1e-6f));
+ }
+ else
+ {
+ // Even though it's probably not part of the spec, why not allow for a size 0
+ // in a channel to be specified? It should be synonymous with identity,
+ // and allows the code lower down to assume all 3 channels exist
+
+ prelut_in[c].push_back(0.0f);
+ prelut_in[c].push_back(1.0f);
+ prelut_out[c].push_back(0.0f);
+ prelut_out[c].push_back(1.0f);
+ useprelut[c] = false;
+ }
+ }
+
+ if (csptype == "1D")
+ {
+
+ // how many 1D lut points do we have
+ nextline (istream, line);
+ int points1D = atoi (line.c_str());
+
+ //
+ float from_min = 0.0;
+ float from_max = 1.0;
+ for(int i=0; i<3; ++i)
+ {
+ lut1d_ptr->from_min[i] = from_min;
+ lut1d_ptr->from_max[i] = from_max;
+ lut1d_ptr->luts[i].clear();
+ lut1d_ptr->luts[i].reserve(points1D);
+ }
+
+ for(int i = 0; i < points1D; ++i)
+ {
+
+ // scan for the three floats
+ float lp[3];
+ nextline (istream, line);
+ if (sscanf (line.c_str(), "%f %f %f",
+ &lp[0], &lp[1], &lp[2]) != 3) {
+ throw Exception ("malformed 1D csp lut");
+ }
+
+ // store each channel
+ lut1d_ptr->luts[0].push_back(lp[0]);
+ lut1d_ptr->luts[1].push_back(lp[1]);
+ lut1d_ptr->luts[2].push_back(lp[2]);
+
+ }
+
+ }
+ else if (csptype == "3D")
+ {
+ // read the cube size
+ nextline (istream, line);
+ if (sscanf (line.c_str(), "%d %d %d",
+ &lut3d_ptr->size[0],
+ &lut3d_ptr->size[1],
+ &lut3d_ptr->size[2]) != 3 ) {
+ throw Exception("malformed 3D csp lut, couldn't read cube size");
+ }
+
+
+ // resize cube
+ int num3dentries = lut3d_ptr->size[0] * lut3d_ptr->size[1] * lut3d_ptr->size[2];
+ lut3d_ptr->lut.resize(num3dentries * 3);
+
+ for(int i=0; i<num3dentries; ++i)
+ {
+ // load the cube
+ nextline (istream, line);
+
+ if(sscanf (line.c_str(), "%f %f %f",
+ &lut3d_ptr->lut[3*i+0],
+ &lut3d_ptr->lut[3*i+1],
+ &lut3d_ptr->lut[3*i+2]) != 3 )
+ {
+ std::ostringstream os;
+ os << "Malformed 3D csp lut, couldn't read cube row (";
+ os << i << "): " << line << " .";
+ throw Exception(os.str().c_str());
+ }
+ }
+ }
+
+ CachedFileCSPRcPtr cachedFile = CachedFileCSPRcPtr (new CachedFileCSP ());
+ cachedFile->csptype = csptype;
+ cachedFile->metadata = metadata;
+
+ if(useprelut[0] || useprelut[1] || useprelut[2])
+ {
+ cachedFile->hasprelut = true;
+
+ for (int c = 0; c < 3; ++c)
+ {
+ size_t prelut_numpts = prelut_in[c].size();
+ float from_min = prelut_in[c][0];
+ float from_max = prelut_in[c][prelut_numpts-1];
+
+ // Allocate the interpolator
+ rsr_Interpolator1D_Raw * cprelut_raw =
+ rsr_Interpolator1D_Raw_create(static_cast<unsigned int>(prelut_numpts));
+
+ // Copy our prelut data into the interpolator
+ for(size_t i=0; i<prelut_numpts; ++i)
+ {
+ cprelut_raw->stims[i] = prelut_in[c][i];
+ cprelut_raw->values[i] = prelut_out[c][i];
+ }
+
+ // Create interpolater, to resample to simple 1D lut
+ rsr_Interpolator1D * interpolater =
+ rsr_Interpolator1D_createFromRaw(cprelut_raw);
+
+ // Resample into 1D lut
+ // TODO: Fancy spline analysis to determine required number of samples
+ prelut_ptr->from_min[c] = from_min;
+ prelut_ptr->from_max[c] = from_max;
+ prelut_ptr->luts[c].clear();
+ prelut_ptr->luts[c].reserve(NUM_PRELUT_SAMPLES);
+
+ for (int i = 0; i < NUM_PRELUT_SAMPLES; ++i)
+ {
+ float interpo = float(i) / float(NUM_PRELUT_SAMPLES-1);
+ float srcval = lerpf(from_min, from_max, interpo);
+ float newval = rsr_Interpolator1D_interpolate(srcval, interpolater);
+ prelut_ptr->luts[c].push_back(newval);
+ }
+
+ rsr_Interpolator1D_Raw_destroy(cprelut_raw);
+ rsr_Interpolator1D_destroy(interpolater);
+ }
+
+ prelut_ptr->maxerror = 1e-6f;
+ prelut_ptr->errortype = ERROR_RELATIVE;
+
+ cachedFile->prelut = prelut_ptr;
+ }
+
+ if(csptype == "1D")
+ {
+ lut1d_ptr->maxerror = 0.0f;
+ lut1d_ptr->errortype = ERROR_RELATIVE;
+ cachedFile->lut1D = lut1d_ptr;
+ }
+ else if (csptype == "3D")
+ {
+ cachedFile->lut3D = lut3d_ptr;
+ }
+
+ return cachedFile;
+ }
+
+
+ void LocalFileFormat::Write(const Baker & baker,
+ const std::string & /*formatName*/,
+ std::ostream & ostream) const
+ {
+ const int DEFAULT_CUBE_SIZE = 32;
+ const int DEFAULT_SHAPER_SIZE = 1024;
+
+ ConstConfigRcPtr config = baker.getConfig();
+
+ // TODO: Add 1d/3d lut writing switch, using hasChannelCrosstalk
+ int cubeSize = baker.getCubeSize();
+ if(cubeSize==-1) cubeSize = DEFAULT_CUBE_SIZE;
+ cubeSize = std::max(2, cubeSize); // smallest cube is 2x2x2
+ std::vector<float> cubeData;
+ cubeData.resize(cubeSize*cubeSize*cubeSize*3);
+ GenerateIdentityLut3D(&cubeData[0], cubeSize, 3, LUT3DORDER_FAST_RED);
+ PackedImageDesc cubeImg(&cubeData[0], cubeSize*cubeSize*cubeSize, 1, 3);
+
+ std::string looks = baker.getLooks();
+
+ std::vector<float> shaperInData;
+ std::vector<float> shaperOutData;
+
+ // Use an explicitly shaper space
+ // TODO: Use the optional allocation for the shaper space,
+ // instead of the implied 0-1 uniform allocation
+ std::string shaperSpace = baker.getShaperSpace();
+ if(!shaperSpace.empty())
+ {
+ int shaperSize = baker.getShaperSize();
+ if(shaperSize<0) shaperSize = DEFAULT_SHAPER_SIZE;
+ if(shaperSize<2)
+ {
+ std::ostringstream os;
+ os << "When a shaper space has been specified, '";
+ os << baker.getShaperSpace() << "', a shaper size less than 2 is not allowed.";
+ throw Exception(os.str().c_str());
+ }
+
+ shaperOutData.resize(shaperSize*3);
+ shaperInData.resize(shaperSize*3);
+ GenerateIdentityLut1D(&shaperOutData[0], shaperSize, 3);
+ GenerateIdentityLut1D(&shaperInData[0], shaperSize, 3);
+
+ ConstProcessorRcPtr shaperToInput = config->getProcessor(baker.getShaperSpace(), baker.getInputSpace());
+ if(shaperToInput->hasChannelCrosstalk())
+ {
+ // TODO: Automatically turn shaper into non-crosstalked version?
+ std::ostringstream os;
+ os << "The specified shaperSpace, '";
+ os << baker.getShaperSpace() << "' has channel crosstalk, which is not appropriate for shapers. ";
+ os << "Please select an alternate shaper space or omit this option.";
+ throw Exception(os.str().c_str());
+ }
+ PackedImageDesc shaperInImg(&shaperInData[0], shaperSize, 1, 3);
+ shaperToInput->apply(shaperInImg);
+
+ ConstProcessorRcPtr shaperToTarget;
+ if (!looks.empty())
+ {
+ LookTransformRcPtr transform = LookTransform::Create();
+ transform->setLooks(looks.c_str());
+ transform->setSrc(baker.getShaperSpace());
+ transform->setDst(baker.getTargetSpace());
+ shaperToTarget = config->getProcessor(transform,
+ TRANSFORM_DIR_FORWARD);
+ }
+ else
+ {
+ shaperToTarget = config->getProcessor(baker.getShaperSpace(),
+ baker.getTargetSpace());
+ }
+ shaperToTarget->apply(cubeImg);
+ }
+ else
+ {
+ // A shaper is not specified, let's fake one, using the input space allocation as
+ // our guide
+
+ ConstColorSpaceRcPtr inputColorSpace = config->getColorSpace(baker.getInputSpace());
+
+ if(!inputColorSpace)
+ {
+ std::ostringstream os;
+ os << "Could not find colorspace '" << baker.getInputSpace() << "'";
+ throw Exception(os.str().c_str());
+ }
+
+ // Let's make an allocation transform for this colorspace
+ AllocationTransformRcPtr allocationTransform = AllocationTransform::Create();
+ allocationTransform->setAllocation(inputColorSpace->getAllocation());
+
+ // numVars may be '0'
+ int numVars = inputColorSpace->getAllocationNumVars();
+ if(numVars>0)
+ {
+ std::vector<float> vars(numVars);
+ inputColorSpace->getAllocationVars(&vars[0]);
+ allocationTransform->setVars(numVars, &vars[0]);
+ }
+ else
+ {
+ allocationTransform->setVars(0, NULL);
+ }
+
+ // What size shaper should we make?
+ int shaperSize = baker.getShaperSize();
+ if(shaperSize<0) shaperSize = DEFAULT_SHAPER_SIZE;
+ shaperSize = std::max(2, shaperSize);
+ if(inputColorSpace->getAllocation() == ALLOCATION_UNIFORM)
+ {
+ // This is an awesome optimization.
+ // If we know it's a uniform scaling, only 2 points will suffice!
+ shaperSize = 2;
+ }
+ shaperOutData.resize(shaperSize*3);
+ shaperInData.resize(shaperSize*3);
+ GenerateIdentityLut1D(&shaperOutData[0], shaperSize, 3);
+ GenerateIdentityLut1D(&shaperInData[0], shaperSize, 3);
+
+ // Apply the forward to the allocation to the output shaper y axis, and the cube
+ ConstProcessorRcPtr shaperToInput = config->getProcessor(allocationTransform, TRANSFORM_DIR_INVERSE);
+ PackedImageDesc shaperInImg(&shaperInData[0], shaperSize, 1, 3);
+ shaperToInput->apply(shaperInImg);
+ shaperToInput->apply(cubeImg);
+
+ // Apply the 3d lut to the remainder (from the input to the output)
+ ConstProcessorRcPtr inputToTarget;
+ if (!looks.empty())
+ {
+ LookTransformRcPtr transform = LookTransform::Create();
+ transform->setLooks(looks.c_str());
+ transform->setSrc(baker.getInputSpace());
+ transform->setDst(baker.getTargetSpace());
+ inputToTarget = config->getProcessor(transform,
+ TRANSFORM_DIR_FORWARD);
+ }
+ else
+ {
+ inputToTarget = config->getProcessor(baker.getInputSpace(), baker.getTargetSpace());
+ }
+ inputToTarget->apply(cubeImg);
+ }
+
+ // Write out the file
+ ostream << "CSPLUTV100\n";
+ ostream << "3D\n";
+ ostream << "BEGIN METADATA\n";
+ std::string metadata = baker.getMetadata();
+ if(!metadata.empty())
+ {
+ ostream << metadata << "\n";
+ }
+ ostream << "END METADATA\n";
+ ostream << "\n";
+
+ // Write out the 1D Prelut
+ ostream.setf(std::ios::fixed, std::ios::floatfield);
+ ostream.precision(6);
+
+ if(shaperInData.size()<2 || shaperOutData.size() != shaperInData.size())
+ {
+ throw Exception("Internal shaper size exception.");
+ }
+
+ if(!shaperInData.empty())
+ {
+ for(int c=0; c<3; ++c)
+ {
+ ostream << shaperInData.size()/3 << "\n";
+ for(unsigned int i = 0; i<shaperInData.size()/3; ++i)
+ {
+ if(i != 0) ostream << " ";
+ ostream << shaperInData[3*i+c];
+ }
+ ostream << "\n";
+
+ for(unsigned int i = 0; i<shaperInData.size()/3; ++i)
+ {
+ if(i != 0) ostream << " ";
+ ostream << shaperOutData[3*i+c];
+ }
+ ostream << "\n";
+ }
+ }
+ ostream << "\n";
+
+ // Write out the 3D Cube
+ if(cubeSize < 2)
+ {
+ throw Exception("Internal cube size exception.");
+ }
+ ostream << cubeSize << " " << cubeSize << " " << cubeSize << "\n";
+ for(int i=0; i<cubeSize*cubeSize*cubeSize; ++i)
+ {
+ ostream << cubeData[3*i+0] << " " << cubeData[3*i+1] << " " << cubeData[3*i+2] << "\n";
+ }
+ ostream << "\n";
+ }
+
+ void
+ LocalFileFormat::BuildFileOps(OpRcPtrVec & ops,
+ const Config& /*config*/,
+ const ConstContextRcPtr & /*context*/,
+ CachedFileRcPtr untypedCachedFile,
+ const FileTransform& fileTransform,
+ TransformDirection dir) const
+ {
+
+ CachedFileCSPRcPtr cachedFile = DynamicPtrCast<CachedFileCSP>(untypedCachedFile);
+
+ // This should never happen.
+ if(!cachedFile)
+ {
+ std::ostringstream os;
+ os << "Cannot build CSP Op. Invalid cache type.";
+ throw Exception(os.str().c_str());
+ }
+
+ TransformDirection newDir = CombineTransformDirections(dir,
+ fileTransform.getDirection());
+
+ if(newDir == TRANSFORM_DIR_FORWARD)
+ {
+ if(cachedFile->hasprelut)
+ {
+ CreateLut1DOp(ops, cachedFile->prelut,
+ PRELUT_INTERPOLATION, newDir);
+ }
+ if(cachedFile->csptype == "1D")
+ CreateLut1DOp(ops, cachedFile->lut1D,
+ fileTransform.getInterpolation(), newDir);
+ else if(cachedFile->csptype == "3D")
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else if(newDir == TRANSFORM_DIR_INVERSE)
+ {
+ if(cachedFile->csptype == "1D")
+ CreateLut1DOp(ops, cachedFile->lut1D,
+ fileTransform.getInterpolation(), newDir);
+ else if(cachedFile->csptype == "3D")
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ if(cachedFile->hasprelut)
+ {
+ CreateLut1DOp(ops, cachedFile->prelut,
+ PRELUT_INTERPOLATION, newDir);
+ }
+ }
+
+ return;
+
+ }
+ }
+
+ FileFormat * CreateFileFormatCSP()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+OIIO_ADD_TEST(FileFormatCSP, simple1D)
+{
+ std::ostringstream strebuf;
+ strebuf << "CSPLUTV100" << "\n";
+ strebuf << "1D" << "\n";
+ strebuf << "" << "\n";
+ strebuf << "BEGIN METADATA" << "\n";
+ strebuf << "foobar" << "\n";
+ strebuf << "END METADATA" << "\n";
+ strebuf << "" << "\n";
+ strebuf << "2" << "\n";
+ strebuf << "0.0 1.0" << "\n";
+ strebuf << "0.0 2.0" << "\n";
+ strebuf << "6" << "\n";
+ strebuf << "0.0 0.2 0.4 0.6 0.8 1.0" << "\n";
+ strebuf << "0.0 0.4 0.8 1.2 1.6 2.0" << "\n";
+ strebuf << "3" << "\n";
+ strebuf << "0.0 0.1 1.0" << "\n";
+ strebuf << "0.0 0.2 2.0" << "\n";
+ strebuf << "" << "\n";
+ strebuf << "6" << "\n";
+ strebuf << "0.0 0.0 0.0" << "\n";
+ strebuf << "0.2 0.3 0.1" << "\n";
+ strebuf << "0.4 0.5 0.2" << "\n";
+ strebuf << "0.5 0.6 0.3" << "\n";
+ strebuf << "0.6 0.8 0.4" << "\n";
+ strebuf << "1.0 0.9 0.5" << "\n";
+
+ float red[6] = { 0.0f, 0.2f, 0.4f, 0.5f, 0.6f, 1.0f };
+ float green[6] = { 0.0f, 0.3f, 0.5f, 0.6f, 0.8f, 0.9f };
+ float blue[6] = { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f };
+
+ std::istringstream simple1D;
+ simple1D.str(strebuf.str());
+
+ // Read file
+ OCIO::LocalFileFormat tester;
+ OCIO::CachedFileRcPtr cachedFile = tester.Read(simple1D);
+ OCIO::CachedFileCSPRcPtr csplut = OCIO::DynamicPtrCast<OCIO::CachedFileCSP>(cachedFile);
+
+ // check prelut data (note: the spline is resampled into a 1D LUT)
+ for(int c=0; c<3; ++c)
+ {
+ for (unsigned int i = 0; i < csplut->prelut->luts[c].size(); i += 128)
+ {
+ float input = float(i) / float(csplut->prelut->luts[c].size()-1);
+ float output = csplut->prelut->luts[c][i];
+ OIIO_CHECK_CLOSE(input*2.0f, output, 1e-4);
+ }
+ }
+ // check 1D data
+ // red
+ unsigned int i;
+ for(i = 0; i < csplut->lut1D->luts[0].size(); ++i)
+ OIIO_CHECK_EQUAL(red[i], csplut->lut1D->luts[0][i]);
+ // green
+ for(i = 0; i < csplut->lut1D->luts[1].size(); ++i)
+ OIIO_CHECK_EQUAL(green[i], csplut->lut1D->luts[1][i]);
+ // blue
+ for(i = 0; i < csplut->lut1D->luts[2].size(); ++i)
+ OIIO_CHECK_EQUAL(blue[i], csplut->lut1D->luts[2][i]);
+
+}
+
+OIIO_ADD_TEST(FileFormatCSP, simple3D)
+{
+ std::ostringstream strebuf;
+ strebuf << "CSPLUTV100" << "\n";
+ strebuf << "3D" << "\n";
+ strebuf << "" << "\n";
+ strebuf << "BEGIN METADATA" << "\n";
+ strebuf << "foobar" << "\n";
+ strebuf << "END METADATA" << "\n";
+ strebuf << "" << "\n";
+ strebuf << "11" << "\n";
+ strebuf << "0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0" << "\n";
+ strebuf << "0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0" << "\n";
+ strebuf << "6" << "\n";
+ strebuf << "0.0 0.2 0.4 0.6 0.8 1.0" << "\n";
+ strebuf << "0.0 0.2000000 0.4 0.6 0.8 1.0" << "\n";
+ strebuf << "5" << "\n";
+ strebuf << "0.0 0.25 0.5 0.6 0.7" << "\n";
+ strebuf << "0.0 0.25000001 0.5 0.6 0.7" << "\n";
+ strebuf << "" << "\n";
+ strebuf << "1 2 3" << "\n";
+ strebuf << "0.0 0.0 0.0" << "\n";
+ strebuf << "1.0 0.0 0.0" << "\n";
+ strebuf << "0.0 0.5 0.0" << "\n";
+ strebuf << "1.0 0.5 0.0" << "\n";
+ strebuf << "0.0 1.0 0.0" << "\n";
+ strebuf << "1.0 1.0 0.0" << "\n";
+
+ float cube[1 * 2 * 3 * 3] = { 0.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 0.5, 0.0,
+ 1.0, 0.5, 0.0,
+ 0.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0 };
+
+ std::istringstream simple3D;
+ simple3D.str(strebuf.str());
+
+ // Load file
+ OCIO::LocalFileFormat tester;
+ OCIO::CachedFileRcPtr cachedFile = tester.Read(simple3D);
+ OCIO::CachedFileCSPRcPtr csplut = OCIO::DynamicPtrCast<OCIO::CachedFileCSP>(cachedFile);
+
+ // check prelut data
+ OIIO_CHECK_ASSERT(csplut->hasprelut == false);
+
+ // check cube data
+ for(unsigned int i = 0; i < csplut->lut3D->lut.size(); ++i) {
+ OIIO_CHECK_EQUAL(cube[i], csplut->lut3D->lut[i]);
+ }
+
+ // check baker output
+ OCIO::ConfigRcPtr config = OCIO::Config::Create();
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("lnf");
+ cs->setFamily("lnf");
+ config->addColorSpace(cs);
+ config->setRole(OCIO::ROLE_REFERENCE, cs->getName());
+ }
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("shaper");
+ cs->setFamily("shaper");
+ OCIO::ExponentTransformRcPtr transform1 = OCIO::ExponentTransform::Create();
+ float test[4] = {2.6f, 2.6f, 2.6f, 1.0f};
+ transform1->setValue(test);
+ cs->setTransform(transform1, OCIO::COLORSPACE_DIR_TO_REFERENCE);
+ config->addColorSpace(cs);
+ }
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("target");
+ cs->setFamily("target");
+ OCIO::CDLTransformRcPtr transform1 = OCIO::CDLTransform::Create();
+ float rgb[3] = {0.1f, 0.1f, 0.1f};
+ transform1->setOffset(rgb);
+ cs->setTransform(transform1, OCIO::COLORSPACE_DIR_FROM_REFERENCE);
+ config->addColorSpace(cs);
+ }
+
+ /*
+ std::string bout =
+ "CSPLUTV100\n"
+ "3D\n"
+ "\n"
+ "BEGIN METADATA\n"
+ "date: 2011:02:21 15:22:55\n"
+ "END METADATA\n"
+ "\n"
+ "10\n"
+ "0.000000 0.111111 0.222222 0.333333 0.444444 0.555556 0.666667 0.777778 0.888889 1.000000\n"
+ "0.000000 0.429520 0.560744 0.655378 0.732058 0.797661 0.855604 0.907865 0.955710 1.000000\n"
+ "10\n"
+ "0.000000 0.111111 0.222222 0.333333 0.444444 0.555556 0.666667 0.777778 0.888889 1.000000\n"
+ "0.000000 0.429520 0.560744 0.655378 0.732058 0.797661 0.855604 0.907865 0.955710 1.000000\n"
+ "10\n"
+ "0.000000 0.111111 0.222222 0.333333 0.444444 0.555556 0.666667 0.777778 0.888889 1.000000\n"
+ "0.000000 0.429520 0.560744 0.655378 0.732058 0.797661 0.855604 0.907865 0.955710 1.000000\n"
+ "\n"
+ "2 2 2\n"
+ "0.100000 0.100000 0.100000\n"
+ "1.000000 0.100000 0.100000\n"
+ "0.100000 1.000000 0.100000\n"
+ "1.000000 1.000000 0.100000\n"
+ "0.100000 0.100000 1.000000\n"
+ "1.000000 0.100000 1.000000\n"
+ "0.100000 1.000000 1.000000\n"
+ "1.000000 1.000000 1.000000\n"
+ "\n";
+
+ OCIO::BakerRcPtr baker = OCIO::Baker::Create();
+ baker->setConfig(config);
+ baker->setFormat("cinespace");
+ baker->setInputSpace("lnf");
+ baker->setShaperSpace("shaper");
+ baker->setTargetSpace("target");
+ baker->setShaperSize(10);
+ baker->setCubeSize(2);
+ std::ostringstream output;
+ baker->bake(output);
+
+ //
+ std::vector<std::string> osvec;
+ OCIO::pystring::splitlines(output.str(), osvec);
+ std::vector<std::string> resvec;
+ OCIO::pystring::splitlines(bout, resvec);
+ OIIO_CHECK_EQUAL(osvec.size(), resvec.size());
+ for(unsigned int i = 0; i < resvec.size(); ++i)
+ {
+ // skip timestamp line
+ if(i != 4) OIIO_CHECK_EQUAL(osvec[i], resvec[i]);
+ }
+ */
+
+}
+
+// TODO: More strenuous tests of prelut resampling (non-noop preluts)
+
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/FileFormatHDL.cpp b/src/core/FileFormatHDL.cpp
new file mode 100644
index 0000000..995ef9f
--- /dev/null
+++ b/src/core/FileFormatHDL.cpp
@@ -0,0 +1,1481 @@
+/*
+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.
+*/
+
+/*
+
+ Houdini LUTs
+ http://www.sidefx.com/docs/hdk11.0/hdk_io_lut.html
+
+ Types:
+ - 1D Lut (partial support)
+ - 3D Lut
+ - 3D Lut with 1D Prelut
+
+ TODO:
+ - Add support for other 1D types (R, G, B, A, RGB, RGBA, All)
+ we only support type 'C' atm.
+ - Add support for 'Sampling' tag
+
+*/
+
+#include <cstdio>
+#include <iostream>
+#include <iterator>
+#include <cmath>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <map>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Lut1DOp.h"
+#include "Lut3DOp.h"
+#include "ParseUtils.h"
+#include "MathUtils.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ // HDL parser helpers
+
+ // HDL headers/LUT's are shoved into these datatypes
+ typedef std::map<std::string, std::vector<std::string> > StringToStringVecMap;
+ typedef std::map<std::string, std::vector<float> > StringToFloatVecMap;
+
+ void
+ readHeaders(StringToStringVecMap& headers,
+ std::istream& istream)
+ {
+ std::string line;
+ while(nextline(istream, line))
+ {
+ std::vector<std::string> chunks;
+
+ // Remove trailing/leading whitespace, lower-case and
+ // split into words
+ pystring::split(pystring::lower(pystring::strip(line)), chunks);
+
+ // Skip empty lines
+ if(chunks.empty()) continue;
+
+ // Stop looking for headers at the "LUT:" line
+ if(chunks[0] == "lut:") break;
+
+ // Use first index as key, and remove it from the value
+ std::string key = chunks[0];
+ chunks.erase(chunks.begin());
+
+ headers[key] = chunks;
+ }
+ }
+
+ // Try to grab key (e.g "version") from headers. Throws
+ // exception if not found, or if number of chunks in value is
+ // not between min_vals and max_vals (e.g the "length" key
+ // must exist, and must have either 1 or 2 values)
+ std::vector<std::string>
+ findHeaderItem(StringToStringVecMap& headers,
+ const std::string key,
+ const unsigned int min_vals,
+ const unsigned int max_vals)
+ {
+ StringToStringVecMap::iterator iter;
+ iter = headers.find(key);
+
+ // Error if key is not found
+ if(iter == headers.end())
+ {
+ std::ostringstream os;
+ os << "'" << key << "' line not found";
+ throw Exception(os.str().c_str());
+ }
+
+ // Error if incorrect number of values is found
+ if(iter->second.size() < min_vals ||
+ iter->second.size() > max_vals)
+ {
+ std::ostringstream os;
+ os << "Incorrect number of chunks (" << iter->second.size() << ")";
+ os << " after '" << key << "' line, expected ";
+
+ if(min_vals == max_vals)
+ {
+ os << min_vals;
+ }
+ else
+ {
+ os << "between " << min_vals << " and " << max_vals;
+ }
+
+ throw Exception(os.str().c_str());
+ }
+
+ return iter->second;
+ }
+
+ // Simple wrapper to call findHeaderItem with a fixed number
+ // of values (e.g "version" should have a single value)
+ std::vector<std::string>
+ findHeaderItem(StringToStringVecMap& chunks,
+ const std::string key,
+ const unsigned int numvals)
+ {
+ return findHeaderItem(chunks, key, numvals, numvals);
+ }
+
+ // Crudely parse LUT's - doesn't do any length checking etc,
+ // just grabs a series of floats for Pre{...}, 3d{...} etc
+ // Does some basic error checking, but there are situations
+ // were it could incorrectly accept broken data (like
+ // "Pre{0.0\n1.0}blah"), but hopefully none where it misses
+ // data
+ void
+ readLuts(std::istream& istream,
+ StringToFloatVecMap& lutValues)
+ {
+ // State variables
+ bool inlut = false;
+ std::string lutname;
+
+ std::string word;
+
+ while(istream >> word)
+ {
+ if(!inlut)
+ {
+ if(word == "{")
+ {
+ // Lone "{" is for a 3D
+ inlut = true;
+ lutname = "3d";
+ }
+ else
+ {
+ // Named lut, e.g "Pre {"
+ inlut = true;
+ lutname = pystring::lower(word);
+
+ // Ensure next word is "{"
+ std::string nextword;
+ istream >> nextword;
+ if(nextword != "{")
+ {
+ std::ostringstream os;
+ os << "Malformed LUT - Unknown word '";
+ os << word << "' after LUT name '";
+ os << nextword << "'";
+ throw Exception(os.str().c_str());
+ }
+ }
+ }
+ else if(word == "}")
+ {
+ // end of LUT
+ inlut = false;
+ lutname = "";
+ }
+ else if(inlut)
+ {
+ // StringToFloat was far slower, for 787456 values:
+ // - StringToFloat took 3879 (avg nanoseconds per value)
+ // - stdtod took 169 nanoseconds
+ char* endptr = 0;
+ float v = static_cast<float>(strtod(word.c_str(), &endptr));
+
+ if(!*endptr)
+ {
+ // Since each word should contain a single
+ // float value, the pointer should be null
+ lutValues[lutname].push_back(v);
+ }
+ else
+ {
+ // stdtod endptr still contained stuff,
+ // meaning an invalid float value
+ std::ostringstream os;
+ os << "Invalid float value in " << lutname;
+ os << " LUT, '" << word << "'";
+ throw Exception(os.str().c_str());
+ }
+ }
+ else
+ {
+ std::ostringstream os;
+ os << "Unexpected word, possibly a value outside";
+ os <<" a LUT {} block. Word was '" << word << "'";
+ throw Exception(os.str().c_str());
+
+ }
+ }
+ }
+
+ } // end anonymous "HDL parser helpers" namespace
+
+ namespace
+ {
+ class CachedFileHDL : public CachedFile
+ {
+ public:
+ CachedFileHDL ()
+ {
+ hdlversion = "unknown";
+ hdlformat = "unknown";
+ hdltype = "unknown";
+ hdlblack = 0.0;
+ hdlwhite = 1.0;
+ lut1D = Lut1D::Create();
+ lut3D = Lut3D::Create();
+ };
+ ~CachedFileHDL() {};
+ std::string hdlversion;
+ std::string hdlformat;
+ std::string hdltype;
+ float to_min; // TODO: maybe add this to Lut1DOp?
+ float to_max; // TODO: maybe add this to Lut1DOp?
+ float hdlblack;
+ float hdlwhite;
+ Lut1DRcPtr lut1D;
+ Lut3DRcPtr lut3D;
+ };
+ typedef OCIO_SHARED_PTR<CachedFileHDL> CachedFileHDLRcPtr;
+
+ class LocalFileFormat : public FileFormat
+ {
+ public:
+
+ ~LocalFileFormat() {};
+
+ virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const;
+
+ virtual CachedFileRcPtr Read(std::istream & istream) const;
+
+ virtual void Write(const Baker & baker,
+ const std::string & formatName,
+ std::ostream & ostream) 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 = "houdini";
+ info.extension = "lut";
+ info.capabilities = FORMAT_CAPABILITY_READ | FORMAT_CAPABILITY_WRITE;
+ 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 Houdini lut");
+
+ //
+ CachedFileHDLRcPtr cachedFile = CachedFileHDLRcPtr (new CachedFileHDL ());
+ Lut1DRcPtr lut1d_ptr = Lut1D::Create();
+ Lut3DRcPtr lut3d_ptr = Lut3D::Create();
+
+ // Parse headers into key-value pairs
+ StringToStringVecMap header_chunks;
+ StringToStringVecMap::iterator iter;
+
+ // Read headers, ending after the "LUT:" line
+ readHeaders(header_chunks, istream);
+
+ // Grab useful values from headers
+ std::vector<std::string> value;
+
+ // "Version 3" - format version (currently one version
+ // number per LUT type)
+ value = findHeaderItem(header_chunks, "version", 1);
+ cachedFile->hdlversion = value[0];
+
+ // "Format any" - bit depth of image the LUT should be
+ // applied to (this is basically ignored)
+ value = findHeaderItem(header_chunks, "format", 1);
+ cachedFile->hdlformat = value[0];
+
+ // "Type 3d" - type of LUT
+ {
+ value = findHeaderItem(header_chunks, "type", 1);
+
+ cachedFile->hdltype = value[0];
+ }
+
+ // "From 0.0 1.0" - range of input values
+ {
+ float from_min, from_max;
+
+ value = findHeaderItem(header_chunks, "from", 2);
+
+ if(!StringToFloat(&from_min, value[0].c_str()) ||
+ !StringToFloat(&from_max, value[1].c_str()))
+ {
+ std::ostringstream os;
+ os << "Invalid float value(s) on 'From' line, '";
+ os << value[0] << "' and '" << value[1] << "'";
+ throw Exception(os.str().c_str());
+ }
+
+ for(int i = 0; i < 3; ++i)
+ {
+ lut1d_ptr->from_min[i] = from_min;
+ lut1d_ptr->from_max[i] = from_max;
+ }
+ }
+
+
+ // "To 0.0 1.0" - range of values in LUT (e.g "0 255"
+ // to specify values as 8-bit numbers, usually "0 1")
+ {
+ float to_min, to_max;
+
+ value = findHeaderItem(header_chunks, "to", 2);
+
+ if(!StringToFloat(&to_min, value[0].c_str()) ||
+ !StringToFloat(&to_max, value[1].c_str()))
+ {
+ std::ostringstream os;
+ os << "Invalid float value(s) on 'To' line, '";
+ os << value[0] << "' and '" << value[1] << "'";
+ throw Exception(os.str().c_str());
+ }
+ cachedFile->to_min = to_min;
+ cachedFile->to_max = to_max;
+ }
+
+ // "Black 0" and "White 1" - obsolete options, should be 0
+ // and 1
+
+ {
+ value = findHeaderItem(header_chunks, "black", 1);
+
+ float black;
+
+ if(!StringToFloat(&black, value[0].c_str()))
+ {
+ std::ostringstream os;
+ os << "Invalid float value on 'Black' line, '";
+ os << value[0] << "'";
+ throw Exception(os.str().c_str());
+ }
+ cachedFile->hdlblack = black;
+ }
+
+ {
+ value = findHeaderItem(header_chunks, "white", 1);
+
+ float white;
+
+ if(!StringToFloat(&white, value[0].c_str()))
+ {
+ std::ostringstream os;
+ os << "Invalid float value on 'White' line, '";
+ os << value[0] << "'";
+ throw Exception(os.str().c_str());
+ }
+ cachedFile->hdlwhite = white;
+ }
+
+
+ // Verify type is valid and supported - used to handle
+ // length sensibly, and checking the LUT later
+ {
+ std::string ltype = cachedFile->hdltype;
+ if(ltype != "3d" && ltype != "3d+1d" && ltype != "c")
+ {
+ std::ostringstream os;
+ os << "Unsupported Houdini LUT type: '" << ltype << "'";
+ throw Exception(os.str().c_str());
+ }
+ }
+
+
+ // "Length 2" or "Length 2 5" - either "[cube size]", or "[cube
+ // size] [prelut size]"
+ int size_3d = -1;
+ int size_prelut = -1;
+ int size_1d = -1;
+
+ {
+ std::vector<int> lut_sizes;
+
+ value = findHeaderItem(header_chunks, "length", 1, 2);
+ for(unsigned int i = 0; i < value.size(); ++i)
+ {
+ int tmpsize = -1;
+ if(!StringToInt(&tmpsize, value[i].c_str()))
+ {
+ std::ostringstream os;
+ os << "Invalid integer on 'Length' line: ";
+ os << "'" << value[0] << "'";
+ throw Exception(os.str().c_str());
+ }
+ lut_sizes.push_back(tmpsize);
+ }
+
+ if(cachedFile->hdltype == "3d" || cachedFile->hdltype == "3d+1d")
+ {
+ // Set cube size
+ size_3d = lut_sizes[0];
+
+ lut3d_ptr->size[0] = lut_sizes[0];
+ lut3d_ptr->size[1] = lut_sizes[0];
+ lut3d_ptr->size[2] = lut_sizes[0];
+ }
+
+ if(cachedFile->hdltype == "c")
+ {
+ size_1d = lut_sizes[0];
+ }
+
+ if(cachedFile->hdltype == "3d+1d")
+ {
+ size_prelut = lut_sizes[1];
+ }
+ }
+
+ // Read stuff after "LUT:"
+ StringToFloatVecMap lut_data;
+ readLuts(istream, lut_data);
+
+ //
+ StringToFloatVecMap::iterator lut_iter;
+
+ if(cachedFile->hdltype == "3d+1d")
+ {
+ // Read prelut, and bind onto cachedFile
+ lut_iter = lut_data.find("pre");
+ if(lut_iter == lut_data.end())
+ {
+ std::ostringstream os;
+ os << "3D+1D LUT should contain Pre{} LUT section";
+ throw Exception(os.str().c_str());
+ }
+
+ if(size_prelut != static_cast<int>(lut_iter->second.size()))
+ {
+ std::ostringstream os;
+ os << "Pre{} LUT was " << lut_iter->second.size();
+ os << " values long, expected " << size_prelut << " values";
+ throw Exception(os.str().c_str());
+ }
+
+ lut1d_ptr->luts[0] = lut_iter->second;
+ lut1d_ptr->luts[1] = lut_iter->second;
+ lut1d_ptr->luts[2] = lut_iter->second;
+ lut1d_ptr->maxerror = 0.0f;
+ lut1d_ptr->errortype = ERROR_RELATIVE;
+ cachedFile->lut1D = lut1d_ptr;
+ }
+
+ if(cachedFile->hdltype == "3d" ||
+ cachedFile->hdltype == "3d+1d")
+ {
+ // Bind 3D LUT to lut3d_ptr, along with some
+ // slightly-elabourate error messages
+
+ lut_iter = lut_data.find("3d");
+ if(lut_iter == lut_data.end())
+ {
+ std::ostringstream os;
+ os << "3D LUT section not found";
+ throw Exception(os.str().c_str());
+ }
+
+ int size_3d_cubed = size_3d * size_3d * size_3d;
+
+ if(size_3d_cubed * 3 != static_cast<int>(lut_iter->second.size()))
+ {
+ int foundsize = static_cast<int>(lut_iter->second.size());
+ int foundlines = foundsize / 3;
+
+ std::ostringstream os;
+ os << "3D LUT contains incorrect number of values. ";
+ os << "Contained " << foundsize << " values ";
+ os << "(" << foundlines << " lines), ";
+ os << "expected " << (size_3d_cubed*3) << " values ";
+ os << "(" << size_3d_cubed << " lines)";
+ throw Exception(os.str().c_str());
+ }
+
+ lut3d_ptr->lut = lut_iter->second;
+
+ // Bind to cachedFile
+ cachedFile->lut3D = lut3d_ptr;
+ }
+
+ if(cachedFile->hdltype == "c")
+ {
+ // Bind simple 1D RGB LUT
+ lut_iter = lut_data.find("rgb");
+ if(lut_iter == lut_data.end())
+ {
+ std::ostringstream os;
+ os << "3D+1D LUT should contain Pre{} LUT section";
+ throw Exception(os.str().c_str());
+ }
+
+ if(size_1d != static_cast<int>(lut_iter->second.size()))
+ {
+ std::ostringstream os;
+ os << "RGB{} LUT was " << lut_iter->second.size();
+ os << " values long, expected " << size_1d << " values";
+ throw Exception(os.str().c_str());
+ }
+
+ lut1d_ptr->luts[0] = lut_iter->second;
+ lut1d_ptr->luts[1] = lut_iter->second;
+ lut1d_ptr->luts[2] = lut_iter->second;
+ lut1d_ptr->maxerror = 0.0f;
+ lut1d_ptr->errortype = ERROR_RELATIVE;
+ cachedFile->lut1D = lut1d_ptr;
+ }
+
+ return cachedFile;
+ }
+
+ void LocalFileFormat::Write(const Baker & baker,
+ const std::string & formatName,
+ std::ostream & ostream) const
+ {
+
+ if(formatName != "houdini")
+ {
+ std::ostringstream os;
+ os << "Unknown hdl format name, '";
+ os << formatName << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ // Get config
+ ConstConfigRcPtr config = baker.getConfig();
+
+ // setup the floating point precision
+ ostream.setf(std::ios::fixed, std::ios::floatfield);
+ ostream.precision(6);
+
+ // Default sizes
+ const int DEFAULT_SHAPER_SIZE = 1024;
+ // MPlay produces bad results with 32^3 cube (in a way
+ // that looks more quantised than even "nearest"
+ // interpolation in OCIOFileTransform)
+ const int DEFAULT_CUBE_SIZE = 64;
+ const int DEFAULT_1D_SIZE = 1024;
+
+ // Get configured sizes
+ int cubeSize = baker.getCubeSize();
+ int shaperSize = baker.getShaperSize();
+ // FIXME: Misusing cube size to set 1D LUT size, as it seemed
+ // slightly less confusing than using the shaper LUT size
+ int onedSize = baker.getCubeSize();
+
+ // Defaults and sanity check on cube size
+ if(cubeSize == -1) cubeSize = DEFAULT_CUBE_SIZE;
+ if(cubeSize < 0) cubeSize = DEFAULT_CUBE_SIZE;
+ if(cubeSize<2)
+ {
+ std::ostringstream os;
+ os << "Cube size must be 2 or larger (was " << cubeSize << ")";
+ throw Exception(os.str().c_str());
+ }
+
+ // ..and same for shaper size
+ if(shaperSize<0) shaperSize = DEFAULT_SHAPER_SIZE;
+ if(shaperSize<2)
+ {
+ std::ostringstream os;
+ os << "A shaper space ('" << baker.getShaperSpace() << "') has";
+ os << " been specified, so the shaper size must be 2 or larger";
+ throw Exception(os.str().c_str());
+ }
+
+ // ..and finally, for the 1D LUT size
+ if(onedSize == -1) onedSize = DEFAULT_1D_SIZE;
+ if(onedSize < 2)
+ {
+ std::ostringstream os;
+ os << "1D LUT size must be higher than 2 (was " << onedSize << ")";
+ throw Exception(os.str().c_str());
+ }
+
+ // Version numbers
+ const int HDL_1D = 1; // 1D LUT version number
+ const int HDL_3D = 2; // 3D LUT version number
+ const int HDL_3D1D = 3; // 3D LUT with 1D prelut
+
+ // Get spaces from baker
+ const std::string shaperSpace = baker.getShaperSpace();
+ const std::string inputSpace = baker.getInputSpace();
+ const std::string targetSpace = baker.getTargetSpace();
+ const std::string looks = baker.getLooks();
+
+ // Determine required LUT type
+ ConstProcessorRcPtr inputToTargetProc;
+ if (!looks.empty())
+ {
+ LookTransformRcPtr transform = LookTransform::Create();
+ transform->setLooks(looks.c_str());
+ transform->setSrc(inputSpace.c_str());
+ transform->setDst(targetSpace.c_str());
+ inputToTargetProc = config->getProcessor(transform,
+ TRANSFORM_DIR_FORWARD);
+ }
+ else
+ {
+ inputToTargetProc = config->getProcessor(
+ inputSpace.c_str(),
+ targetSpace.c_str());
+ }
+
+ int required_lut = -1;
+
+ if(inputToTargetProc->hasChannelCrosstalk())
+ {
+ if(shaperSpace.empty())
+ {
+ // Has crosstalk, but no prelut, so need 3D LUT
+ required_lut = HDL_3D;
+ }
+ else
+ {
+ // Crosstalk with shaper-space
+ required_lut = HDL_3D1D;
+ }
+ }
+ else
+ {
+ // No crosstalk
+ required_lut = HDL_1D;
+ }
+
+ if(required_lut == -1)
+ {
+ // Unnecessary paranoia
+ throw Exception(
+ "Internal logic error, LUT type was not determined");
+ }
+
+ // Make prelut
+ std::vector<float> prelutData;
+
+ float fromInStart = 0; // for "From:" part of header
+ float fromInEnd = 1;
+
+ if(required_lut == HDL_3D1D)
+ {
+ // TODO: Later we only grab the green channel for the prelut,
+ // should ensure the prelut is monochromatic somehow?
+
+ ConstProcessorRcPtr inputToShaperProc = config->getProcessor(
+ inputSpace.c_str(),
+ shaperSpace.c_str());
+
+ if(inputToShaperProc->hasChannelCrosstalk())
+ {
+ // TODO: Automatically turn shaper into
+ // non-crosstalked version?
+ std::ostringstream os;
+ os << "The specified shaperSpace, '" << baker.getShaperSpace();
+ os << "' has channel crosstalk, which is not appropriate for";
+ os << " shapers. Please select an alternate shaper space or";
+ os << " omit this option.";
+ throw Exception(os.str().c_str());
+ }
+
+ // Calculate min/max value
+ {
+ // Get input value of 1.0 in shaper space, as this
+ // is the higest value that is transformed by the
+ // cube (e.g for a generic lin-to-log trasnform,
+ // what the log value 1.0 is in linear).
+ ConstProcessorRcPtr shaperToInputProc = config->getProcessor(
+ shaperSpace.c_str(),
+ inputSpace.c_str());
+
+ float minval[3] = {0.0f, 0.0f, 0.0f};
+ float maxval[3] = {1.0f, 1.0f, 1.0f};
+
+ shaperToInputProc->applyRGB(minval);
+ shaperToInputProc->applyRGB(maxval);
+
+ // Grab green channel, as this is the one used later
+ fromInStart = minval[1];
+ fromInEnd = maxval[1];
+ }
+
+ // Generate the identity prelut values, then apply the transform.
+ // Prelut is linearly sampled from fromInStart to fromInEnd
+ prelutData.resize(shaperSize*3);
+
+ for (int i = 0; i < shaperSize; ++i)
+ {
+ const float x = (float)(double(i) / double(shaperSize - 1));
+ float cur_value = lerpf(fromInStart, fromInEnd, x);
+
+ prelutData[3*i+0] = cur_value;
+ prelutData[3*i+1] = cur_value;
+ prelutData[3*i+2] = cur_value;
+ }
+
+ PackedImageDesc prelutImg(&prelutData[0], shaperSize, 1, 3);
+ inputToShaperProc->apply(prelutImg);
+ }
+
+ // TODO: Do same "auto prelut" input-space allocation as FileFormatCSP?
+
+ // Make 3D LUT
+ std::vector<float> cubeData;
+ if(required_lut == HDL_3D || required_lut == HDL_3D1D)
+ {
+ cubeData.resize(cubeSize*cubeSize*cubeSize*3);
+
+ GenerateIdentityLut3D(&cubeData[0], cubeSize, 3, LUT3DORDER_FAST_RED);
+ PackedImageDesc cubeImg(&cubeData[0], cubeSize*cubeSize*cubeSize, 1, 3);
+
+ ConstProcessorRcPtr cubeProc;
+ if(required_lut == HDL_3D1D)
+ {
+ // Prelut goes from input-to-shaper, so cube goes from shaper-to-target
+ if (!looks.empty())
+ {
+ LookTransformRcPtr transform = LookTransform::Create();
+ transform->setLooks(looks.c_str());
+ transform->setSrc(inputSpace.c_str());
+ transform->setDst(targetSpace.c_str());
+ cubeProc = config->getProcessor(transform,
+ TRANSFORM_DIR_FORWARD);
+ }
+ else
+ {
+ cubeProc = config->getProcessor(shaperSpace.c_str(),
+ targetSpace.c_str());
+ }
+ }
+ else
+ {
+ // No prelut, so cube goes from input-to-target
+ cubeProc = inputToTargetProc;
+ }
+
+
+ cubeProc->apply(cubeImg);
+ }
+
+
+ // Make 1D LUT
+ std::vector<float> onedData;
+ if(required_lut == HDL_1D)
+ {
+ onedData.resize(onedSize * 3);
+
+ GenerateIdentityLut1D(&onedData[0], onedSize, 3);
+ PackedImageDesc onedImg(&onedData[0], onedSize, 1, 3);
+
+ inputToTargetProc->apply(onedImg);
+ }
+
+
+ // Write the file contents
+ ostream << "Version\t\t" << required_lut << "\n";
+ ostream << "Format\t\t" << "any" << "\n";
+
+ ostream << "Type\t\t";
+ if(required_lut == HDL_1D)
+ ostream << "RGB";
+ if(required_lut == HDL_3D)
+ ostream << "3D";
+ if(required_lut == HDL_3D1D)
+ ostream << "3D+1D";
+ ostream << "\n";
+
+ ostream << "From\t\t" << fromInStart << " " << fromInEnd << "\n";
+ ostream << "To\t\t" << 0.0f << " " << 1.0f << "\n";
+ ostream << "Black\t\t" << 0.0f << "\n";
+ ostream << "White\t\t" << 1.0f << "\n";
+
+ if(required_lut == HDL_3D1D)
+ ostream << "Length\t\t" << cubeSize << " " << shaperSize << "\n";
+ if(required_lut == HDL_3D)
+ ostream << "Length\t\t" << cubeSize << "\n";
+ if(required_lut == HDL_1D)
+ ostream << "Length\t\t" << onedSize << "\n";
+
+ ostream << "LUT:\n";
+
+ // Write prelut
+ if(required_lut == HDL_3D1D)
+ {
+ ostream << "Pre {\n";
+ for(int i=0; i < shaperSize; ++i)
+ {
+ // Grab green channel from RGB prelut
+ ostream << "\t" << prelutData[i*3+1] << "\n";
+ }
+ ostream << "}\n";
+ }
+
+ // Write "3D {" part of output of 3D+1D LUT
+ if(required_lut == HDL_3D1D)
+ {
+ ostream << "3D {\n";
+ }
+
+ // Write the slightly-different "{" without line for the 3D-only LUT
+ if(required_lut == HDL_3D)
+ {
+ ostream << " {\n";
+ }
+
+ // Write the cube data after the "{"
+ if(required_lut == HDL_3D || required_lut == HDL_3D1D)
+ {
+ for(int i=0; i < cubeSize*cubeSize*cubeSize; ++i)
+ {
+ // TODO: Original baker code clamped values to
+ // 1.0, was this necessary/desirable?
+
+ ostream << "\t" << cubeData[3*i+0];
+ ostream << " " << cubeData[3*i+1];
+ ostream << " " << cubeData[3*i+2] << "\n";
+ }
+
+ // Write closing "}"
+ ostream << " }\n";
+ }
+
+ // Write out channels for 1D LUT
+ if(required_lut == HDL_1D)
+ {
+ ostream << "R {\n";
+ for(int i=0; i < onedSize; ++i)
+ ostream << "\t" << onedData[i*3+0] << "\n";
+ ostream << "}\n";
+
+ ostream << "G {\n";
+ for(int i=0; i < onedSize; ++i)
+ ostream << "\t" << onedData[i*3+1] << "\n";
+ ostream << "}\n";
+
+ ostream << "B {\n";
+ for(int i=0; i < onedSize; ++i)
+ ostream << "\t" << onedData[i*3+2] << "\n";
+ ostream << "}\n";
+ }
+ }
+
+ void
+ LocalFileFormat::BuildFileOps(OpRcPtrVec & ops,
+ const Config& /*config*/,
+ const ConstContextRcPtr & /*context*/,
+ CachedFileRcPtr untypedCachedFile,
+ const FileTransform& fileTransform,
+ TransformDirection dir) const
+ {
+
+ CachedFileHDLRcPtr cachedFile = DynamicPtrCast<CachedFileHDL>(untypedCachedFile);
+
+ // This should never happen.
+ if(!cachedFile)
+ {
+ std::ostringstream os;
+ os << "Cannot build Houdini Op. Invalid cache type.";
+ throw Exception(os.str().c_str());
+ }
+
+ TransformDirection newDir = CombineTransformDirections(dir,
+ fileTransform.getDirection());
+
+ if(newDir == TRANSFORM_DIR_FORWARD) {
+ if(cachedFile->hdltype == "c")
+ {
+ CreateLut1DOp(ops, cachedFile->lut1D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else if(cachedFile->hdltype == "3d")
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else if(cachedFile->hdltype == "3d+1d")
+ {
+ CreateLut1DOp(ops, cachedFile->lut1D,
+ fileTransform.getInterpolation(), newDir);
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else
+ {
+ throw Exception("Unhandled hdltype while creating forward ops");
+ }
+ } else if(newDir == TRANSFORM_DIR_INVERSE) {
+ if(cachedFile->hdltype == "c")
+ {
+ CreateLut1DOp(ops, cachedFile->lut1D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else if(cachedFile->hdltype == "3d")
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else if(cachedFile->hdltype == "3d+1d")
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ CreateLut1DOp(ops, cachedFile->lut1D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else
+ {
+ throw Exception("Unhandled hdltype while creating reverse ops");
+ }
+ }
+ return;
+ }
+ }
+
+ FileFormat * CreateFileFormatHDL()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+OIIO_ADD_TEST(FileFormatHDL, Read1D)
+{
+ std::ostringstream strebuf;
+ strebuf << "Version\t\t1" << "\n";
+ strebuf << "Format\t\tany" << "\n";
+ strebuf << "Type\t\tC" << "\n";
+ strebuf << "From\t\t0.1 3.2" << "\n";
+ strebuf << "To\t\t0 1" << "\n";
+ strebuf << "Black\t\t0" << "\n";
+ strebuf << "White\t\t0.99" << "\n";
+ strebuf << "Length\t\t9" << "\n";
+ strebuf << "LUT:" << "\n";
+ strebuf << "RGB {" << "\n";
+ strebuf << "\t0" << "\n";
+ strebuf << "\t0.000977517" << "\n";
+ strebuf << "\t0.00195503" << "\n";
+ strebuf << "\t0.00293255" << "\n";
+ strebuf << "\t0.00391007" << "\n";
+ strebuf << "\t0.00488759" << "\n";
+ strebuf << "\t0.0058651" << "\n";
+ strebuf << "\t0.999022" << "\n";
+ strebuf << "\t1.67 }" << "\n";
+
+ //
+ float from_min = 0.1f;
+ float from_max = 3.2f;
+ float to_min = 0.0f;
+ float to_max = 1.0f;
+ float black = 0.0f;
+ float white = 0.99f;
+ float lut1d[9] = { 0.0f, 0.000977517f, 0.00195503f, 0.00293255f,
+ 0.00391007f, 0.00488759f, 0.0058651f, 0.999022f, 1.67f };
+
+ std::istringstream simple3D1D;
+ simple3D1D.str(strebuf.str());
+
+ // Load file
+ OCIO::LocalFileFormat tester;
+ OCIO::CachedFileRcPtr cachedFile = tester.Read(simple3D1D);
+ OCIO::CachedFileHDLRcPtr lut = OCIO::DynamicPtrCast<OCIO::CachedFileHDL>(cachedFile);
+
+ //
+ OIIO_CHECK_EQUAL(to_min, lut->to_min);
+ OIIO_CHECK_EQUAL(to_max, lut->to_max);
+ OIIO_CHECK_EQUAL(black, lut->hdlblack);
+ OIIO_CHECK_EQUAL(white, lut->hdlwhite);
+
+ // check 1D data (each channel has the same data)
+ for(int c = 0; c < 3; ++c) {
+ OIIO_CHECK_EQUAL(from_min, lut->lut1D->from_min[c]);
+ OIIO_CHECK_EQUAL(from_max, lut->lut1D->from_max[c]);
+
+ OIIO_CHECK_EQUAL(9, lut->lut1D->luts[c].size());
+
+ for(unsigned int i = 0; i < lut->lut1D->luts[c].size(); ++i) {
+ OIIO_CHECK_EQUAL(lut1d[i], lut->lut1D->luts[c][i]);
+ }
+ }
+}
+
+OIIO_ADD_TEST(FileFormatHDL, Bake1D)
+{
+
+ OCIO::ConfigRcPtr config = OCIO::Config::Create();
+
+ // Add lnf space
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("lnf");
+ cs->setFamily("lnf");
+ config->addColorSpace(cs);
+ config->setRole(OCIO::ROLE_REFERENCE, cs->getName());
+ }
+
+ // Add target space
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("target");
+ cs->setFamily("target");
+ OCIO::CDLTransformRcPtr transform1 = OCIO::CDLTransform::Create();
+
+ float rgb[3] = {0.1f, 0.1f, 0.1f};
+ transform1->setOffset(rgb);
+
+ cs->setTransform(transform1, OCIO::COLORSPACE_DIR_FROM_REFERENCE);
+ config->addColorSpace(cs);
+ }
+
+ std::string bout =
+ "Version\t\t1\n"
+ "Format\t\tany\n"
+ "Type\t\tRGB\n"
+ "From\t\t0.000000 1.000000\n"
+ "To\t\t0.000000 1.000000\n"
+ "Black\t\t0.000000\n"
+ "White\t\t1.000000\n"
+ "Length\t\t10\n"
+ "LUT:\n"
+ "R {\n"
+ "\t0.100000\n"
+ "\t0.211111\n"
+ "\t0.322222\n"
+ "\t0.433333\n"
+ "\t0.544444\n"
+ "\t0.655556\n"
+ "\t0.766667\n"
+ "\t0.877778\n"
+ "\t0.988889\n"
+ "\t1.100000\n"
+ " }\n"
+ "G {\n"
+ "\t0.100000\n"
+ "\t0.211111\n"
+ "\t0.322222\n"
+ "\t0.433333\n"
+ "\t0.544444\n"
+ "\t0.655556\n"
+ "\t0.766667\n"
+ "\t0.877778\n"
+ "\t0.988889\n"
+ "\t1.100000\n"
+ " }\n"
+ "B {\n"
+ "\t0.100000\n"
+ "\t0.211111\n"
+ "\t0.322222\n"
+ "\t0.433333\n"
+ "\t0.544444\n"
+ "\t0.655556\n"
+ "\t0.766667\n"
+ "\t0.877778\n"
+ "\t0.988889\n"
+ "\t1.100000\n"
+ " }\n";
+
+ //
+ OCIO::BakerRcPtr baker = OCIO::Baker::Create();
+ baker->setConfig(config);
+ baker->setFormat("houdini");
+ baker->setInputSpace("lnf");
+ baker->setTargetSpace("target");
+ baker->setCubeSize(10); // FIXME: Misusing the cube size to set the 1D LUT size
+ std::ostringstream output;
+ baker->bake(output);
+
+ //std::cerr << "The LUT: " << std::endl << output.str() << std::endl;
+ //std::cerr << "Expected:" << std::endl << bout << std::endl;
+
+ //
+ std::vector<std::string> osvec;
+ OCIO::pystring::splitlines(output.str(), osvec);
+ std::vector<std::string> resvec;
+ OCIO::pystring::splitlines(bout, resvec);
+ OIIO_CHECK_EQUAL(osvec.size(), resvec.size());
+ for(unsigned int i = 0; i < std::min(osvec.size(), resvec.size()); ++i)
+ OIIO_CHECK_EQUAL(OCIO::pystring::strip(osvec[i]), OCIO::pystring::strip(resvec[i]));
+
+}
+
+OIIO_ADD_TEST(FileFormatHDL, Read3D)
+{
+ std::ostringstream strebuf;
+ strebuf << "Version 2" << "\n";
+ strebuf << "Format any" << "\n";
+ strebuf << "Type 3D" << "\n";
+ strebuf << "From 0.2 0.9" << "\n";
+ strebuf << "To 0.001 0.999" << "\n";
+ strebuf << "Black 0.002" << "\n";
+ strebuf << "White 0.98" << "\n";
+ strebuf << "Length 2" << "\n";
+ strebuf << "LUT:" << "\n";
+ strebuf << " {" << "\n";
+ strebuf << " 0 0 0" << "\n";
+ strebuf << " 0 0 0" << "\n";
+ strebuf << " 0 0.390735 2.68116e-28" << "\n";
+ strebuf << " 0 0.390735 0" << "\n";
+ strebuf << " 0 0 0" << "\n";
+ strebuf << " 0 0 0.599397" << "\n";
+ strebuf << " 0 0.601016 0" << "\n";
+ strebuf << " 0 0.601016 0.917034" << "\n";
+ strebuf << " }" << "\n";
+
+ std::istringstream simple3D1D;
+ simple3D1D.str(strebuf.str());
+ // Load file
+ OCIO::LocalFileFormat tester;
+ OCIO::CachedFileRcPtr cachedFile = tester.Read(simple3D1D);
+ OCIO::CachedFileHDLRcPtr lut = OCIO::DynamicPtrCast<OCIO::CachedFileHDL>(cachedFile);
+
+ //
+ //float from_min = 0.2;
+ //float from_max = 0.9;
+ float to_min = 0.001f;
+ float to_max = 0.999f;
+ float black = 0.002f;
+ float white = 0.98f;
+ float cube[2 * 2 * 2 * 3 ] = {
+ 0.f, 0.f, 0.f,
+ 0.f, 0.f, 0.f,
+ 0.f, 0.390735f, 2.68116e-28f,
+ 0.f, 0.390735f, 0.f,
+ 0.f, 0.f, 0.f,
+ 0.f, 0.f, 0.599397f,
+ 0.f, 0.601016f, 0.f,
+ 0.f, 0.601016f, 0.917034f };
+
+ //
+ OIIO_CHECK_EQUAL(to_min, lut->to_min);
+ OIIO_CHECK_EQUAL(to_max, lut->to_max);
+ OIIO_CHECK_EQUAL(black, lut->hdlblack);
+ OIIO_CHECK_EQUAL(white, lut->hdlwhite);
+
+ // check cube data
+ OIIO_CHECK_EQUAL(2*2*2*3, lut->lut3D->lut.size());
+
+ for(unsigned int i = 0; i < lut->lut3D->lut.size(); ++i) {
+ OIIO_CHECK_EQUAL(cube[i], lut->lut3D->lut[i]);
+ }
+}
+
+OIIO_ADD_TEST(FileFormatHDL, Bake3D)
+{
+ OCIO::ConfigRcPtr config = OCIO::Config::Create();
+
+ // Set luma coef's to simple values
+ {
+ float lumaCoef[3] = {0.333f, 0.333f, 0.333f};
+ config->setDefaultLumaCoefs(lumaCoef);
+ }
+
+ // Add lnf space
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("lnf");
+ cs->setFamily("lnf");
+ config->addColorSpace(cs);
+ config->setRole(OCIO::ROLE_REFERENCE, cs->getName());
+ }
+
+ // Add target space
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("target");
+ cs->setFamily("target");
+ OCIO::CDLTransformRcPtr transform1 = OCIO::CDLTransform::Create();
+
+ // Set saturation to cause channel crosstalk, making a 3D LUT
+ transform1->setSat(0.5f);
+
+ cs->setTransform(transform1, OCIO::COLORSPACE_DIR_FROM_REFERENCE);
+ config->addColorSpace(cs);
+ }
+
+ std::string bout =
+ "Version\t\t2\n"
+ "Format\t\tany\n"
+ "Type\t\t3D\n"
+ "From\t\t0.000000 1.000000\n"
+ "To\t\t0.000000 1.000000\n"
+ "Black\t\t0.000000\n"
+ "White\t\t1.000000\n"
+ "Length\t\t2\n"
+ "LUT:\n"
+ " {\n"
+ "\t0.000000 0.000000 0.000000\n"
+ "\t0.606300 0.106300 0.106300\n"
+ "\t0.357600 0.857600 0.357600\n"
+ "\t0.963900 0.963900 0.463900\n"
+ "\t0.036100 0.036100 0.536100\n"
+ "\t0.642400 0.142400 0.642400\n"
+ "\t0.393700 0.893700 0.893700\n"
+ "\t1.000000 1.000000 1.000000\n"
+ " }\n";
+
+ //
+ OCIO::BakerRcPtr baker = OCIO::Baker::Create();
+ baker->setConfig(config);
+ baker->setFormat("houdini");
+ baker->setInputSpace("lnf");
+ baker->setTargetSpace("target");
+ baker->setCubeSize(2);
+ std::ostringstream output;
+ baker->bake(output);
+
+ //std::cerr << "The LUT: " << std::endl << output.str() << std::endl;
+ //std::cerr << "Expected:" << std::endl << bout << std::endl;
+
+ //
+ std::vector<std::string> osvec;
+ OCIO::pystring::splitlines(output.str(), osvec);
+ std::vector<std::string> resvec;
+ OCIO::pystring::splitlines(bout, resvec);
+ OIIO_CHECK_EQUAL(osvec.size(), resvec.size());
+ for(unsigned int i = 0; i < std::min(osvec.size(), resvec.size()); ++i)
+ OIIO_CHECK_EQUAL(OCIO::pystring::strip(osvec[i]), OCIO::pystring::strip(resvec[i]));
+}
+
+OIIO_ADD_TEST(FileFormatHDL, Read3D1D)
+{
+ std::ostringstream strebuf;
+ strebuf << "Version 3" << "\n";
+ strebuf << "Format any" << "\n";
+ strebuf << "Type 3D+1D" << "\n";
+ strebuf << "From 0.005478 14.080103" << "\n";
+ strebuf << "To 0 1" << "\n";
+ strebuf << "Black 0" << "\n";
+ strebuf << "White 1" << "\n";
+ strebuf << "Length 2 10" << "\n";
+ strebuf << "LUT:" << "\n";
+ strebuf << "Pre {" << "\n";
+ strebuf << " 0.994922" << "\n";
+ strebuf << " 0.995052" << "\n";
+ strebuf << " 0.995181" << "\n";
+ strebuf << " 0.995310" << "\n";
+ strebuf << " 0.995439" << "\n";
+ strebuf << " 0.995568" << "\n";
+ strebuf << " 0.995697" << "\n";
+ strebuf << " 0.995826" << "\n";
+ strebuf << " 0.995954" << "\n";
+ strebuf << " 0.996082" << "\n";
+ strebuf << "}" << "\n";
+ strebuf << "3D {" << "\n";
+ strebuf << " 0.093776 0.093776 0.093776" << "\n";
+ strebuf << " 0.105219 0.093776 0.093776" << "\n";
+ strebuf << " 0.118058 0.093776 0.093776" << "\n";
+ strebuf << " 0.132463 0.093776 0.093776" << "\n";
+ strebuf << " 0.148626 0.093776 0.093776" << "\n";
+ strebuf << " 0.166761 0.093776 0.093776" << "\n";
+ strebuf << " 0.187109 0.093776 0.093776" << "\n";
+ strebuf << " 0.209939 0.093776 0.093776" << "\n";
+ strebuf << "}" << "\n";
+
+ //
+ float from_min = 0.005478f;
+ float from_max = 14.080103f;
+ float to_min = 0.0f;
+ float to_max = 1.0f;
+ float black = 0.0f;
+ float white = 1.0f;
+ float prelut[10] = { 0.994922f, 0.995052f, 0.995181f, 0.995310f, 0.995439f,
+ 0.995568f, 0.995697f, 0.995826f, 0.995954f, 0.996082f };
+ float cube[2 * 2 * 2 * 3 ] = {
+ 0.093776f, 0.093776f, 0.093776f,
+ 0.105219f, 0.093776f, 0.093776f,
+ 0.118058f, 0.093776f, 0.093776f,
+ 0.132463f, 0.093776f, 0.093776f,
+ 0.148626f, 0.093776f, 0.093776f,
+ 0.166761f, 0.093776f, 0.093776f,
+ 0.187109f, 0.093776f, 0.093776f,
+ 0.209939f, 0.093776f, 0.093776f };
+
+ std::istringstream simple3D1D;
+ simple3D1D.str(strebuf.str());
+
+ // Load file
+ OCIO::LocalFileFormat tester;
+ OCIO::CachedFileRcPtr cachedFile = tester.Read(simple3D1D);
+ OCIO::CachedFileHDLRcPtr lut = OCIO::DynamicPtrCast<OCIO::CachedFileHDL>(cachedFile);
+
+ //
+ OIIO_CHECK_EQUAL(to_min, lut->to_min);
+ OIIO_CHECK_EQUAL(to_max, lut->to_max);
+ OIIO_CHECK_EQUAL(black, lut->hdlblack);
+ OIIO_CHECK_EQUAL(white, lut->hdlwhite);
+
+ // check prelut data (each channel has the same data)
+ for(int c = 0; c < 3; ++c) {
+ OIIO_CHECK_EQUAL(from_min, lut->lut1D->from_min[c]);
+ OIIO_CHECK_EQUAL(from_max, lut->lut1D->from_max[c]);
+ OIIO_CHECK_EQUAL(10, lut->lut1D->luts[c].size());
+
+ for(unsigned int i = 0; i < lut->lut1D->luts[c].size(); ++i) {
+ OIIO_CHECK_EQUAL(prelut[i], lut->lut1D->luts[c][i]);
+ }
+ }
+
+ OIIO_CHECK_EQUAL(2*2*2*3, lut->lut3D->lut.size());
+
+ // check cube data
+ for(unsigned int i = 0; i < lut->lut3D->lut.size(); ++i) {
+ OIIO_CHECK_EQUAL(cube[i], lut->lut3D->lut[i]);
+ }
+}
+
+OIIO_ADD_TEST(FileFormatHDL, Bake3D1D)
+{
+ // check baker output
+ OCIO::ConfigRcPtr config = OCIO::Config::Create();
+
+ // Set luma coef's to simple values
+ {
+ float lumaCoef[3] = {0.333f, 0.333f, 0.333f};
+ config->setDefaultLumaCoefs(lumaCoef);
+ }
+
+ // Add lnf space
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("lnf");
+ cs->setFamily("lnf");
+ config->addColorSpace(cs);
+ config->setRole(OCIO::ROLE_REFERENCE, cs->getName());
+ }
+
+ // Add shaper space
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("shaper");
+ cs->setFamily("shaper");
+ OCIO::ExponentTransformRcPtr transform1 = OCIO::ExponentTransform::Create();
+ float test[4] = {2.6f, 2.6f, 2.6f, 1.0f};
+ transform1->setValue(test);
+ cs->setTransform(transform1, OCIO::COLORSPACE_DIR_TO_REFERENCE);
+ config->addColorSpace(cs);
+ }
+
+ // Add target space
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("target");
+ cs->setFamily("target");
+ OCIO::CDLTransformRcPtr transform1 = OCIO::CDLTransform::Create();
+
+ // Set saturation to cause channel crosstalk, making a 3D LUT
+ transform1->setSat(0.5f);
+
+ cs->setTransform(transform1, OCIO::COLORSPACE_DIR_FROM_REFERENCE);
+ config->addColorSpace(cs);
+ }
+
+ std::string bout =
+ "Version\t\t3\n"
+ "Format\t\tany\n"
+ "Type\t\t3D+1D\n"
+ "From\t\t0.000000 1.000000\n"
+ "To\t\t0.000000 1.000000\n"
+ "Black\t\t0.000000\n"
+ "White\t\t1.000000\n"
+ "Length\t\t2 10\n"
+ "LUT:\n"
+ "Pre {\n"
+ "\t0.000000\n"
+ "\t0.429520\n"
+ "\t0.560744\n"
+ "\t0.655378\n"
+ "\t0.732057\n"
+ "\t0.797661\n"
+ "\t0.855604\n"
+ "\t0.907865\n"
+ "\t0.955710\n"
+ "\t1.000000\n"
+ "}\n"
+ "3D {\n"
+ "\t0.000000 0.000000 0.000000\n"
+ "\t0.606300 0.106300 0.106300\n"
+ "\t0.357600 0.857600 0.357600\n"
+ "\t0.963900 0.963900 0.463900\n"
+ "\t0.036100 0.036100 0.536100\n"
+ "\t0.642400 0.142400 0.642400\n"
+ "\t0.393700 0.893700 0.893700\n"
+ "\t1.000000 1.000000 1.000000\n"
+ "}\n";
+
+ //
+ OCIO::BakerRcPtr baker = OCIO::Baker::Create();
+ baker->setConfig(config);
+ baker->setFormat("houdini");
+ baker->setInputSpace("lnf");
+ baker->setShaperSpace("shaper");
+ baker->setTargetSpace("target");
+ baker->setShaperSize(10);
+ baker->setCubeSize(2);
+ std::ostringstream output;
+ baker->bake(output);
+
+ //std::cerr << "The LUT: " << std::endl << output.str() << std::endl;
+ //std::cerr << "Expected:" << std::endl << bout << std::endl;
+
+ //
+ std::vector<std::string> osvec;
+ OCIO::pystring::splitlines(output.str(), osvec);
+ std::vector<std::string> resvec;
+ OCIO::pystring::splitlines(bout, resvec);
+ OIIO_CHECK_EQUAL(osvec.size(), resvec.size());
+
+ // TODO: Get this working on osx
+ /*
+ for(unsigned int i = 0; i < std::min(osvec.size(), resvec.size()); ++i)
+ OIIO_CHECK_EQUAL(OCIO::pystring::strip(osvec[i]), OCIO::pystring::strip(resvec[i]));
+ */
+}
+
+#endif // OCIO_BUILD_TESTS
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
+
+
+///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/FileFormatIridasItx.cpp b/src/core/FileFormatIridasItx.cpp
new file mode 100644
index 0000000..43af60e
--- /dev/null
+++ b/src/core/FileFormatIridasItx.cpp
@@ -0,0 +1,336 @@
+/*
+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"
+
+/*
+
+Iridas itx format
+LUT_3D_SIZE M
+
+#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
+
+#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
+*/
+
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ class LocalCachedFile : public CachedFile
+ {
+ public:
+ LocalCachedFile ()
+ {
+ lut3D = Lut3D::Create();
+ };
+ ~LocalCachedFile() {};
+
+ 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 Write(const Baker & baker,
+ const std::string & formatName,
+ std::ostream & ostream) 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_itx";
+ info.extension = "itx";
+ info.capabilities = (FORMAT_CAPABILITY_READ | FORMAT_CAPABILITY_WRITE);
+ 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 .itx lut");
+ }
+
+ // Parse the file
+ std::vector<float> raw;
+
+ int size3d[] = { 0, 0, 0 };
+ bool in3d = false;
+
+ {
+ 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]) == "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 .itx lut.");
+ }
+ size3d[0] = size;
+ size3d[1] = size;
+ size3d[2] = size;
+
+ raw.reserve(3*size3d[0]*size3d[1]*size3d[2]);
+ in3d = true;
+ }
+ else if(in3d)
+ {
+ // It must be a float triple!
+
+ if(!StringVecToFloatVec(tmpfloats, parts) || tmpfloats.size() != 3)
+ {
+ std::ostringstream os;
+ os << "Malformed color triples specified in Iridas .itx 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(in3d)
+ {
+ if(size3d[0]*size3d[1]*size3d[2] != static_cast<int>(raw.size()/3))
+ {
+ std::ostringstream os;
+ os << "Parse error in Iridas .itx 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
+ 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 .itx lut. ";
+ os << "Lut type (1D/3D) unspecified.";
+ throw Exception(os.str().c_str());
+ }
+
+ return cachedFile;
+ }
+
+ void LocalFileFormat::Write(const Baker & baker,
+ const std::string & formatName,
+ std::ostream & ostream) const
+ {
+ int DEFAULT_CUBE_SIZE = 64;
+
+ if(formatName != "iridas_itx")
+ {
+ std::ostringstream os;
+ os << "Unknown 3dl format name, '";
+ os << formatName << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ ConstConfigRcPtr config = baker.getConfig();
+
+ int cubeSize = baker.getCubeSize();
+ if(cubeSize==-1) cubeSize = DEFAULT_CUBE_SIZE;
+ cubeSize = std::max(2, cubeSize); // smallest cube is 2x2x2
+
+ std::vector<float> cubeData;
+ cubeData.resize(cubeSize*cubeSize*cubeSize*3);
+ GenerateIdentityLut3D(&cubeData[0], cubeSize, 3, LUT3DORDER_FAST_RED);
+ PackedImageDesc cubeImg(&cubeData[0], cubeSize*cubeSize*cubeSize, 1, 3);
+
+ // Apply our conversion from the input space to the output space.
+ ConstProcessorRcPtr inputToTarget;
+ std::string looks = baker.getLooks();
+ if (!looks.empty())
+ {
+ LookTransformRcPtr transform = LookTransform::Create();
+ transform->setLooks(looks.c_str());
+ transform->setSrc(baker.getInputSpace());
+ transform->setDst(baker.getTargetSpace());
+ inputToTarget = config->getProcessor(transform,
+ TRANSFORM_DIR_FORWARD);
+ }
+ else
+ {
+ inputToTarget = config->getProcessor(baker.getInputSpace(),
+ baker.getTargetSpace());
+ }
+ inputToTarget->apply(cubeImg);
+
+ // Write out the file.
+ // For for maximum compatibility with other apps, we will
+ // not utilize the shaper or output any metadata
+
+ ostream << "LUT_3D_SIZE " << cubeSize << "\n";
+ if(cubeSize < 2)
+ {
+ throw Exception("Internal cube size exception.");
+ }
+
+ // Set to a fixed 6 decimal precision
+ ostream.setf(std::ios::fixed, std::ios::floatfield);
+ ostream.precision(6);
+ for(int i=0; i<cubeSize*cubeSize*cubeSize; ++i)
+ {
+ float r = cubeData[3*i+0];
+ float g = cubeData[3*i+1];
+ float b = cubeData[3*i+2];
+ ostream << r << " " << g << " " << b << "\n";
+ }
+ ostream << "\n";
+ }
+
+
+ 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 .itx 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)
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else if(newDir == TRANSFORM_DIR_INVERSE)
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ }
+ }
+
+ FileFormat * CreateFileFormatIridasItx()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/FileFormatIridasLook.cpp b/src/core/FileFormatIridasLook.cpp
new file mode 100644
index 0000000..d53c641
--- /dev/null
+++ b/src/core/FileFormatIridasLook.cpp
@@ -0,0 +1,1356 @@
+/*
+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 <tinyxml.h>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Lut1DOp.h"
+#include "Lut3DOp.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+/*
+
+Iridas .look format
+
+An XML format containing <shaders>, a series of layers describing the
+operations and their parameters (irrelevant to us in this context).
+
+This series of shaders is baked into the <LUT> section.
+
+<?xml version="1.0" ?>
+<look>
+ <shaders>
+ # anything in here is useless to us
+ </shaders>
+ <LUT>
+ <size>"8"</size> # Size of 3D LUT
+ <data>"
+ 0000008000000080000000802CF52E3D2DF52E3D2DF52E3D2CF5AE3D2DF5AE3D
+ # ...cut...
+ 5A216A3F5A216A3FAD10753FAD10753FAD10753F0000803F0000803F0000803F"
+ </data>
+ </LUT>
+</look>
+
+The LUT data contains a 3D LUT, as a hex-encoded series of 32-bit
+floats, with little-endian bit-ordering. LUT value ordering is
+LUT3DORDER_FAST_RED (red index incrementing fastest, then green, then
+blue)
+
+The LUT data is parsed by removing all whitespace and quotes. Taking 8
+characters at a time and intepreting as little-endian float, as follows:
+
+Given the string "0000003F0000803FAD10753F":
+
+ >>> import binascii, struct
+ >>> struct.unpack("<f", binascii.unhexlify("0000003F"))[0]
+ 0.5
+ >>> struct.unpack("<f", binascii.unhexlify("0000803F"))[0]
+ 1.0
+ >>> struct.unpack("<f", binascii.unhexlify("AD10753F"))[0]
+ 0.9572857022285461
+
+ */
+
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ // convert hex ascii to int
+ // return true on success, false on failure
+ bool hexasciitoint(char& ival, char character)
+ {
+ if(character>=48 && character<=57) // [0-9]
+ {
+ ival = static_cast<char>(character-48);
+ return true;
+ }
+ else if(character>=65 && character<=70) // [A-F]
+ {
+ ival = static_cast<char>(10+character-65);
+ return true;
+ }
+ else if(character>=97 && character<=102) // [a-f]
+ {
+ ival = static_cast<char>(10+character-97);
+ return true;
+ }
+
+ ival = 0;
+ return false;
+ }
+
+ // convert array of 8 hex ascii to f32
+ // The input hexascii is required to be a little-endian representation
+ // as used in the iridas file format
+ // "AD10753F" -> 0.9572857022285461f on ALL architectures
+
+ bool hexasciitofloat(float& fval, const char * ascii)
+ {
+ // Convert all ASCII numbers to their numerical representations
+ char asciinums[8];
+ for(unsigned int i=0; i<8; ++i)
+ {
+ if(!hexasciitoint(asciinums[i], ascii[i]))
+ {
+ return false;
+ }
+ }
+
+ unsigned char * fvalbytes = reinterpret_cast<unsigned char *>(&fval);
+
+#if OCIO_LITTLE_ENDIAN
+ // Since incoming values are little endian, and we're on little endian
+ // preserve the byte order
+ fvalbytes[0] = (unsigned char) (asciinums[1] | (asciinums[0] << 4));
+ fvalbytes[1] = (unsigned char) (asciinums[3] | (asciinums[2] << 4));
+ fvalbytes[2] = (unsigned char) (asciinums[5] | (asciinums[4] << 4));
+ fvalbytes[3] = (unsigned char) (asciinums[7] | (asciinums[6] << 4));
+#else
+ // Since incoming values are little endian, and we're on big endian
+ // flip the byte order
+ fvalbytes[3] = (unsigned char) (asciinums[1] | (asciinums[0] << 4));
+ fvalbytes[2] = (unsigned char) (asciinums[3] | (asciinums[2] << 4));
+ fvalbytes[1] = (unsigned char) (asciinums[5] | (asciinums[4] << 4));
+ fvalbytes[0] = (unsigned char) (asciinums[7] | (asciinums[6] << 4));
+#endif
+ return true;
+ }
+ }
+
+ namespace
+ {
+ class LocalCachedFile : public CachedFile
+ {
+ public:
+ LocalCachedFile ()
+ {
+ lut3D = Lut3D::Create();
+ };
+ ~LocalCachedFile() {};
+
+ Lut3DRcPtr lut3D;
+ };
+
+ typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr;
+ typedef OCIO_SHARED_PTR<TiXmlDocument> TiXmlDocumentRcPtr;
+
+
+ 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_look";
+ info.extension = "look";
+ info.capabilities = FORMAT_CAPABILITY_READ;
+ formatInfoVec.push_back(info);
+ }
+
+ CachedFileRcPtr
+ LocalFileFormat::Read(std::istream & istream) const
+ {
+
+ // Get root element from XML file
+ TiXmlDocumentRcPtr doc;
+ TiXmlElement* rootElement;
+
+ {
+ std::ostringstream rawdata;
+ rawdata << istream.rdbuf();
+
+ doc = TiXmlDocumentRcPtr(new TiXmlDocument());
+ doc->Parse(rawdata.str().c_str());
+
+ if(doc->Error())
+ {
+ std::ostringstream os;
+ os << "XML Parse Error. ";
+ os << doc->ErrorDesc() << " (line ";
+ os << doc->ErrorRow() << ", character ";
+ os << doc->ErrorCol() << ")";
+ throw Exception(os.str().c_str());
+ }
+
+ // Check for blank file
+ rootElement = doc->RootElement();
+ if(!rootElement)
+ {
+ std::ostringstream os;
+ os << "Error loading xml. Null root element.";
+ throw Exception(os.str().c_str());
+ }
+ }
+
+ // Check root element is <look>
+ if(std::string(rootElement->Value()) != "look")
+ {
+ std::ostringstream os;
+ os << "Error loading .look LUT. ";
+ os << "Root element is type '" << rootElement->Value() << "', ";
+ os << "expected 'look'.";
+ throw Exception(os.str().c_str());
+ }
+
+ // Fail to load file if it contains a <mask> section
+ if(rootElement->FirstChild("mask") && rootElement->FirstChild("mask")->FirstChild())
+ {
+ // If root element contains "mask" child, and it is
+ // not empty, throw exception
+ std::ostringstream os;
+ os << "Cannot load .look LUT containing mask";
+ throw Exception(os.str().c_str());
+ }
+
+ // Get <LUT> section
+
+ // TODO: There is a LUT1D section in some .look files,
+ // which we could use if available. Need to check
+ // assumption that it is only written for 1D transforms,
+ // and it matches the desired output
+ TiXmlNode* lutsection = rootElement->FirstChild("LUT");
+
+ if(!lutsection)
+ {
+ std::ostringstream os;
+ os << "Error loading .look LUT. ";
+ os << "Could not find required 'LUT' section.";
+ throw Exception(os.str().c_str());
+ }
+
+ // Get 3D LUT size
+ int size_3d = -1;
+
+ {
+ // Get size from <look><LUT><size>'123'</size></LUT></look>
+ TiXmlNode* elemsize = lutsection->FirstChild("size");
+ if(!elemsize)
+ {
+ std::ostringstream os;
+ os << "Error loading .look LUT. ";
+ os << "LUT section did not contain 'size'.";
+ throw Exception(os.str().c_str());
+ }
+
+ std::string size_raw = std::string(elemsize->ToElement()->GetText());
+ std::string size_clean = pystring::strip(size_raw, "'\" "); // strip quotes and space
+
+ char* endptr = 0;
+ size_3d = static_cast<int>(strtol(size_clean.c_str(), &endptr, 10));
+
+ if(*endptr)
+ {
+ // strtol didn't consume entire string, there was
+ // remaining data, thus did not contain a single interger
+ std::ostringstream os;
+ os << "Invalid LUT size value: '" << size_raw;
+ os << "'. Expected quoted integer.";
+ throw Exception(os.str().c_str());
+ }
+ }
+
+ // Grab raw 3D data
+ std::vector<float> raw;
+ {
+ TiXmlNode* dataelem = lutsection->FirstChild("data");
+ if(!dataelem)
+ {
+ std::ostringstream os;
+ os << "Error loading .look LUT. ";
+ os << "LUT section did not contain 'data'.";
+ throw Exception(os.str().c_str());
+ }
+
+ raw.reserve(3*(size_3d*size_3d*size_3d));
+
+ std::string what = dataelem->ToElement()->GetText();
+
+ // Remove spaces, quotes and newlines
+ what = pystring::replace(what, " ", "");
+ what = pystring::replace(what, "\"", "");
+ what = pystring::replace(what, "'", "");
+ what = pystring::replace(what, "\n", "");
+
+ if(what.size() % 8 != 0)
+ {
+ std::ostringstream os;
+ os << "Error loading .look LUT. ";
+ os << "Number of characters in 'data' must be multiple of 8. ";
+ os << what.size() << " elements found.";
+ throw Exception(os.str().c_str());
+ }
+
+ const char * ascii = what.c_str();
+ float fval = 0.0f;
+ for(unsigned int i=0; i<what.size()/8; ++i)
+ {
+ if(!hexasciitofloat(fval, &ascii[8*i]))
+ {
+ std::ostringstream os;
+ os << "Error loading .look LUT. ";
+ os << "Non-hex characters found in 'data' block ";
+ os << "at index '" << (8*i) << "'.";
+ throw Exception(os.str().c_str());
+ }
+ raw.push_back(fval);
+ }
+ }
+
+
+ // Validate LUT sizes, and create cached file object
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+
+ if((size_3d*size_3d*size_3d)*3 != static_cast<int>(raw.size()))
+ {
+ std::ostringstream os;
+ os << "Parse error in Iridas .look lut. ";
+ os << "Incorrect number of lut3d entries. ";
+ os << "Found " << raw.size() << " values, expected " << (size_3d*size_3d*size_3d)*3 << ".";
+ throw Exception(os.str().c_str());
+ }
+
+ // Reformat 3D data
+ cachedFile->lut3D->size[0] = size_3d;
+ cachedFile->lut3D->size[1] = size_3d;
+ cachedFile->lut3D->size[2] = size_3d;
+ cachedFile->lut3D->lut = raw;
+
+ 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 .look 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());
+ }
+
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ }
+
+ FileFormat * CreateFileFormatIridasLook()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+#include "UnitTest.h"
+
+OCIO_NAMESPACE_USING
+
+
+
+OIIO_ADD_TEST(FileFormatIridasLook, hexasciitoint)
+{
+ {
+ char ival = 0;
+ bool success = hexasciitoint(ival, 'a');
+ OIIO_CHECK_EQUAL(success, true); OIIO_CHECK_EQUAL(ival, 10);
+ }
+
+ {
+ char ival = 0;
+ bool success = hexasciitoint(ival, 'A');
+ OIIO_CHECK_EQUAL(success, true); OIIO_CHECK_EQUAL(ival, 10);
+ }
+
+ {
+ char ival = 0;
+ bool success = hexasciitoint(ival, 'f');
+ OIIO_CHECK_EQUAL(success, true); OIIO_CHECK_EQUAL(ival, 15);
+ }
+
+ {
+ char ival = 0;
+ bool success = hexasciitoint(ival, 'F');
+ OIIO_CHECK_EQUAL(success, true); OIIO_CHECK_EQUAL(ival, 15);
+ }
+
+ {
+ char ival = 0;
+ bool success = hexasciitoint(ival, '0');
+ OIIO_CHECK_EQUAL(success, true); OIIO_CHECK_EQUAL(ival, 0);
+ }
+
+ {
+ char ival = 0;
+ bool success = hexasciitoint(ival, '0');
+ OIIO_CHECK_EQUAL(success, true); OIIO_CHECK_EQUAL(ival, 0);
+ }
+
+ {
+ char ival = 0;
+ bool success = hexasciitoint(ival, '9');
+ OIIO_CHECK_EQUAL(success, true); OIIO_CHECK_EQUAL(ival, 9);
+ }
+
+ {
+ char ival = 0;
+ bool success = hexasciitoint(ival, '\n');
+ OIIO_CHECK_EQUAL(success, false);
+ }
+
+ {
+ char ival = 0;
+ bool success = hexasciitoint(ival, 'j');
+ OIIO_CHECK_EQUAL(success, false);
+ }
+
+ {
+ char ival = 0;
+ bool success = hexasciitoint(ival, 'x');
+ OIIO_CHECK_EQUAL(success, false);
+ }
+}
+
+
+OIIO_ADD_TEST(FileFormatIridasLook, hexasciitofloat)
+{
+ //>>> import binascii, struct
+ //>> struct.unpack("<f", binascii.unhexlify("AD10753F"))[0]
+ //0.9572857022285461
+
+ {
+ float fval = 0.0f;
+ bool success = hexasciitofloat(fval, "0000003F");
+ OIIO_CHECK_EQUAL(success, true); OIIO_CHECK_EQUAL(fval, 0.5f);
+ }
+
+ {
+ float fval = 0.0f;
+ bool success = hexasciitofloat(fval, "0000803F");
+ OIIO_CHECK_EQUAL(success, true); OIIO_CHECK_EQUAL(fval, 1.0f);
+ }
+
+ {
+ float fval = 0.0f;
+ bool success = hexasciitofloat(fval, "AD10753F");
+ OIIO_CHECK_EQUAL(success, true); OIIO_CHECK_EQUAL(fval, 0.9572857022285461f);
+ }
+
+ {
+ float fval = 0.0f;
+ bool success = hexasciitofloat(fval, "AD10X53F");
+ OIIO_CHECK_EQUAL(success, false);
+ }
+}
+
+
+OIIO_ADD_TEST(FileFormatIridasLook, simple3d)
+{
+ std::ostringstream strebuf;
+ strebuf << "<?xml version=\"1.0\" ?>" << "\n";
+ strebuf << "<look>" << "\n";
+ strebuf << " <shaders>" << "\n";
+ strebuf << " <base>" << "\n";
+ strebuf << " <visible>\"1\"</visible>" << "\n";
+ strebuf << " <sublayer0>" << "\n";
+ strebuf << " <opacity>\"1\"</opacity>" << "\n";
+ strebuf << " <parameters>" << "\n";
+ strebuf << " <Secondary1>\"1\"</Secondary1>" << "\n";
+ strebuf << " <Secondary5>\"0\"</Secondary5>" << "\n";
+ strebuf << " <Secondary4>\"0\"</Secondary4>" << "\n";
+ strebuf << " <Secondary2>\"0\"</Secondary2>" << "\n";
+ strebuf << " <Secondary6>\"0\"</Secondary6>" << "\n";
+ strebuf << " <Secondary3>\"0\"</Secondary3>" << "\n";
+ strebuf << " <Blur>\"0\"</Blur>" << "\n";
+ strebuf << " <saturation>\"0\"</saturation>" << "\n";
+ strebuf << " </parameters>" << "\n";
+ strebuf << " </sublayer0>" << "\n";
+ strebuf << " </base>" << "\n";
+ strebuf << " </shaders>" << "\n";
+ strebuf << " <LUT>" << "\n";
+ strebuf << " <size>\"8\"</size>" << "\n";
+ strebuf << " <data>\"" << "\n";
+ strebuf << " 0000008000000080000000802CF52E3D2DF52E3D2DF52E3D2CF5AE3D2DF5AE3D" << "\n";
+ strebuf << " 2DF5AE3DE237033EE237033EE237033E2CF52E3E2DF52E3E2DF52E3E78B25A3E" << "\n";
+ strebuf << " 78B25A3E78B25A3EE037833EE137833EE137833E8616993E8716993E8716993E" << "\n";
+ strebuf << " 4BBDAB3D4BBDAB3D4BBDAB3DF09B013EF09B013EF09B013E3C592D3E3C592D3E" << "\n";
+ strebuf << " 3C592D3E8716593E8716593E8716593EE969823EE969823EE969823E8E48983E" << "\n";
+ strebuf << " 8E48983E8E48983E3227AE3E3327AE3E3327AE3ED805C43ED905C43ED905C43E" << "\n";
+ strebuf << " 4BBD2B3E4BBD2B3E4BBD2B3E967A573E967A573E967A573EF09B813EF09B813E" << "\n";
+ strebuf << " F09B813E967A973E967A973E967A973E3C59AD3E3C59AD3E3C59AD3EE137C33E" << "\n";
+ strebuf << " E137C33EE137C33E8616D93E8616D93E8616D93E2CF5EE3E2CF5EE3E2CF5EE3E" << "\n";
+ strebuf << " F9CD803EF9CD803EF9CD803E9EAC963E9EAC963E9EAC963E448BAC3E448BAC3E" << "\n";
+ strebuf << " 448BAC3EEA69C23EEA69C23EEA69C23E8F48D83E8F48D83E8F48D83E3527EE3E" << "\n";
+ strebuf << " 3527EE3E3527EE3EED02023FED02023FED02023F40F20C3F40F20C3F40F20C3F" << "\n";
+ strebuf << " 4BBDAB3E4BBDAB3E4BBDAB3EF09BC13EF09BC13EF09BC13E967AD73E967AD73E" << "\n";
+ strebuf << " 967AD73E3C59ED3E3C59ED3E3C59ED3EF09B013FF09B013FF09B013F438B0C3F" << "\n";
+ strebuf << " 438B0C3F438B0C3F967A173F967A173F967A173FE969223FE969223FE969223F" << "\n";
+ strebuf << " 9EACD63E9EACD63E9EACD63E428BEC3E438BEC3E438BEC3EF434013FF434013F" << "\n";
+ strebuf << " F434013F47240C3F47240C3F47240C3F9A13173F9A13173F9A13173FED02223F" << "\n";
+ strebuf << " ED02223FED02223F3FF22C3F3FF22C3F3FF22C3F92E1373F92E1373F92E1373F" << "\n";
+ strebuf << " F8CD003FF8CD003FF8CD003F49BD0B3F4ABD0B3F4ABD0B3F9DAC163F9DAC163F" << "\n";
+ strebuf << " 9DAC163FF09B213FF09B213FF09B213F438B2C3F438B2C3F438B2C3F967A373F" << "\n";
+ strebuf << " 967A373F967A373FE869423FE869423FE869423F3B594D3F3B594D3F3B594D3F" << "\n";
+ strebuf << " A245163FA245163FA245163FF334213FF434213FF434213F47242C3F47242C3F" << "\n";
+ strebuf << " 47242C3F9A13373F9A13373F9A13373FED02423FED02423FED02423F40F24C3F" << "\n";
+ strebuf << " 40F24C3F40F24C3F92E1573F92E1573F92E1573FE5D0623FE5D0623FE5D0623F" << "\n";
+ strebuf << " 9E69853C9E69853C9869853CFCA9713DFCA9713DFCA9713D944FD03D944FD03D" << "\n";
+ strebuf << " 944FD03D14E5133E15E5133E15E5133E60A23F3E60A23F3E60A23F3EAA5F6B3E" << "\n";
+ strebuf << " AB5F6B3EAB5F6B3E7A8E8B3E7A8E8B3E7A8E8B3E206DA13E206DA13E206DA13E" << "\n";
+ strebuf << " B217CD3DB217CD3DB217CD3D2449123E2449123E2449123E6F063E3E6F063E3E" << "\n";
+ strebuf << " 6F063E3EBAC3693EBAC3693EBAC3693E82C08A3E82C08A3E82C08A3E289FA03E" << "\n";
+ strebuf << " 289FA03E289FA03ECC7DB63ECC7DB63ECC7DB63E725CCC3E715CCC3E715CCC3E" << "\n";
+ strebuf << " 7E6A3C3E7E6A3C3E7E6A3C3ECA27683ECA27683ECA27683E8AF2893E8AF2893E" << "\n";
+ strebuf << " 8AF2893E30D19F3E30D19F3E30D19F3ED5AFB53ED5AFB53ED5AFB53E7B8ECB3E" << "\n";
+ strebuf << " 7B8ECB3E7A8ECB3E1F6DE13E1F6DE13E1E6DE13EC44BF73EC54BF73EC44BF73E" << "\n";
+ strebuf << " 9224893E9224893E9224893E38039F3E38039F3E38039F3EDEE1B43EDEE1B43E" << "\n";
+ strebuf << " DEE1B43E83C0CA3E83C0CA3E82C0CA3E299FE03E299FE03E289FE03ECE7DF63E" << "\n";
+ strebuf << " CE7DF63ECD7DF63E392E063F392E063F382E063F8C1D113F8C1D113F8B1D113F" << "\n";
+ strebuf << " E413B43EE413B43EE413B43E89F2C93E8AF2C93E89F2C93E30D1DF3E30D1DF3E" << "\n";
+ strebuf << " 2FD1DF3ED5AFF53ED5AFF53ED4AFF53E3DC7053F3DC7053F3CC7053F90B6103F" << "\n";
+ strebuf << " 90B6103F8FB6103FE2A51B3FE2A51B3FE1A51B3F3595263F3595263F3495263F" << "\n";
+ strebuf << " 3703DF3E3703DF3E3603DF3EDCE1F43EDDE1F43EDCE1F43E4160053F4160053F" << "\n";
+ strebuf << " 4060053F944F103F944F103F934F103FE73E1B3FE73E1B3FE63E1B3F392E263F" << "\n";
+ strebuf << " 392E263F382E263F8C1D313F8C1D313F8B1D313FDF0C3C3FDF0C3C3FDE0C3C3F" << "\n";
+ strebuf << " 44F9043F44F9043F43F9043F96E80F3F97E80F3F96E80F3FEAD71A3FEAD71A3F" << "\n";
+ strebuf << " E9D71A3F3DC7253F3DC7253F3CC7253F90B6303F90B6303F8FB6303FE2A53B3F" << "\n";
+ strebuf << " E2A53B3FE1A53B3F3595463F3595463F3495463F8884513F8884513F8784513F" << "\n";
+ strebuf << " EE701A3FEE701A3FED701A3F4060253F4160253F4060253F944F303F944F303F" << "\n";
+ strebuf << " 934F303FE73E3B3FE73E3B3FE63E3B3F3A2E463F3A2E463F392E463F8C1D513F" << "\n";
+ strebuf << " 8C1D513F8B1D513FDF0C5C3FDF0C5C3FDE0C5C3F32FC663F32FC663F31FC663F" << "\n";
+ strebuf << " 9E69053D9E69053D9869053D652F9A3D652F9A3D642F9A3DFCA9F13DFCA9F13D" << "\n";
+ strebuf << " FCA9F13D4892243E4992243E4992243E944F503E944F503E944F503EDE0C7C3E" << "\n";
+ strebuf << " DF0C7C3EDF0C7C3E14E5933E14E5933E14E5933EBAC3A93EBAC3A93EBAC3A93E" << "\n";
+ strebuf << " 1A72EE3D1A72EE3D1A72EE3D58F6223E58F6223E58F6223EA3B34E3EA3B34E3E" << "\n";
+ strebuf << " A3B34E3EEE707A3EEE707A3EEE707A3E1C17933E1C17933E1C17933EC2F5A83E" << "\n";
+ strebuf << " C2F5A83EC2F5A83E66D4BE3E66D4BE3E66D4BE3E0CB3D43E0BB3D43E0CB3D43E" << "\n";
+ strebuf << " B2174D3EB2174D3EB2174D3EFDD4783EFDD4783EFDD4783E2449923E2449923E" << "\n";
+ strebuf << " 2449923ECA27A83ECA27A83ECA27A83E6F06BE3E6F06BE3E6F06BE3E15E5D33E" << "\n";
+ strebuf << " 15E5D33E15E5D33EB9C3E93EB9C3E93EB9C3E93E5EA2FF3E5FA2FF3E5FA2FF3E" << "\n";
+ strebuf << " 2C7B913E2C7B913E2C7B913ED259A73ED259A73ED259A73E7838BD3E7838BD3E" << "\n";
+ strebuf << " 7838BD3E1D17D33E1D17D33E1D17D33EC3F5E83EC3F5E83EC3F5E83E68D4FE3E" << "\n";
+ strebuf << " 68D4FE3E68D4FE3E86590A3F86590A3F86590A3FD948153FD948153FD948153F" << "\n";
+ strebuf << " 7E6ABC3E7E6ABC3E7E6ABC3E2349D23E2449D23E2449D23ECA27E83ECA27E83E" << "\n";
+ strebuf << " CA27E83E6F06FE3E6F06FE3E6F06FE3E8AF2093F8AF2093F8AF2093FDDE1143F" << "\n";
+ strebuf << " DDE1143FDDE1143F2FD11F3F2FD11F3F2FD11F3F82C02A3F82C02A3F82C02A3F" << "\n";
+ strebuf << " D159E73ED159E73ED159E73E7638FD3E7738FD3E7738FD3E8E8B093F8E8B093F" << "\n";
+ strebuf << " 8E8B093FE17A143FE17A143FE17A143F346A1F3F346A1F3F346A1F3F86592A3F" << "\n";
+ strebuf << " 86592A3F86592A3FD948353FD948353FD948353F2C38403F2C38403F2C38403F" << "\n";
+ strebuf << " 9124093F9124093F9124093FE313143FE413143FE413143F37031F3F37031F3F" << "\n";
+ strebuf << " 37031F3F8AF2293F8AF2293F8AF2293FDDE1343FDDE1343FDDE1343F2FD13F3F" << "\n";
+ strebuf << " 2FD13F3F2FD13F3F82C04A3F82C04A3F81C04A3FD5AF553FD5AF553FD4AF553F" << "\n";
+ strebuf << " 3B9C1E3F3B9C1E3F3B9C1E3F8D8B293F8E8B293F8E8B293FE17A343FE17A343F" << "\n";
+ strebuf << " E17A343F346A3F3F346A3F3F346A3F3F87594A3F87594A3F86594A3FD948553F" << "\n";
+ strebuf << " D948553FD848553F2C38603F2C38603F2B38603F7F276B3F7F276B3F7E276B3F" << "\n";
+ strebuf << " 6E1E483D6E1E483D681E483DCD89BB3DCD89BB3DCC89BB3D3282093E3282093E" << "\n";
+ strebuf << " 3282093E7C3F353E7D3F353E7C3F353EC8FC603EC8FC603EC8FC603E095D863E" << "\n";
+ strebuf << " 095D863E095D863EAE3B9C3EAE3B9C3EAE3B9C3E541AB23E541AB23E541AB23E" << "\n";
+ strebuf << " 41E6073E41E6073E40E6073E8CA3333E8CA3333E8CA3333ED7605F3ED7605F3E" << "\n";
+ strebuf << " D7605F3E118F853E118F853E118F853EB66D9B3EB66D9B3EB66D9B3E5B4CB13E" << "\n";
+ strebuf << " 5B4CB13E5B4CB13E002BC73E002BC73E002BC73EA609DD3EA509DD3EA609DD3E" << "\n";
+ strebuf << " E6C45D3EE6C45D3EE6C45D3E18C1843E18C1843E18C1843EBE9F9A3EBE9F9A3E" << "\n";
+ strebuf << " BE9F9A3E647EB03E647EB03E647EB03E095DC63E095DC63E095DC63EAE3BDC3E" << "\n";
+ strebuf << " AE3BDC3EAE3BDC3E531AF23E531AF23E531AF23E7CFC033F7CFC033F7CFC033F" << "\n";
+ strebuf << " C6D1993EC6D1993EC6D1993E6CB0AF3E6CB0AF3E6CB0AF3E128FC53E128FC53E" << "\n";
+ strebuf << " 128FC53EB76DDB3EB76DDB3EB76DDB3E5D4CF13E5D4CF13E5D4CF13E8195033F" << "\n";
+ strebuf << " 8195033F8195033FD3840E3FD3840E3FD3840E3F2674193F2674193F2674193F" << "\n";
+ strebuf << " 18C1C43E18C1C43E18C1C43EBD9FDA3EBE9FDA3EBE9FDA3E647EF03E647EF03E" << "\n";
+ strebuf << " 647EF03E842E033F842E033F842E033FD71D0E3FD71D0E3FD71D0E3F2A0D193F" << "\n";
+ strebuf << " 2A0D193F2A0D193F7CFC233F7CFC233F7CFC233FCFEB2E3FCFEB2E3FCFEB2E3F" << "\n";
+ strebuf << " 6BB0EF3E6BB0EF3E6BB0EF3E87C7023F88C7023F88C7023FDBB60D3FDBB60D3F" << "\n";
+ strebuf << " DBB60D3F2EA6183F2EA6183F2EA6183F8195233F8195233F8195233FD3842E3F" << "\n";
+ strebuf << " D3842E3FD3842E3F2674393F2674393F2674393F7963443F7963443F7963443F" << "\n";
+ strebuf << " DE4F0D3FDE4F0D3FDE4F0D3F303F183F313F183F313F183F842E233F842E233F" << "\n";
+ strebuf << " 842E233FD71D2E3FD71D2E3FD71D2E3F2A0D393F2A0D393F2A0D393F7CFC433F" << "\n";
+ strebuf << " 7CFC433F7CFC433FCFEB4E3FCFEB4E3FCFEB4E3F22DB593F22DB593F22DB593F" << "\n";
+ strebuf << " 88C7223F88C7223F88C7223FDAB62D3FDBB62D3FDBB62D3F2EA6383F2EA6383F" << "\n";
+ strebuf << " 2EA6383F8195433F8195433F8195433FD4844E3FD4844E3FD4844E3F2674593F" << "\n";
+ strebuf << " 2674593F2674593F7963643F7963643F7963643FCC526F3FCC526F3FCC526F3F" << "\n";
+ strebuf << " 9E69853D9E69853D9869853D34E4DC3D34E4DC3D34E4DC3D652F1A3E652F1A3E" << "\n";
+ strebuf << " 642F1A3EB1EC453EB1EC453EB0EC453EFCA9713EFCA9713EFCA9713EA3B38E3E" << "\n";
+ strebuf << " A3B38E3EA3B38E3E4892A43E4892A43E4892A43EEE70BA3EEE70BA3EEE70BA3E" << "\n";
+ strebuf << " 7493183E7493183E7493183EBF50443EBF50443EBE50443E0A0E703E0A0E703E" << "\n";
+ strebuf << " 0A0E703EABE58D3EABE58D3EABE58D3E50C4A33E50C4A33E50C4A33EF5A2B93E" << "\n";
+ strebuf << " F5A2B93EF5A2B93E9A81CF3E9981CF3E9A81CF3E4060E53E3F60E53E4060E53E" << "\n";
+ strebuf << " 1A726E3E1A726E3E1A726E3EB2178D3EB2178D3EB2178D3E58F6A23E58F6A23E" << "\n";
+ strebuf << " 58F6A23EFED4B83EFED4B83EFED4B83EA3B3CE3EA3B3CE3EA3B3CE3E4892E43E" << "\n";
+ strebuf << " 4892E43E4892E43EED70FA3EED70FA3EED70FA3EC927083FC927083FC927083F" << "\n";
+ strebuf << " 6028A23E6028A23E6028A23E0607B83E0607B83E0607B83EABE5CD3EABE5CD3E" << "\n";
+ strebuf << " ABE5CD3E51C4E33E51C4E33E51C4E33EF7A2F93EF7A2F93EF7A2F93ECEC0073F" << "\n";
+ strebuf << " CEC0073FCEC0073F20B0123F20B0123F20B0123F739F1D3F739F1D3F739F1D3F" << "\n";
+ strebuf << " B217CD3EB217CD3EB217CD3E57F6E23E58F6E23E58F6E23EFDD4F83EFDD4F83E" << "\n";
+ strebuf << " FDD4F83ED159073FD159073FD159073F2449123F2449123F2449123F77381D3F" << "\n";
+ strebuf << " 77381D3F77381D3FC927283FC927283FC927283F1C17333F1C17333F1C17333F" << "\n";
+ strebuf << " 0507F83E0507F83E0507F83ED4F2063FD5F2063FD5F2063F28E2113F28E2113F" << "\n";
+ strebuf << " 28E2113F7BD11C3F7BD11C3F7BD11C3FCEC0273FCEC0273FCEC0273F20B0323F" << "\n";
+ strebuf << " 20B0323F20B0323F739F3D3F739F3D3F739F3D3FC68E483FC68E483FC68E483F" << "\n";
+ strebuf << " 2B7B113F2B7B113F2B7B113F7D6A1C3F7E6A1C3F7E6A1C3FD159273FD159273F" << "\n";
+ strebuf << " D159273F2449323F2449323F2449323F77383D3F77383D3F77383D3FC927483F" << "\n";
+ strebuf << " C927483FC927483F1C17533F1C17533F1C17533F6F065E3F6F065E3F6F065E3F" << "\n";
+ strebuf << " D5F2263FD5F2263FD5F2263F27E2313F28E2313F28E2313F7BD13C3F7BD13C3F" << "\n";
+ strebuf << " 7BD13C3FCEC0473FCEC0473FCEC0473F21B0523F21B0523F21B0523F739F5D3F" << "\n";
+ strebuf << " 739F5D3F739F5D3FC68E683FC68E683FC68E683F197E733F197E733F197E733F" << "\n";
+ strebuf << " 06C4A63D06C4A63D00C4A63D9C3EFE3D9C3EFE3D983EFE3D99DC2A3E99DC2A3E" << "\n";
+ strebuf << " 98DC2A3EE599563EE599563EE499563E982B813E982B813E982B813E3D0A973E" << "\n";
+ strebuf << " 3D0A973E3D0A973EE2E8AC3EE2E8AC3EE2E8AC3E88C7C23E88C7C23E88C7C23E" << "\n";
+ strebuf << " A840293EA840293EA840293EF3FD543EF3FD543EF0FD543E9F5D803E9F5D803E" << "\n";
+ strebuf << " 9F5D803E453C963E453C963E453C963EEA1AAC3EEA1AAC3EEA1AAC3E8FF9C13E" << "\n";
+ strebuf << " 8FF9C13E8FF9C13E34D8D73E33D8D73E34D8D73EDAB6ED3ED9B6ED3EDAB6ED3E" << "\n";
+ strebuf << " 4E1F7F3E4E1F7F3E4E1F7F3E4C6E953E4C6E953E4C6E953EF24CAB3EF24CAB3E" << "\n";
+ strebuf << " F24CAB3E982BC13E982BC13E982BC13E3D0AD73E3D0AD73E3D0AD73EE2E8EC3E" << "\n";
+ strebuf << " E2E8EC3EE2E8EC3EC363013FC363013FC363013F16530C3F16530C3F16530C3F" << "\n";
+ strebuf << " FA7EAA3EFA7EAA3EFA7EAA3EA05DC03EA05DC03EA05DC03E453CD63E453CD63E" << "\n";
+ strebuf << " 453CD63EEB1AEC3EEB1AEC3EEB1AEC3EC8FC003FC8FC003FC8FC003F1BEC0B3F" << "\n";
+ strebuf << " 1BEC0B3F1BEC0B3F6DDB163F6DDB163F6DDB163FC0CA213FC0CA213FC0CA213F" << "\n";
+ strebuf << " 4C6ED53E4C6ED53E4C6ED53EF14CEB3EF24CEB3EF24CEB3ECB95003FCB95003F" << "\n";
+ strebuf << " CB95003F1E850B3F1E850B3F1E850B3F7174163F7174163F7174163FC463213F" << "\n";
+ strebuf << " C463213FC463213F16532C3F16532C3F16532C3F6942373F6942373F6942373F" << "\n";
+ strebuf << " CF2E003FCF2E003FCF2E003F211E0B3F221E0B3F221E0B3F750D163F750D163F" << "\n";
+ strebuf << " 750D163FC8FC203FC8FC203FC8FC203F1BEC2B3F1BEC2B3F1BEC2B3F6DDB363F" << "\n";
+ strebuf << " 6DDB363F6DDB363FC0CA413FC0CA413FC0CA413F13BA4C3F13BA4C3F13BA4C3F" << "\n";
+ strebuf << " 78A6153F78A6153F78A6153FCA95203FCB95203FCB95203F1E852B3F1E852B3F" << "\n";
+ strebuf << " 1E852B3F7174363F7174363F7174363FC463413FC463413FC463413F16534C3F" << "\n";
+ strebuf << " 16534C3F16534C3F6942573F6942573F6942573FBC31623FBC31623FBC31623F" << "\n";
+ strebuf << " 221E2B3F221E2B3F221E2B3F740D363F750D363F750D363FC8FC403FC8FC403F" << "\n";
+ strebuf << " C8FC403F1BEC4B3F1BEC4B3F1BEC4B3F6EDB563F6EDB563F6EDB563FC0CA613F" << "\n";
+ strebuf << " C0CA613FC0CA613F13BA6C3F13BA6C3F13BA6C3F66A9773F66A9773F66A9773F" << "\n";
+ strebuf << " 6D1EC83D6D1EC83D681EC83D81CC0F3E81CC0F3E80CC0F3ECD893B3ECD893B3E" << "\n";
+ strebuf << " CC893B3E1847673E1847673E1847673E3182893E3182893E3082893ED7609F3E" << "\n";
+ strebuf << " D7609F3ED6609F3E7C3FB53E7C3FB53E7C3FB53E221ECB3E221ECB3E221ECB3E" << "\n";
+ strebuf << " DCED393EDCED393EDCED393E26AB653E26AB653E24AB653E39B4883E39B4883E" << "\n";
+ strebuf << " 38B4883EDE929E3EDE929E3EDE929E3E8371B43E8371B43E8271B43E2950CA3E" << "\n";
+ strebuf << " 2850CA3E2950CA3ECE2EE03ECD2EE03ECE2EE03E740DF63E730DF63E740DF63E" << "\n";
+ strebuf << " 40E6873E40E6873E40E6873EE6C49D3EE6C49D3EE6C49D3E8CA3B33E8CA3B33E" << "\n";
+ strebuf << " 8CA3B33E3182C93E3182C93E3182C93ED660DF3ED660DF3ED660DF3E7C3FF53E" << "\n";
+ strebuf << " 7C3FF53E7C3FF53E108F053F108F053F108F053F637E103F637E103F637E103F" << "\n";
+ strebuf << " 94D5B23E94D5B23E94D5B23E39B4C83E39B4C83E39B4C83EDF92DE3EDF92DE3E" << "\n";
+ strebuf << " DF92DE3E8571F43E8571F43E8571F43E1528053F1528053F1528053F6817103F" << "\n";
+ strebuf << " 6817103F6817103FBA061B3FBA061B3FBA061B3F0DF6253F0DF6253F0DF6253F" << "\n";
+ strebuf << " E6C4DD3EE6C4DD3EE6C4DD3E8AA3F33E8BA3F33E8BA3F33E18C1043F18C1043F" << "\n";
+ strebuf << " 18C1043F6BB00F3F6BB00F3F6BB00F3FBE9F1A3FBE9F1A3FBE9F1A3F118F253F" << "\n";
+ strebuf << " 118F253F118F253F637E303F637E303F637E303FB66D3B3FB66D3B3FB66D3B3F" << "\n";
+ strebuf << " 1C5A043F1C5A043F1C5A043F6E490F3F6F490F3F6F490F3FC2381A3FC2381A3F" << "\n";
+ strebuf << " C2381A3F1528253F1528253F1528253F6717303F6717303F6717303FBA063B3F" << "\n";
+ strebuf << " BA063B3FBA063B3F0DF6453F0DF6453F0DF6453F60E5503F60E5503F60E5503F" << "\n";
+ strebuf << " C5D1193FC5D1193FC5D1193F17C1243F18C1243F18C1243F6BB02F3F6BB02F3F" << "\n";
+ strebuf << " 6BB02F3FBE9F3A3FBE9F3A3FBE9F3A3F108F453F108F453F108F453F637E503F" << "\n";
+ strebuf << " 637E503F637E503FB66D5B3FB66D5B3FB66D5B3F095D663F095D663F095D663F" << "\n";
+ strebuf << " 6F492F3F6F492F3F6F492F3FC1383A3FC2383A3FC2383A3F1528453F1528453F" << "\n";
+ strebuf << " 1528453F6817503F6817503F6817503FBA065B3FBA065B3FBA065B3F0DF6653F" << "\n";
+ strebuf << " 0DF6653F0DF6653F60E5703F60E5703F60E5703FB3D47B3FB3D47B3FB3D47B3F" << "\n";
+ strebuf << " D578E93DD578E93DD078E93DB579203EB579203EB479203E01374C3E01374C3E" << "\n";
+ strebuf << " 00374C3E4CF4773E4CF4773E4CF4773ECBD8913ECBD8913ECAD8913E71B7A73E" << "\n";
+ strebuf << " 71B7A73E70B7A73E1696BD3E1696BD3E1696BD3EBC74D33EBC74D33EBC74D33E" << "\n";
+ strebuf << " 109B4A3E109B4A3E109B4A3E5A58763E5A58763E5858763ED30A913ED30A913E" << "\n";
+ strebuf << " D20A913E78E9A63E78E9A63E78E9A63E1DC8BC3E1DC8BC3E1CC8BC3EC3A6D23E" << "\n";
+ strebuf << " C2A6D23EC2A6D23E6885E83E6785E83E6885E83E0E64FE3E0D64FE3E0E64FE3E" << "\n";
+ strebuf << " DA3C903EDA3C903EDA3C903E801BA63E801BA63E801BA63E26FABB3E26FABB3E" << "\n";
+ strebuf << " 26FABB3ECBD8D13ECBD8D13ECAD8D13E70B7E73E70B7E73E70B7E73E1696FD3E" << "\n";
+ strebuf << " 1696FD3E1696FD3E5DBA093F5DBA093F5DBA093FB0A9143FB0A9143FB0A9143F" << "\n";
+ strebuf << " 2E2CBB3E2E2CBB3E2E2CBB3ED20AD13ED30AD13ED20AD13E79E9E63E79E9E63E" << "\n";
+ strebuf << " 78E9E63E1FC8FC3E1FC8FC3E1EC8FC3E6253093F6253093F6253093FB542143F" << "\n";
+ strebuf << " B542143FB542143F07321F3F07321F3F07321F3F5A212A3F5A212A3F5A212A3F" << "\n";
+ strebuf << " 801BE63E801BE63E801BE63E24FAFB3E25FAFB3E24FAFB3E65EC083F65EC083F" << "\n";
+ strebuf << " 65EC083FB8DB133FB8DB133FB8DB133F0BCB1E3F0BCB1E3F0BCB1E3F5EBA293F" << "\n";
+ strebuf << " 5EBA293F5EBA293FB0A9343FB0A9343FB0A9343F03993F3F03993F3F03993F3F" << "\n";
+ strebuf << " 6985083F6985083F6985083FBB74133FBC74133FBC74133F0F641E3F0F641E3F" << "\n";
+ strebuf << " 0F641E3F6253293F6253293F6253293FB442343FB442343FB442343F07323F3F" << "\n";
+ strebuf << " 07323F3F07323F3F5A214A3F5A214A3F5A214A3FAD10553FAD10553FAD10553F" << "\n";
+ strebuf << " 12FD1D3F12FD1D3F12FD1D3F64EC283F65EC283F65EC283FB8DB333FB8DB333F" << "\n";
+ strebuf << " B8DB333F0BCB3E3F0BCB3E3F0BCB3E3F5DBA493F5DBA493F5DBA493FB0A9543F" << "\n";
+ strebuf << " B0A9543FB0A9543F03995F3F03995F3F03995F3F56886A3F56886A3F56886A3F" << "\n";
+ strebuf << " BC74333FBC74333FBC74333F0E643E3F0F643E3F0F643E3F6153493F6253493F" << "\n";
+ strebuf << " 6253493FB542543FB542543FB542543F07325F3F07325F3F07325F3F5A216A3F" << "\n";
+ strebuf << " 5A216A3F5A216A3FAD10753FAD10753FAD10753F0000803F0000803F0000803F\"" << "\n";
+ strebuf << " </data>" << "\n";
+ strebuf << " </LUT>" << "\n";
+ strebuf << "</look>" << "\n";
+
+ std::istringstream simple1D;
+ simple1D.str(strebuf.str());
+
+ // Read file
+ LocalFileFormat tester;
+ CachedFileRcPtr cachedFile = tester.Read(simple1D);
+ LocalCachedFileRcPtr looklut = DynamicPtrCast<LocalCachedFile>(cachedFile);
+
+ // Check LUT size is correct
+ OIIO_CHECK_EQUAL(looklut->lut3D->size[0], 8);
+ OIIO_CHECK_EQUAL(looklut->lut3D->size[1], 8);
+ OIIO_CHECK_EQUAL(looklut->lut3D->size[2], 8);
+
+ // Check LUT values
+ OIIO_CHECK_EQUAL(looklut->lut3D->lut.size(), 8*8*8*3);
+
+ double cube[8 * 8 * 8 * 3] = {
+ -0.00000, -0.00000, -0.00000,
+ 0.04271, 0.04271, 0.04271,
+ 0.08543, 0.08543, 0.08543,
+ 0.12814, 0.12814, 0.12814,
+ 0.17086, 0.17086, 0.17086,
+ 0.21357, 0.21357, 0.21357,
+ 0.25629, 0.25629, 0.25629,
+ 0.29900, 0.29900, 0.29900,
+ 0.08386, 0.08386, 0.08386,
+ 0.12657, 0.12657, 0.12657,
+ 0.16929, 0.16929, 0.16929,
+ 0.21200, 0.21200, 0.21200,
+ 0.25471, 0.25471, 0.25471,
+ 0.29743, 0.29743, 0.29743,
+ 0.34014, 0.34014, 0.34014,
+ 0.38286, 0.38286, 0.38286,
+ 0.16771, 0.16771, 0.16771,
+ 0.21043, 0.21043, 0.21043,
+ 0.25314, 0.25314, 0.25314,
+ 0.29586, 0.29586, 0.29586,
+ 0.33857, 0.33857, 0.33857,
+ 0.38129, 0.38129, 0.38129,
+ 0.42400, 0.42400, 0.42400,
+ 0.46671, 0.46671, 0.46671,
+ 0.25157, 0.25157, 0.25157,
+ 0.29429, 0.29429, 0.29429,
+ 0.33700, 0.33700, 0.33700,
+ 0.37971, 0.37971, 0.37971,
+ 0.42243, 0.42243, 0.42243,
+ 0.46514, 0.46514, 0.46514,
+ 0.50786, 0.50786, 0.50786,
+ 0.55057, 0.55057, 0.55057,
+ 0.33543, 0.33543, 0.33543,
+ 0.37814, 0.37814, 0.37814,
+ 0.42086, 0.42086, 0.42086,
+ 0.46357, 0.46357, 0.46357,
+ 0.50629, 0.50629, 0.50629,
+ 0.54900, 0.54900, 0.54900,
+ 0.59171, 0.59171, 0.59171,
+ 0.63443, 0.63443, 0.63443,
+ 0.41929, 0.41929, 0.41929,
+ 0.46200, 0.46200, 0.46200,
+ 0.50471, 0.50471, 0.50471,
+ 0.54743, 0.54743, 0.54743,
+ 0.59014, 0.59014, 0.59014,
+ 0.63286, 0.63286, 0.63286,
+ 0.67557, 0.67557, 0.67557,
+ 0.71829, 0.71829, 0.71829,
+ 0.50314, 0.50314, 0.50314,
+ 0.54586, 0.54586, 0.54586,
+ 0.58857, 0.58857, 0.58857,
+ 0.63129, 0.63129, 0.63129,
+ 0.67400, 0.67400, 0.67400,
+ 0.71671, 0.71671, 0.71671,
+ 0.75943, 0.75943, 0.75943,
+ 0.80214, 0.80214, 0.80214,
+ 0.58700, 0.58700, 0.58700,
+ 0.62971, 0.62971, 0.62971,
+ 0.67243, 0.67243, 0.67243,
+ 0.71514, 0.71514, 0.71514,
+ 0.75786, 0.75786, 0.75786,
+ 0.80057, 0.80057, 0.80057,
+ 0.84329, 0.84329, 0.84329,
+ 0.88600, 0.88600, 0.88600,
+ 0.01629, 0.01629, 0.01629,
+ 0.05900, 0.05900, 0.05900,
+ 0.10171, 0.10171, 0.10171,
+ 0.14443, 0.14443, 0.14443,
+ 0.18714, 0.18714, 0.18714,
+ 0.22986, 0.22986, 0.22986,
+ 0.27257, 0.27257, 0.27257,
+ 0.31529, 0.31529, 0.31529,
+ 0.10014, 0.10014, 0.10014,
+ 0.14286, 0.14286, 0.14286,
+ 0.18557, 0.18557, 0.18557,
+ 0.22829, 0.22829, 0.22829,
+ 0.27100, 0.27100, 0.27100,
+ 0.31371, 0.31371, 0.31371,
+ 0.35643, 0.35643, 0.35643,
+ 0.39914, 0.39914, 0.39914,
+ 0.18400, 0.18400, 0.18400,
+ 0.22671, 0.22671, 0.22671,
+ 0.26943, 0.26943, 0.26943,
+ 0.31214, 0.31214, 0.31214,
+ 0.35486, 0.35486, 0.35486,
+ 0.39757, 0.39757, 0.39757,
+ 0.44029, 0.44029, 0.44029,
+ 0.48300, 0.48300, 0.48300,
+ 0.26786, 0.26786, 0.26786,
+ 0.31057, 0.31057, 0.31057,
+ 0.35329, 0.35329, 0.35329,
+ 0.39600, 0.39600, 0.39600,
+ 0.43871, 0.43871, 0.43871,
+ 0.48143, 0.48143, 0.48143,
+ 0.52414, 0.52414, 0.52414,
+ 0.56686, 0.56686, 0.56686,
+ 0.35171, 0.35171, 0.35171,
+ 0.39443, 0.39443, 0.39443,
+ 0.43714, 0.43714, 0.43714,
+ 0.47986, 0.47986, 0.47986,
+ 0.52257, 0.52257, 0.52257,
+ 0.56529, 0.56529, 0.56529,
+ 0.60800, 0.60800, 0.60800,
+ 0.65071, 0.65071, 0.65071,
+ 0.43557, 0.43557, 0.43557,
+ 0.47829, 0.47829, 0.47829,
+ 0.52100, 0.52100, 0.52100,
+ 0.56371, 0.56371, 0.56371,
+ 0.60643, 0.60643, 0.60643,
+ 0.64914, 0.64914, 0.64914,
+ 0.69186, 0.69186, 0.69186,
+ 0.73457, 0.73457, 0.73457,
+ 0.51943, 0.51943, 0.51943,
+ 0.56214, 0.56214, 0.56214,
+ 0.60486, 0.60486, 0.60486,
+ 0.64757, 0.64757, 0.64757,
+ 0.69029, 0.69029, 0.69029,
+ 0.73300, 0.73300, 0.73300,
+ 0.77571, 0.77571, 0.77571,
+ 0.81843, 0.81843, 0.81843,
+ 0.60329, 0.60329, 0.60329,
+ 0.64600, 0.64600, 0.64600,
+ 0.68871, 0.68871, 0.68871,
+ 0.73143, 0.73143, 0.73143,
+ 0.77414, 0.77414, 0.77414,
+ 0.81686, 0.81686, 0.81686,
+ 0.85957, 0.85957, 0.85957,
+ 0.90229, 0.90229, 0.90229,
+ 0.03257, 0.03257, 0.03257,
+ 0.07529, 0.07529, 0.07529,
+ 0.11800, 0.11800, 0.11800,
+ 0.16071, 0.16071, 0.16071,
+ 0.20343, 0.20343, 0.20343,
+ 0.24614, 0.24614, 0.24614,
+ 0.28886, 0.28886, 0.28886,
+ 0.33157, 0.33157, 0.33157,
+ 0.11643, 0.11643, 0.11643,
+ 0.15914, 0.15914, 0.15914,
+ 0.20186, 0.20186, 0.20186,
+ 0.24457, 0.24457, 0.24457,
+ 0.28729, 0.28729, 0.28729,
+ 0.33000, 0.33000, 0.33000,
+ 0.37271, 0.37271, 0.37271,
+ 0.41543, 0.41543, 0.41543,
+ 0.20029, 0.20029, 0.20029,
+ 0.24300, 0.24300, 0.24300,
+ 0.28571, 0.28571, 0.28571,
+ 0.32843, 0.32843, 0.32843,
+ 0.37114, 0.37114, 0.37114,
+ 0.41386, 0.41386, 0.41386,
+ 0.45657, 0.45657, 0.45657,
+ 0.49929, 0.49929, 0.49929,
+ 0.28414, 0.28414, 0.28414,
+ 0.32686, 0.32686, 0.32686,
+ 0.36957, 0.36957, 0.36957,
+ 0.41229, 0.41229, 0.41229,
+ 0.45500, 0.45500, 0.45500,
+ 0.49771, 0.49771, 0.49771,
+ 0.54043, 0.54043, 0.54043,
+ 0.58314, 0.58314, 0.58314,
+ 0.36800, 0.36800, 0.36800,
+ 0.41071, 0.41071, 0.41071,
+ 0.45343, 0.45343, 0.45343,
+ 0.49614, 0.49614, 0.49614,
+ 0.53886, 0.53886, 0.53886,
+ 0.58157, 0.58157, 0.58157,
+ 0.62429, 0.62429, 0.62429,
+ 0.66700, 0.66700, 0.66700,
+ 0.45186, 0.45186, 0.45186,
+ 0.49457, 0.49457, 0.49457,
+ 0.53729, 0.53729, 0.53729,
+ 0.58000, 0.58000, 0.58000,
+ 0.62271, 0.62271, 0.62271,
+ 0.66543, 0.66543, 0.66543,
+ 0.70814, 0.70814, 0.70814,
+ 0.75086, 0.75086, 0.75086,
+ 0.53571, 0.53571, 0.53571,
+ 0.57843, 0.57843, 0.57843,
+ 0.62114, 0.62114, 0.62114,
+ 0.66386, 0.66386, 0.66386,
+ 0.70657, 0.70657, 0.70657,
+ 0.74929, 0.74929, 0.74929,
+ 0.79200, 0.79200, 0.79200,
+ 0.83471, 0.83471, 0.83471,
+ 0.61957, 0.61957, 0.61957,
+ 0.66229, 0.66229, 0.66229,
+ 0.70500, 0.70500, 0.70500,
+ 0.74771, 0.74771, 0.74771,
+ 0.79043, 0.79043, 0.79043,
+ 0.83314, 0.83314, 0.83314,
+ 0.87586, 0.87586, 0.87586,
+ 0.91857, 0.91857, 0.91857,
+ 0.04886, 0.04886, 0.04886,
+ 0.09157, 0.09157, 0.09157,
+ 0.13429, 0.13429, 0.13429,
+ 0.17700, 0.17700, 0.17700,
+ 0.21971, 0.21971, 0.21971,
+ 0.26243, 0.26243, 0.26243,
+ 0.30514, 0.30514, 0.30514,
+ 0.34786, 0.34786, 0.34786,
+ 0.13271, 0.13271, 0.13271,
+ 0.17543, 0.17543, 0.17543,
+ 0.21814, 0.21814, 0.21814,
+ 0.26086, 0.26086, 0.26086,
+ 0.30357, 0.30357, 0.30357,
+ 0.34629, 0.34629, 0.34629,
+ 0.38900, 0.38900, 0.38900,
+ 0.43171, 0.43171, 0.43171,
+ 0.21657, 0.21657, 0.21657,
+ 0.25929, 0.25929, 0.25929,
+ 0.30200, 0.30200, 0.30200,
+ 0.34471, 0.34471, 0.34471,
+ 0.38743, 0.38743, 0.38743,
+ 0.43014, 0.43014, 0.43014,
+ 0.47286, 0.47286, 0.47286,
+ 0.51557, 0.51557, 0.51557,
+ 0.30043, 0.30043, 0.30043,
+ 0.34314, 0.34314, 0.34314,
+ 0.38586, 0.38586, 0.38586,
+ 0.42857, 0.42857, 0.42857,
+ 0.47129, 0.47129, 0.47129,
+ 0.51400, 0.51400, 0.51400,
+ 0.55671, 0.55671, 0.55671,
+ 0.59943, 0.59943, 0.59943,
+ 0.38429, 0.38429, 0.38429,
+ 0.42700, 0.42700, 0.42700,
+ 0.46971, 0.46971, 0.46971,
+ 0.51243, 0.51243, 0.51243,
+ 0.55514, 0.55514, 0.55514,
+ 0.59786, 0.59786, 0.59786,
+ 0.64057, 0.64057, 0.64057,
+ 0.68329, 0.68329, 0.68329,
+ 0.46814, 0.46814, 0.46814,
+ 0.51086, 0.51086, 0.51086,
+ 0.55357, 0.55357, 0.55357,
+ 0.59629, 0.59629, 0.59629,
+ 0.63900, 0.63900, 0.63900,
+ 0.68171, 0.68171, 0.68171,
+ 0.72443, 0.72443, 0.72443,
+ 0.76714, 0.76714, 0.76714,
+ 0.55200, 0.55200, 0.55200,
+ 0.59471, 0.59471, 0.59471,
+ 0.63743, 0.63743, 0.63743,
+ 0.68014, 0.68014, 0.68014,
+ 0.72286, 0.72286, 0.72286,
+ 0.76557, 0.76557, 0.76557,
+ 0.80829, 0.80829, 0.80829,
+ 0.85100, 0.85100, 0.85100,
+ 0.63586, 0.63586, 0.63586,
+ 0.67857, 0.67857, 0.67857,
+ 0.72129, 0.72129, 0.72129,
+ 0.76400, 0.76400, 0.76400,
+ 0.80671, 0.80671, 0.80671,
+ 0.84943, 0.84943, 0.84943,
+ 0.89214, 0.89214, 0.89214,
+ 0.93486, 0.93486, 0.93486,
+ 0.06514, 0.06514, 0.06514,
+ 0.10786, 0.10786, 0.10786,
+ 0.15057, 0.15057, 0.15057,
+ 0.19329, 0.19329, 0.19329,
+ 0.23600, 0.23600, 0.23600,
+ 0.27871, 0.27871, 0.27871,
+ 0.32143, 0.32143, 0.32143,
+ 0.36414, 0.36414, 0.36414,
+ 0.14900, 0.14900, 0.14900,
+ 0.19171, 0.19171, 0.19171,
+ 0.23443, 0.23443, 0.23443,
+ 0.27714, 0.27714, 0.27714,
+ 0.31986, 0.31986, 0.31986,
+ 0.36257, 0.36257, 0.36257,
+ 0.40529, 0.40529, 0.40529,
+ 0.44800, 0.44800, 0.44800,
+ 0.23286, 0.23286, 0.23286,
+ 0.27557, 0.27557, 0.27557,
+ 0.31829, 0.31829, 0.31829,
+ 0.36100, 0.36100, 0.36100,
+ 0.40371, 0.40371, 0.40371,
+ 0.44643, 0.44643, 0.44643,
+ 0.48914, 0.48914, 0.48914,
+ 0.53186, 0.53186, 0.53186,
+ 0.31671, 0.31671, 0.31671,
+ 0.35943, 0.35943, 0.35943,
+ 0.40214, 0.40214, 0.40214,
+ 0.44486, 0.44486, 0.44486,
+ 0.48757, 0.48757, 0.48757,
+ 0.53029, 0.53029, 0.53029,
+ 0.57300, 0.57300, 0.57300,
+ 0.61571, 0.61571, 0.61571,
+ 0.40057, 0.40057, 0.40057,
+ 0.44329, 0.44329, 0.44329,
+ 0.48600, 0.48600, 0.48600,
+ 0.52871, 0.52871, 0.52871,
+ 0.57143, 0.57143, 0.57143,
+ 0.61414, 0.61414, 0.61414,
+ 0.65686, 0.65686, 0.65686,
+ 0.69957, 0.69957, 0.69957,
+ 0.48443, 0.48443, 0.48443,
+ 0.52714, 0.52714, 0.52714,
+ 0.56986, 0.56986, 0.56986,
+ 0.61257, 0.61257, 0.61257,
+ 0.65529, 0.65529, 0.65529,
+ 0.69800, 0.69800, 0.69800,
+ 0.74071, 0.74071, 0.74071,
+ 0.78343, 0.78343, 0.78343,
+ 0.56829, 0.56829, 0.56829,
+ 0.61100, 0.61100, 0.61100,
+ 0.65371, 0.65371, 0.65371,
+ 0.69643, 0.69643, 0.69643,
+ 0.73914, 0.73914, 0.73914,
+ 0.78186, 0.78186, 0.78186,
+ 0.82457, 0.82457, 0.82457,
+ 0.86729, 0.86729, 0.86729,
+ 0.65214, 0.65214, 0.65214,
+ 0.69486, 0.69486, 0.69486,
+ 0.73757, 0.73757, 0.73757,
+ 0.78029, 0.78029, 0.78029,
+ 0.82300, 0.82300, 0.82300,
+ 0.86571, 0.86571, 0.86571,
+ 0.90843, 0.90843, 0.90843,
+ 0.95114, 0.95114, 0.95114,
+ 0.08143, 0.08143, 0.08143,
+ 0.12414, 0.12414, 0.12414,
+ 0.16686, 0.16686, 0.16686,
+ 0.20957, 0.20957, 0.20957,
+ 0.25229, 0.25229, 0.25229,
+ 0.29500, 0.29500, 0.29500,
+ 0.33771, 0.33771, 0.33771,
+ 0.38043, 0.38043, 0.38043,
+ 0.16529, 0.16529, 0.16529,
+ 0.20800, 0.20800, 0.20800,
+ 0.25071, 0.25071, 0.25071,
+ 0.29343, 0.29343, 0.29343,
+ 0.33614, 0.33614, 0.33614,
+ 0.37886, 0.37886, 0.37886,
+ 0.42157, 0.42157, 0.42157,
+ 0.46429, 0.46429, 0.46429,
+ 0.24914, 0.24914, 0.24914,
+ 0.29186, 0.29186, 0.29186,
+ 0.33457, 0.33457, 0.33457,
+ 0.37729, 0.37729, 0.37729,
+ 0.42000, 0.42000, 0.42000,
+ 0.46271, 0.46271, 0.46271,
+ 0.50543, 0.50543, 0.50543,
+ 0.54814, 0.54814, 0.54814,
+ 0.33300, 0.33300, 0.33300,
+ 0.37571, 0.37571, 0.37571,
+ 0.41843, 0.41843, 0.41843,
+ 0.46114, 0.46114, 0.46114,
+ 0.50386, 0.50386, 0.50386,
+ 0.54657, 0.54657, 0.54657,
+ 0.58929, 0.58929, 0.58929,
+ 0.63200, 0.63200, 0.63200,
+ 0.41686, 0.41686, 0.41686,
+ 0.45957, 0.45957, 0.45957,
+ 0.50229, 0.50229, 0.50229,
+ 0.54500, 0.54500, 0.54500,
+ 0.58771, 0.58771, 0.58771,
+ 0.63043, 0.63043, 0.63043,
+ 0.67314, 0.67314, 0.67314,
+ 0.71586, 0.71586, 0.71586,
+ 0.50071, 0.50071, 0.50071,
+ 0.54343, 0.54343, 0.54343,
+ 0.58614, 0.58614, 0.58614,
+ 0.62886, 0.62886, 0.62886,
+ 0.67157, 0.67157, 0.67157,
+ 0.71429, 0.71429, 0.71429,
+ 0.75700, 0.75700, 0.75700,
+ 0.79971, 0.79971, 0.79971,
+ 0.58457, 0.58457, 0.58457,
+ 0.62729, 0.62729, 0.62729,
+ 0.67000, 0.67000, 0.67000,
+ 0.71271, 0.71271, 0.71271,
+ 0.75543, 0.75543, 0.75543,
+ 0.79814, 0.79814, 0.79814,
+ 0.84086, 0.84086, 0.84086,
+ 0.88357, 0.88357, 0.88357,
+ 0.66843, 0.66843, 0.66843,
+ 0.71114, 0.71114, 0.71114,
+ 0.75386, 0.75386, 0.75386,
+ 0.79657, 0.79657, 0.79657,
+ 0.83929, 0.83929, 0.83929,
+ 0.88200, 0.88200, 0.88200,
+ 0.92471, 0.92471, 0.92471,
+ 0.96743, 0.96743, 0.96743,
+ 0.09771, 0.09771, 0.09771,
+ 0.14043, 0.14043, 0.14043,
+ 0.18314, 0.18314, 0.18314,
+ 0.22586, 0.22586, 0.22586,
+ 0.26857, 0.26857, 0.26857,
+ 0.31129, 0.31129, 0.31129,
+ 0.35400, 0.35400, 0.35400,
+ 0.39671, 0.39671, 0.39671,
+ 0.18157, 0.18157, 0.18157,
+ 0.22429, 0.22429, 0.22429,
+ 0.26700, 0.26700, 0.26700,
+ 0.30971, 0.30971, 0.30971,
+ 0.35243, 0.35243, 0.35243,
+ 0.39514, 0.39514, 0.39514,
+ 0.43786, 0.43786, 0.43786,
+ 0.48057, 0.48057, 0.48057,
+ 0.26543, 0.26543, 0.26543,
+ 0.30814, 0.30814, 0.30814,
+ 0.35086, 0.35086, 0.35086,
+ 0.39357, 0.39357, 0.39357,
+ 0.43629, 0.43629, 0.43629,
+ 0.47900, 0.47900, 0.47900,
+ 0.52171, 0.52171, 0.52171,
+ 0.56443, 0.56443, 0.56443,
+ 0.34929, 0.34929, 0.34929,
+ 0.39200, 0.39200, 0.39200,
+ 0.43471, 0.43471, 0.43471,
+ 0.47743, 0.47743, 0.47743,
+ 0.52014, 0.52014, 0.52014,
+ 0.56286, 0.56286, 0.56286,
+ 0.60557, 0.60557, 0.60557,
+ 0.64829, 0.64829, 0.64829,
+ 0.43314, 0.43314, 0.43314,
+ 0.47586, 0.47586, 0.47586,
+ 0.51857, 0.51857, 0.51857,
+ 0.56129, 0.56129, 0.56129,
+ 0.60400, 0.60400, 0.60400,
+ 0.64671, 0.64671, 0.64671,
+ 0.68943, 0.68943, 0.68943,
+ 0.73214, 0.73214, 0.73214,
+ 0.51700, 0.51700, 0.51700,
+ 0.55971, 0.55971, 0.55971,
+ 0.60243, 0.60243, 0.60243,
+ 0.64514, 0.64514, 0.64514,
+ 0.68786, 0.68786, 0.68786,
+ 0.73057, 0.73057, 0.73057,
+ 0.77329, 0.77329, 0.77329,
+ 0.81600, 0.81600, 0.81600,
+ 0.60086, 0.60086, 0.60086,
+ 0.64357, 0.64357, 0.64357,
+ 0.68629, 0.68629, 0.68629,
+ 0.72900, 0.72900, 0.72900,
+ 0.77171, 0.77171, 0.77171,
+ 0.81443, 0.81443, 0.81443,
+ 0.85714, 0.85714, 0.85714,
+ 0.89986, 0.89986, 0.89986,
+ 0.68471, 0.68471, 0.68471,
+ 0.72743, 0.72743, 0.72743,
+ 0.77014, 0.77014, 0.77014,
+ 0.81286, 0.81286, 0.81286,
+ 0.85557, 0.85557, 0.85557,
+ 0.89829, 0.89829, 0.89829,
+ 0.94100, 0.94100, 0.94100,
+ 0.98371, 0.98371, 0.98371,
+ 0.11400, 0.11400, 0.11400,
+ 0.15671, 0.15671, 0.15671,
+ 0.19943, 0.19943, 0.19943,
+ 0.24214, 0.24214, 0.24214,
+ 0.28486, 0.28486, 0.28486,
+ 0.32757, 0.32757, 0.32757,
+ 0.37029, 0.37029, 0.37029,
+ 0.41300, 0.41300, 0.41300,
+ 0.19786, 0.19786, 0.19786,
+ 0.24057, 0.24057, 0.24057,
+ 0.28329, 0.28329, 0.28329,
+ 0.32600, 0.32600, 0.32600,
+ 0.36871, 0.36871, 0.36871,
+ 0.41143, 0.41143, 0.41143,
+ 0.45414, 0.45414, 0.45414,
+ 0.49686, 0.49686, 0.49686,
+ 0.28171, 0.28171, 0.28171,
+ 0.32443, 0.32443, 0.32443,
+ 0.36714, 0.36714, 0.36714,
+ 0.40986, 0.40986, 0.40986,
+ 0.45257, 0.45257, 0.45257,
+ 0.49529, 0.49529, 0.49529,
+ 0.53800, 0.53800, 0.53800,
+ 0.58071, 0.58071, 0.58071,
+ 0.36557, 0.36557, 0.36557,
+ 0.40829, 0.40829, 0.40829,
+ 0.45100, 0.45100, 0.45100,
+ 0.49371, 0.49371, 0.49371,
+ 0.53643, 0.53643, 0.53643,
+ 0.57914, 0.57914, 0.57914,
+ 0.62186, 0.62186, 0.62186,
+ 0.66457, 0.66457, 0.66457,
+ 0.44943, 0.44943, 0.44943,
+ 0.49214, 0.49214, 0.49214,
+ 0.53486, 0.53486, 0.53486,
+ 0.57757, 0.57757, 0.57757,
+ 0.62029, 0.62029, 0.62029,
+ 0.66300, 0.66300, 0.66300,
+ 0.70571, 0.70571, 0.70571,
+ 0.74843, 0.74843, 0.74843,
+ 0.53329, 0.53329, 0.53329,
+ 0.57600, 0.57600, 0.57600,
+ 0.61871, 0.61871, 0.61871,
+ 0.66143, 0.66143, 0.66143,
+ 0.70414, 0.70414, 0.70414,
+ 0.74686, 0.74686, 0.74686,
+ 0.78957, 0.78957, 0.78957,
+ 0.83229, 0.83229, 0.83229,
+ 0.61714, 0.61714, 0.61714,
+ 0.65986, 0.65986, 0.65986,
+ 0.70257, 0.70257, 0.70257,
+ 0.74529, 0.74529, 0.74529,
+ 0.78800, 0.78800, 0.78800,
+ 0.83071, 0.83071, 0.83071,
+ 0.87343, 0.87343, 0.87343,
+ 0.91614, 0.91614, 0.91614,
+ 0.70100, 0.70100, 0.70100,
+ 0.74371, 0.74371, 0.74371,
+ 0.78643, 0.78643, 0.78643,
+ 0.82914, 0.82914, 0.82914,
+ 0.87186, 0.87186, 0.87186,
+ 0.91457, 0.91457, 0.91457,
+ 0.95729, 0.95729, 0.95729,
+ 1.00000, 1.00000, 1.00000
+ };
+
+ // check cube data
+ for(unsigned int i = 0; i < looklut->lut3D->lut.size(); ++i) {
+ OIIO_CHECK_CLOSE(cube[i], looklut->lut3D->lut[i], 1e-4);
+ }
+}
+
+
+OIIO_ADD_TEST(FileFormatIridasLook, fail_on_mask)
+{
+ std::ostringstream strebuf;
+ strebuf << "<?xml version=\"1.0\" ?>" << "\n";
+ strebuf << "<look>" << "\n";
+ strebuf << " <shaders>" << "\n";
+ strebuf << " <base>" << "\n";
+ strebuf << " <rangeversion>\"2\"</rangeversion>" << "\n";
+ strebuf << " <visible>\"1\"</visible>" << "\n";
+ strebuf << " <sublayer0>" << "\n";
+ strebuf << " <opacity>\"1\"</opacity>" << "\n";
+ strebuf << " <parameters>" << "\n";
+ strebuf << " <LogPrinterLights>\"N1\"</LogPrinterLights>" << "\n";
+ strebuf << " </parameters>" << "\n";
+ strebuf << " </sublayer0>" << "\n";
+ strebuf << " <sublayer3>" << "\n";
+ strebuf << " <opacity>\"1\"</opacity>" << "\n";
+ strebuf << " <parameters>" << "\n";
+ strebuf << " <gamma.Z>\"0.49967\"</gamma.Z>" << "\n";
+ strebuf << " <gain.Z>\"0.28739\"</gain.Z>" << "\n";
+ strebuf << " <gamma.Y>\"0.49179\"</gamma.Y>" << "\n";
+ strebuf << " <gain.Y>\"0.22243\"</gain.Y>" << "\n";
+ strebuf << " <gain.X>\"0.34531\"</gain.X>" << "\n";
+ strebuf << " <gamma.X>\"0.39388\"</gamma.X>" << "\n";
+ strebuf << " </parameters>" << "\n";
+ strebuf << " </sublayer3>" << "\n";
+ strebuf << " </base>" << "\n";
+ strebuf << " </shaders>" << "\n";
+ strebuf << " <mask>" << "\n";
+ strebuf << " <name>\"Untitled00_00_00_00\"</name>" << "\n";
+ strebuf << " <activecontour>\"0\"</activecontour>" << "\n";
+ strebuf << " <width>\"1024\"</width>" << "\n";
+ strebuf << " <height>\"778\"</height>" << "\n";
+ strebuf << " <contour>" << "\n";
+ strebuf << " <positive>\"1\"</positive>" << "\n";
+ strebuf << " <point>" << "\n";
+ strebuf << " <inner>\"catmull-rom,value:317.5,583.5@0\"</inner>" << "\n";
+ strebuf << " <innerprevtangent>\"catmull-rom,value:0,0@0\"</innerprevtangent>" << "\n";
+ strebuf << " <innernexttangent>\"catmull-rom,value:0,0@0\"</innernexttangent>" << "\n";
+ strebuf << " <falloffexponent>\"catmull-rom,value:1@0\"</falloffexponent>" << "\n";
+ strebuf << " <falloffweight>\"catmull-rom,value:0.5@0\"</falloffweight>" << "\n";
+ strebuf << " <detached>linear,value:0@0</detached>" << "\n";
+ strebuf << " <outer>\"catmull-rom,value:317.5,583.5@0\"</outer>" << "\n";
+ strebuf << " <outerprevtangent>\"catmull-rom,value:0,0@0\"</outerprevtangent>" << "\n";
+ strebuf << " <outernexttangent>\"catmull-rom,value:0,0@0\"</outernexttangent>" << "\n";
+ strebuf << " <spline>\"linear,value:0@0\"</spline>" << "\n";
+ strebuf << " <smooth>\"linear,value:0@0\"</smooth>" << "\n";
+ strebuf << " </point>" << "\n";
+ strebuf << " </contour>" << "\n";
+ strebuf << " </mask>" << "\n";
+ strebuf << " <LUT>" << "\n";
+ strebuf << " <size>\"8\"</size>" << "\n";
+ strebuf << " <data>\"" << "\n";
+ strebuf << " 000000000000000000000000878B933D000000000000000057BC563E00000000" << "\n";
+ strebuf << " ...truncated, should never be parsed due to mask section\"" << "\n";
+ strebuf << " </data>" << "\n";
+ strebuf << " </LUT>" << "\n";
+ strebuf << "</look>" << "\n";
+
+ std::istringstream infile;
+ infile.str(strebuf.str());
+
+ // Read file
+ LocalFileFormat tester;
+ try
+ {
+ CachedFileRcPtr cachedFile = tester.Read(infile);
+ OIIO_CHECK_ASSERT(false); // Fail test if previous line doesn't throw Exception
+ }
+ catch(Exception& e)
+ {
+ // Check exception message is correct error (not something
+ // like "cannot parse LUT data")
+ std::string expected_error = "Cannot load .look LUT containing mask";
+ OIIO_CHECK_EQUAL(e.what(), expected_error);
+ }
+
+}
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/FileFormatPandora.cpp b/src/core/FileFormatPandora.cpp
new file mode 100644
index 0000000..eef68d2
--- /dev/null
+++ b/src/core/FileFormatPandora.cpp
@@ -0,0 +1,286 @@
+/*
+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 <iostream>
+#include <iterator>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Lut1DOp.h"
+#include "Lut3DOp.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ class LocalCachedFile : public CachedFile
+ {
+ public:
+ LocalCachedFile ()
+ {
+ lut3D = Lut3D::Create();
+ };
+ ~LocalCachedFile() {};
+
+ 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 = "pandora_mga";
+ info.extension = "mga";
+ info.capabilities = FORMAT_CAPABILITY_READ;
+ formatInfoVec.push_back(info);
+
+ FormatInfo info2;
+ info2.name = "pandora_m3d";
+ info2.extension = "m3d";
+ info2.capabilities = FORMAT_CAPABILITY_READ;
+ formatInfoVec.push_back(info2);
+ }
+
+ CachedFileRcPtr
+ LocalFileFormat::Read(std::istream & istream) const
+ {
+ // this shouldn't happen
+ if(!istream)
+ {
+ throw Exception ("File stream empty when trying to read Pandora lut");
+ }
+
+ // Validate the file type
+ std::string line;
+
+ // Parse the file
+ std::string format;
+ int lutEdgeLen = 0;
+ int outputBitDepthMaxValue = 0;
+ std::vector<int> raw3d;
+
+ {
+ std::vector<std::string> parts;
+ std::vector<int> tmpints;
+ bool inLut3d = false;
+
+ while(nextline(istream, line))
+ {
+ // Strip, lowercase, and split the line
+ pystring::split(pystring::lower(pystring::strip(line)), parts);
+ if(parts.empty()) continue;
+
+ // Skip all lines starting with '#'
+ if(pystring::startswith(parts[0],"#")) continue;
+
+ if(parts[0] == "channel")
+ {
+ if(parts.size() != 2 || pystring::lower(parts[1]) != "3d")
+ {
+ throw Exception("Parse error in Pandora lut. Only 3d luts are currently supported. (channel: 3d).");
+ }
+ }
+ else if(parts[0] == "in")
+ {
+ int inval = 0;
+ if(parts.size() != 2 || !StringToInt( &inval, parts[1].c_str()))
+ {
+ throw Exception("Malformed 'in' tag in Pandora lut.");
+ }
+ raw3d.reserve(inval*3);
+ lutEdgeLen = Get3DLutEdgeLenFromNumPixels(inval);
+ }
+ else if(parts[0] == "out")
+ {
+ if(parts.size() != 2 || !StringToInt( &outputBitDepthMaxValue, parts[1].c_str()))
+ {
+ throw Exception("Malformed 'out' tag in Pandora lut.");
+ }
+ }
+ else if(parts[0] == "format")
+ {
+ if(parts.size() != 2 || pystring::lower(parts[1]) != "lut")
+ {
+ throw Exception("Parse error in Pandora lut. Only luts are currently supported. (format: lut).");
+ }
+ }
+ else if(parts[0] == "values")
+ {
+ if(parts.size() != 4 ||
+ pystring::lower(parts[1]) != "red" ||
+ pystring::lower(parts[2]) != "green" ||
+ pystring::lower(parts[3]) != "blue")
+ {
+ throw Exception("Parse error in Pandora lut. Only rgb luts are currently supported. (values: red green blue).");
+ }
+
+ inLut3d = true;
+ }
+ else if(inLut3d)
+ {
+ if(!StringVecToIntVec(tmpints, parts) || tmpints.size() != 4)
+ {
+ std::ostringstream os;
+ os << "Parse error in Pandora lut. Expected to find 4 integers. Instead found '"
+ << line << "'";
+ throw Exception(os.str().c_str());
+ }
+
+ raw3d.push_back(tmpints[1]);
+ raw3d.push_back(tmpints[2]);
+ raw3d.push_back(tmpints[3]);
+ }
+ }
+ }
+
+ // Interpret the parsed data, validate lut sizes
+ if(lutEdgeLen*lutEdgeLen*lutEdgeLen != static_cast<int>(raw3d.size()/3))
+ {
+ std::ostringstream os;
+ os << "Parse error in Pandora lut. ";
+ os << "Incorrect number of lut3d entries. ";
+ os << "Found " << raw3d.size()/3 << ", expected " << lutEdgeLen*lutEdgeLen*lutEdgeLen << ".";
+ throw Exception(os.str().c_str());
+ }
+
+ if(lutEdgeLen*lutEdgeLen*lutEdgeLen == 0)
+ {
+ std::ostringstream os;
+ os << "Parse error in Pandora lut. ";
+ os << "No 3D Lut entries found.";
+ throw Exception(os.str().c_str());
+ }
+
+ if(outputBitDepthMaxValue <= 0)
+ {
+ std::ostringstream os;
+ os << "Parse error in Pandora lut. ";
+ os << "A valid 'out' tag was not found.";
+ throw Exception(os.str().c_str());
+ }
+
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+
+ // Reformat 3D data
+ cachedFile->lut3D->size[0] = lutEdgeLen;
+ cachedFile->lut3D->size[1] = lutEdgeLen;
+ cachedFile->lut3D->size[2] = lutEdgeLen;
+ cachedFile->lut3D->lut.reserve(raw3d.size());
+
+ float scale = 1.0f / ((float)outputBitDepthMaxValue - 1.0f);
+
+ for(int rIndex=0; rIndex<lutEdgeLen; ++rIndex)
+ {
+ for(int gIndex=0; gIndex<lutEdgeLen; ++gIndex)
+ {
+ for(int bIndex=0; bIndex<lutEdgeLen; ++bIndex)
+ {
+ int i = GetLut3DIndex_B(rIndex, gIndex, bIndex,
+ lutEdgeLen, lutEdgeLen, lutEdgeLen);
+ cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+0]) * scale);
+ cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+1]) * scale);
+ cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+2]) * scale);
+ }
+ }
+ }
+
+ 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 Truelight .cub 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());
+ }
+
+ if(newDir == TRANSFORM_DIR_FORWARD)
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else if(newDir == TRANSFORM_DIR_INVERSE)
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ }
+ }
+
+ FileFormat * CreateFileFormatPandora()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/FileFormatSpi1D.cpp b/src/core/FileFormatSpi1D.cpp
new file mode 100644
index 0000000..2835c50
--- /dev/null
+++ b/src/core/FileFormatSpi1D.cpp
@@ -0,0 +1,261 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Lut1DOp.h"
+#include "pystring/pystring.h"
+
+#include <cstdio>
+#include <sstream>
+
+/*
+Version 1
+From -7.5 3.7555555555555555
+Components 1
+Length 4096
+{
+ 0.031525943963232252
+ 0.045645604561056156
+ ...
+}
+
+*/
+
+
+OCIO_NAMESPACE_ENTER
+{
+ ////////////////////////////////////////////////////////////////
+
+ namespace
+ {
+ class LocalCachedFile : public CachedFile
+ {
+ public:
+ LocalCachedFile()
+ {
+ lut = Lut1D::Create();
+ };
+ ~LocalCachedFile() {};
+
+ Lut1DRcPtr lut;
+ };
+
+ 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 = "spi1d";
+ info.extension = "spi1d";
+ info.capabilities = FORMAT_CAPABILITY_READ;
+ formatInfoVec.push_back(info);
+ }
+
+ // Try and load the format
+ // Raise an exception if it can't be loaded.
+
+ CachedFileRcPtr LocalFileFormat::Read(std::istream & istream) const
+ {
+ Lut1DRcPtr lut1d = Lut1D::Create();
+
+ // Parse Header Info
+ int lut_size = -1;
+ float from_min = 0.0;
+ float from_max = 1.0;
+ int version = -1;
+ int components = -1;
+
+ const int MAX_LINE_SIZE = 4096;
+ char lineBuffer[MAX_LINE_SIZE];
+
+ // PARSE HEADER INFO
+ {
+ std::string headerLine("");
+ do
+ {
+ istream.getline(lineBuffer, MAX_LINE_SIZE);
+ headerLine = std::string(lineBuffer);
+ if(pystring::startswith(headerLine, "Version"))
+ {
+ if(sscanf(lineBuffer, "Version %d", &version)!=1)
+ throw Exception("Invalid 'Version' Tag");
+ }
+ else if(pystring::startswith(headerLine, "From"))
+ {
+ if(sscanf(lineBuffer, "From %f %f", &from_min, &from_max)!=2)
+ throw Exception("Invalid 'From' Tag");
+ }
+ else if(pystring::startswith(headerLine, "Components"))
+ {
+ if(sscanf(lineBuffer, "Components %d", &components)!=1)
+ throw Exception("Invalid 'Components' Tag");
+ }
+ else if(pystring::startswith(headerLine, "Length"))
+ {
+ if(sscanf(lineBuffer, "Length %d", &lut_size)!=1)
+ throw Exception("Invalid 'Length' Tag");
+ }
+ }
+ while (istream.good() && !pystring::startswith(headerLine,"{"));
+ }
+
+ if(version == -1)
+ throw Exception("Could not find 'Version' Tag");
+ if(version != 1)
+ throw Exception("Only format version 1 supported.");
+ if (lut_size == -1)
+ throw Exception("Could not find 'Length' Tag");
+ if (components == -1)
+ throw Exception("Could not find 'Components' Tag");
+ if (components<0 || components>3)
+ throw Exception("Components must be [1,2,3]");
+
+
+
+ for(int i=0; i<3; ++i)
+ {
+ lut1d->from_min[i] = from_min;
+ lut1d->from_max[i] = from_max;
+ lut1d->luts[i].clear();
+ lut1d->luts[i].reserve(lut_size);
+ }
+
+ {
+ istream.getline(lineBuffer, MAX_LINE_SIZE);
+
+ int lineCount=0;
+ float values[4];
+
+ while (istream.good())
+ {
+ // If 1 component is specificed, use x1 x1 x1 defaultA
+ if(components==1 && sscanf(lineBuffer,"%f",&values[0])==1)
+ {
+ lut1d->luts[0].push_back(values[0]);
+ lut1d->luts[1].push_back(values[0]);
+ lut1d->luts[2].push_back(values[0]);
+ ++lineCount;
+ }
+ // If 2 components are specificed, use x1 x2 0.0
+ else if(components==2 && sscanf(lineBuffer,"%f %f",&values[0],&values[1])==2)
+ {
+ lut1d->luts[0].push_back(values[0]);
+ lut1d->luts[1].push_back(values[1]);
+ lut1d->luts[2].push_back(0.0);
+ ++lineCount;
+ }
+ // If 3 component is specificed, use x1 x2 x3 defaultA
+ else if(components==3 && sscanf(lineBuffer,"%f %f %f",&values[0],&values[1],&values[2])==3)
+ {
+ lut1d->luts[0].push_back(values[0]);
+ lut1d->luts[1].push_back(values[1]);
+ lut1d->luts[2].push_back(values[2]);
+ ++lineCount;
+ }
+
+ if(lineCount == lut_size) break;
+
+ istream.getline(lineBuffer, MAX_LINE_SIZE);
+ }
+
+ if(lineCount!=lut_size)
+ throw Exception("Not enough entries found.");
+ }
+
+ // 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
+ lut1d->maxerror = 1e-5f;
+ lut1d->errortype = ERROR_RELATIVE;
+
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+ cachedFile->lut = lut1d;
+ 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);
+
+ if(!cachedFile) // This should never happen.
+ {
+ std::ostringstream os;
+ os << "Cannot build Spi1D Op. Invalid cache type.";
+ throw Exception(os.str().c_str());
+ }
+
+ TransformDirection newDir = CombineTransformDirections(dir,
+ fileTransform.getDirection());
+
+ CreateLut1DOp(ops,
+ cachedFile->lut,
+ fileTransform.getInterpolation(),
+ newDir);
+ }
+ }
+
+ FileFormat * CreateFileFormatSpi1D()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/FileFormatSpi3D.cpp b/src/core/FileFormatSpi3D.cpp
new file mode 100644
index 0000000..b1d8c69
--- /dev/null
+++ b/src/core/FileFormatSpi3D.cpp
@@ -0,0 +1,209 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Lut3DOp.h"
+#include "pystring/pystring.h"
+
+#include <cstdio>
+#include <sstream>
+
+/*
+SPILUT 1.0
+3 3
+32 32 32
+0 0 0 0.0132509 0.0158522 0.0156622
+0 0 1 0.0136178 0.018843 0.033921
+0 0 2 0.0136487 0.0240918 0.0563014
+0 0 3 0.015706 0.0303061 0.0774135
+
+... entries can be in any order
+... after the expected number of entries is found, file can contain anything
+*/
+
+
+OCIO_NAMESPACE_ENTER
+{
+ ////////////////////////////////////////////////////////////////
+
+ namespace
+ {
+ class LocalCachedFile : public CachedFile
+ {
+ public:
+ LocalCachedFile()
+ {
+ lut = Lut3D::Create();
+ };
+ ~LocalCachedFile() {};
+
+ Lut3DRcPtr lut;
+ };
+
+ 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 = "spi3d";
+ info.extension = "spi3d";
+ info.capabilities = FORMAT_CAPABILITY_READ;
+ formatInfoVec.push_back(info);
+ }
+
+ CachedFileRcPtr
+ LocalFileFormat::Read(std::istream & istream) const
+ {
+ const int MAX_LINE_SIZE = 4096;
+ char lineBuffer[MAX_LINE_SIZE];
+
+ Lut3DRcPtr lut3d = Lut3D::Create();
+
+ // Read header information
+ istream.getline(lineBuffer, MAX_LINE_SIZE);
+ if(!pystring::startswith(pystring::lower(lineBuffer), "spilut"))
+ {
+ std::ostringstream os;
+ os << "Lut does not appear to be valid spilut format. ";
+ os << "Expected 'SPILUT'. Found, '" << lineBuffer << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ // TODO: Assert 2nd line is 3 3
+ istream.getline(lineBuffer, MAX_LINE_SIZE);
+
+ // Get LUT Size
+ // TODO: Error handling
+ int rSize, gSize, bSize;
+ istream.getline(lineBuffer, MAX_LINE_SIZE);
+ sscanf(lineBuffer, "%d %d %d", &rSize, &gSize, &bSize);
+
+ lut3d->size[0] = rSize;
+ lut3d->size[1] = gSize;
+ lut3d->size[2] = bSize;
+ lut3d->lut.resize(rSize * gSize * bSize * 3);
+
+ // Parse table
+ int index = 0;
+ int rIndex, gIndex, bIndex;
+ float redValue, greenValue, blueValue;
+
+ int entriesRemaining = rSize * gSize * bSize;
+
+ while (istream.good() && entriesRemaining > 0)
+ {
+ istream.getline(lineBuffer, MAX_LINE_SIZE);
+
+ if (sscanf(lineBuffer, "%d %d %d %f %f %f",
+ &rIndex, &gIndex, &bIndex,
+ &redValue, &greenValue, &blueValue) == 6)
+ {
+ index = GetLut3DIndex_B(rIndex, gIndex, bIndex,
+ rSize, gSize, bSize);
+ if(index < 0 || index >= (int) lut3d->lut.size())
+ {
+ std::ostringstream os;
+ os << "Cannot load .spi3d lut, data is invalid. ";
+ os << "A lut entry is specified (";
+ os << rIndex << " " << gIndex << " " << bIndex;
+ os << " that falls outside of the cube.";
+ throw Exception(os.str().c_str());
+ }
+
+ lut3d->lut[index+0] = redValue;
+ lut3d->lut[index+1] = greenValue;
+ lut3d->lut[index+2] = blueValue;
+
+ entriesRemaining--;
+ }
+ }
+
+ // Have we fully populated the table?
+ if (entriesRemaining>0)
+ throw Exception("Not enough entries found.");
+
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+ cachedFile->lut = lut3d;
+ 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);
+
+ if(!cachedFile) // This should never happen.
+ {
+ std::ostringstream os;
+ os << "Cannot build Spi3D Op. Invalid cache type.";
+ throw Exception(os.str().c_str());
+ }
+
+ TransformDirection newDir = CombineTransformDirections(dir,
+ fileTransform.getDirection());
+
+ CreateLut3DOp(ops,
+ cachedFile->lut,
+ fileTransform.getInterpolation(),
+ newDir);
+ }
+ }
+
+ FileFormat * CreateFileFormatSpi3D()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/FileFormatSpiMtx.cpp b/src/core/FileFormatSpiMtx.cpp
new file mode 100644
index 0000000..e14f557
--- /dev/null
+++ b/src/core/FileFormatSpiMtx.cpp
@@ -0,0 +1,195 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "MatrixOps.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+#include <cstdio>
+#include <cstring>
+#include <sstream>
+
+
+OCIO_NAMESPACE_ENTER
+{
+ ////////////////////////////////////////////////////////////////
+
+ namespace
+ {
+ class LocalCachedFile : public CachedFile
+ {
+ public:
+ LocalCachedFile()
+ {
+ memset(m44, 0, 16*sizeof(float));
+ memset(offset4, 0, 4*sizeof(float));
+ };
+ ~LocalCachedFile() {};
+
+ float m44[16];
+ float offset4[4];
+ };
+
+ 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 = "spimtx";
+ info.extension = "spimtx";
+ info.capabilities = FORMAT_CAPABILITY_READ;
+ formatInfoVec.push_back(info);
+ }
+
+ CachedFileRcPtr
+ LocalFileFormat::Read(std::istream & istream) const
+ {
+
+ // Read the entire file.
+ std::ostringstream fileStream;
+
+ {
+ const int MAX_LINE_SIZE = 4096;
+ char lineBuffer[MAX_LINE_SIZE];
+
+ while (istream.good())
+ {
+ istream.getline(lineBuffer, MAX_LINE_SIZE);
+ fileStream << std::string(lineBuffer) << " ";
+ }
+ }
+
+ // Turn it into parts
+ std::vector<std::string> lineParts;
+ pystring::split(pystring::strip(fileStream.str()), lineParts);
+ if(lineParts.size() != 12)
+ {
+ std::ostringstream os;
+ os << "Error parsing .spimtx file. ";
+ os << "File must contain 12 float entries. ";
+ os << lineParts.size() << " found.";
+ throw Exception(os.str().c_str());
+ }
+
+ // Turn the parts into floats
+ std::vector<float> floatArray;
+ if(!StringVecToFloatVec(floatArray, lineParts))
+ {
+ std::ostringstream os;
+ os << "Error parsing .spimtx file. ";
+ os << "File must contain all float entries. ";
+ throw Exception(os.str().c_str());
+ }
+
+
+ // Put the bits in the right place
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+
+ cachedFile->m44[0] = floatArray[0];
+ cachedFile->m44[1] = floatArray[1];
+ cachedFile->m44[2] = floatArray[2];
+ cachedFile->m44[3] = 0.0f;
+
+ cachedFile->m44[4] = floatArray[4];
+ cachedFile->m44[5] = floatArray[5];
+ cachedFile->m44[6] = floatArray[6];
+ cachedFile->m44[7] = 0.0f;
+
+ cachedFile->m44[8] = floatArray[8];
+ cachedFile->m44[9] = floatArray[9];
+ cachedFile->m44[10] = floatArray[10];
+ cachedFile->m44[11] = 0.0f;
+
+ cachedFile->m44[12] = 0.0f;
+ cachedFile->m44[13] = 0.0f;
+ cachedFile->m44[14] = 0.0f;
+ cachedFile->m44[15] = 1.0f;
+
+ cachedFile->offset4[0] = floatArray[3] / 65535.0f;
+ cachedFile->offset4[1] = floatArray[7] / 65535.0f;
+ cachedFile->offset4[2] = floatArray[11] / 65535.0f;
+ cachedFile->offset4[3] = 0.0f;
+
+ 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);
+
+ if(!cachedFile) // This should never happen.
+ {
+ std::ostringstream os;
+ os << "Cannot build SpiMtx Ops. Invalid cache type.";
+ throw Exception(os.str().c_str());
+ }
+
+ TransformDirection newDir = CombineTransformDirections(dir,
+ fileTransform.getDirection());
+
+ CreateMatrixOffsetOp(ops,
+ cachedFile->m44,
+ cachedFile->offset4,
+ newDir);
+ }
+ }
+
+ FileFormat * CreateFileFormatSpiMtx()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/FileFormatTruelight.cpp b/src/core/FileFormatTruelight.cpp
new file mode 100644
index 0000000..a7ccc48
--- /dev/null
+++ b/src/core/FileFormatTruelight.cpp
@@ -0,0 +1,620 @@
+/*
+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 <iostream>
+#include <iterator>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Lut1DOp.h"
+#include "Lut3DOp.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+// This implements the spec for:
+// Per http://www.filmlight.ltd.uk/resources/documents/truelight/white-papers_tl.php
+// FL-TL-TN-0388-TLCubeFormat2.0.pdf
+//
+// Known deficiency in implementation:
+// 1D shaper luts (InputLUT) using integer encodings (vs float) are not supported.
+// How to we determine if the input is integer? MaxVal? Or do we look for a decimal-point?
+// How about scientific notation? (which is explicitly allowed?)
+
+/*
+The input LUT is used to interpolate a higher precision LUT matched to the particular image
+format. For integer formats, the range 0-1 is mapped onto the integer range. Floating point
+values outside the 0-1 range are allowed but may be truncated for integer formats.
+*/
+
+
+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 = "truelight";
+ info.extension = "cub";
+ 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 Truelight .cub lut");
+ }
+
+ // Validate the file type
+ std::string line;
+ if(!nextline(istream, line) ||
+ !pystring::startswith(pystring::lower(line), "# truelight cube"))
+ {
+ throw Exception("Lut doesn't seem to be a Truelight .cub lut.");
+ }
+
+ // Parse the file
+ std::vector<float> raw1d;
+ std::vector<float> raw3d;
+ int size3d[] = { 0, 0, 0 };
+ int size1d = 0;
+ {
+ std::vector<std::string> parts;
+ std::vector<float> tmpfloats;
+
+ bool in1d = false;
+ bool in3d = false;
+
+ while(nextline(istream, line))
+ {
+ // Strip, lowercase, and split the line
+ pystring::split(pystring::lower(pystring::strip(line)), parts);
+
+ if(parts.empty()) continue;
+
+ // Parse header metadata (which starts with #)
+ if(pystring::startswith(parts[0],"#"))
+ {
+ if(parts.size() < 2) continue;
+
+ if(parts[1] == "width")
+ {
+ if(parts.size() != 5 ||
+ !StringToInt( &size3d[0], parts[2].c_str()) ||
+ !StringToInt( &size3d[1], parts[3].c_str()) ||
+ !StringToInt( &size3d[2], parts[4].c_str()))
+ {
+ throw Exception("Malformed width tag in Truelight .cub lut.");
+ }
+
+ raw3d.reserve(3*size3d[0]*size3d[1]*size3d[2]);
+ }
+ else if(parts[1] == "lutlength")
+ {
+ if(parts.size() != 3 ||
+ !StringToInt( &size1d, parts[2].c_str()))
+ {
+ throw Exception("Malformed lutlength tag in Truelight .cub lut.");
+ }
+ raw1d.reserve(3*size1d);
+ }
+ else if(parts[1] == "inputlut")
+ {
+ in1d = true;
+ in3d = false;
+ }
+ else if(parts[1] == "cube")
+ {
+ in3d = true;
+ in1d = false;
+ }
+ else if(parts[1] == "end")
+ {
+ in3d = false;
+ in1d = false;
+
+ // If we hit the end tag, don't bother searching further in the file.
+ break;
+ }
+ }
+
+
+ if(in1d || in3d)
+ {
+ if(StringVecToFloatVec(tmpfloats, parts) && (tmpfloats.size() == 3))
+ {
+ if(in1d)
+ {
+ raw1d.push_back(tmpfloats[0]);
+ raw1d.push_back(tmpfloats[1]);
+ raw1d.push_back(tmpfloats[2]);
+ }
+ else if(in3d)
+ {
+ raw3d.push_back(tmpfloats[0]);
+ raw3d.push_back(tmpfloats[1]);
+ raw3d.push_back(tmpfloats[2]);
+ }
+ }
+ }
+ }
+ }
+
+ // Interpret the parsed data, validate lut sizes
+
+ if(size1d != static_cast<int>(raw1d.size()/3))
+ {
+ std::ostringstream os;
+ os << "Parse error in Truelight .cub lut. ";
+ os << "Incorrect number of lut1d entries. ";
+ os << "Found " << raw1d.size()/3 << ", expected " << size1d << ".";
+ throw Exception(os.str().c_str());
+ }
+
+ if(size3d[0]*size3d[1]*size3d[2] != static_cast<int>(raw3d.size()/3))
+ {
+ std::ostringstream os;
+ os << "Parse error in Truelight .cub lut. ";
+ os << "Incorrect number of lut3d entries. ";
+ os << "Found " << raw3d.size()/3 << ", expected " << size3d[0]*size3d[1]*size3d[2] << ".";
+ throw Exception(os.str().c_str());
+ }
+
+
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+
+ cachedFile->has1D = (size1d>0);
+ cachedFile->has3D = (size3d[0]*size3d[1]*size3d[2]>0);
+
+ // Reformat 1D data
+ if(cachedFile->has1D)
+ {
+ for(int channel=0; channel<3; ++channel)
+ {
+ // Determine the scale factor for the 1d lut. Example:
+ // The inputlut feeding a 6x6x6 3dlut should be scaled from 0.0-5.0.
+ // Beware: Nuke Truelight Writer (at least 6.3 and before) is busted
+ // and does this scaling incorrectly.
+
+ float descale = 1.0f;
+ if(cachedFile->has3D)
+ {
+ descale = 1.0f / static_cast<float>(size3d[channel]-1);
+ }
+
+ cachedFile->lut1D->luts[channel].resize(size1d);
+ for(int i=0; i<size1d; ++i)
+ {
+ cachedFile->lut1D->luts[channel][i] = raw1d[3*i+channel] * descale;
+ }
+ }
+
+ // 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;
+ }
+
+ // Reformat 3D data
+ if(cachedFile->has3D)
+ {
+ cachedFile->lut3D->size[0] = size3d[0];
+ cachedFile->lut3D->size[1] = size3d[1];
+ cachedFile->lut3D->size[2] = size3d[2];
+ cachedFile->lut3D->lut = raw3d;
+ }
+
+ 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 Truelight .cub 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);
+ }
+
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else if(newDir == TRANSFORM_DIR_INVERSE)
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+
+ if(cachedFile->has1D)
+ {
+ CreateLut1DOp(ops, cachedFile->lut1D,
+ INTERP_LINEAR, newDir);
+ }
+ }
+ }
+ }
+
+ FileFormat * CreateFileFormatTruelight()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+OIIO_ADD_TEST(FileFormatTruelight, ShaperAndLut3D)
+{
+ // This lowers the red channel by 0.5, other channels are unaffected.
+ const char * luttext = "# Truelight Cube v2.0\n"
+ "# iDims 3\n"
+ "# oDims 3\n"
+ "# width 3 3 3\n"
+ "# lutLength 5\n"
+ "# InputLUT\n"
+ " 0.000000 0.000000 0.000000\n"
+ " 0.500000 0.500000 0.500000\n"
+ " 1.000000 1.000000 1.000000\n"
+ " 1.500000 1.500000 1.500000\n"
+ " 2.000000 2.000000 2.000000\n"
+ "\n"
+ "# Cube\n"
+ " 0.000000 0.000000 0.000000\n"
+ " 0.250000 0.000000 0.000000\n"
+ " 0.500000 0.000000 0.000000\n"
+ " 0.000000 0.500000 0.000000\n"
+ " 0.250000 0.500000 0.000000\n"
+ " 0.500000 0.500000 0.000000\n"
+ " 0.000000 1.000000 0.000000\n"
+ " 0.250000 1.000000 0.000000\n"
+ " 0.500000 1.000000 0.000000\n"
+ " 0.000000 0.000000 0.500000\n"
+ " 0.250000 0.000000 0.500000\n"
+ " 0.500000 0.000000 0.500000\n"
+ " 0.000000 0.500000 0.500000\n"
+ " 0.250000 0.500000 0.500000\n"
+ " 0.500000 0.500000 0.500000\n"
+ " 0.000000 1.000000 0.500000\n"
+ " 0.250000 1.000000 0.500000\n"
+ " 0.500000 1.000000 0.500000\n"
+ " 0.000000 0.000000 1.000000\n"
+ " 0.250000 0.000000 1.000000\n"
+ " 0.500000 0.000000 1.000000\n"
+ " 0.000000 0.500000 1.000000\n"
+ " 0.250000 0.500000 1.000000\n"
+ " 0.500000 0.500000 1.000000\n"
+ " 0.000000 1.000000 1.000000\n"
+ " 0.250000 1.000000 1.000000\n"
+ " 0.500000 1.000000 1.000000\n"
+ "\n"
+ "# end\n"
+ "\n"
+ "# Truelight profile\n"
+ "title{madeup on some display}\n"
+ "print{someprint}\n"
+ "display{some}\n"
+ "cubeFile{madeup.cube}\n"
+ "\n"
+ " # This last line confirms 'end' tag is obeyed\n"
+ " 1.23456 1.23456 1.23456\n";
+
+ std::istringstream lutIStream;
+ lutIStream.str(luttext);
+
+ // Read file
+ OCIO::LocalFileFormat tester;
+ OCIO::CachedFileRcPtr cachedFile;
+ OIIO_CHECK_NO_THOW(cachedFile = tester.Read(lutIStream));
+ OCIO::LocalCachedFileRcPtr lut = OCIO::DynamicPtrCast<OCIO::LocalCachedFile>(cachedFile);
+
+ OIIO_CHECK_ASSERT(lut->has1D);
+ OIIO_CHECK_ASSERT(lut->has3D);
+
+ float data[4*3] = { 0.1f, 0.2f, 0.3f, 0.0f,
+ 1.0f, 0.5f, 0.123456f, 0.0f,
+ -1.0f, 1.5f, 0.5f, 0.0f };
+
+ float result[4*3] = { 0.05f, 0.2f, 0.3f, 0.0f,
+ 0.50f, 0.5f, 0.123456f, 0.0f,
+ 0.0f, 1.5f, 0.5f, 0.0f };
+
+ OCIO::OpRcPtrVec ops;
+ if(lut->has1D)
+ {
+ CreateLut1DOp(ops, lut->lut1D,
+ OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_FORWARD);
+ }
+ if(lut->has3D)
+ {
+ CreateLut3DOp(ops, lut->lut3D,
+ OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_FORWARD);
+ }
+ FinalizeOpVec(ops);
+
+
+ // Apply the result
+ for(OCIO::OpRcPtrVec::size_type i = 0, size = ops.size(); i < size; ++i)
+ {
+ ops[i]->apply(data, 3);
+ }
+
+ for(int i=0; i<3; ++i)
+ {
+ OIIO_CHECK_CLOSE( data[i], result[i], 1.0e-6 );
+ }
+}
+
+OIIO_ADD_TEST(FileFormatTruelight, Shaper)
+{
+ const char * luttext = "# Truelight Cube v2.0\n"
+ "# lutLength 11\n"
+ "# iDims 3\n"
+ "\n"
+ "\n"
+ "# InputLUT\n"
+ " 0.000 0.000 -0.000\n"
+ " 0.200 0.010 -0.100\n"
+ " 0.400 0.040 -0.200\n"
+ " 0.600 0.090 -0.300\n"
+ " 0.800 0.160 -0.400\n"
+ " 1.000 0.250 -0.500\n"
+ " 1.200 0.360 -0.600\n"
+ " 1.400 0.490 -0.700\n"
+ " 1.600 0.640 -0.800\n"
+ " 1.800 0.820 -0.900\n"
+ " 2.000 1.000 -1.000\n"
+ "\n\n\n"
+ "# end\n";
+
+ std::istringstream lutIStream;
+ lutIStream.str(luttext);
+
+ // Read file
+ OCIO::LocalFileFormat tester;
+ OCIO::CachedFileRcPtr cachedFile;
+ OIIO_CHECK_NO_THOW(cachedFile = tester.Read(lutIStream));
+
+ OCIO::LocalCachedFileRcPtr lut = OCIO::DynamicPtrCast<OCIO::LocalCachedFile>(cachedFile);
+
+ OIIO_CHECK_ASSERT(lut->has1D);
+ OIIO_CHECK_ASSERT(!lut->has3D);
+
+ float data[4*3] = { 0.1f, 0.2f, 0.3f, 0.0f,
+ 1.0f, 0.5f, 0.123456f, 0.0f,
+ -1.0f, 1.5f, 0.5f, 0.0f };
+
+ float result[4*3] = { 0.2f, 0.04f, -0.3f, 0.0f,
+ 2.0f, 0.25f, -0.123456f, 0.0f,
+ 0.0f, 1.0f, -0.5f, 0.0f };
+
+ OCIO::OpRcPtrVec ops;
+ if(lut->has1D)
+ {
+ CreateLut1DOp(ops, lut->lut1D,
+ OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_FORWARD);
+ }
+ if(lut->has3D)
+ {
+ CreateLut3DOp(ops, lut->lut3D,
+ OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_FORWARD);
+ }
+ FinalizeOpVec(ops);
+
+
+ // Apply the result
+ for(OCIO::OpRcPtrVec::size_type i = 0, size = ops.size(); i < size; ++i)
+ {
+ ops[i]->apply(data, 3);
+ }
+
+ for(int i=0; i<3; ++i)
+ {
+ OIIO_CHECK_CLOSE( data[i], result[i], 1.0e-6 );
+ }
+}
+
+
+OIIO_ADD_TEST(FileFormatTruelight, Lut3D)
+{
+ // This lowers the red channel by 0.5, other channels are unaffected.
+ const char * luttext = "# Truelight Cube v2.0\n"
+ "# iDims 3\n"
+ "# oDims 3\n"
+ "# width 3 3 3\n"
+ "\n\n\n"
+ "# Cube\n"
+ " 0.000000 0.000000 0.000000\n"
+ " 0.250000 0.000000 0.000000\n"
+ " 0.500000 0.000000 0.000000\n"
+ " 0.000000 0.500000 0.000000\n"
+ " 0.250000 0.500000 0.000000\n"
+ " 0.500000 0.500000 0.000000\n"
+ " 0.000000 1.000000 0.000000\n"
+ " 0.250000 1.000000 0.000000\n"
+ " 0.500000 1.000000 0.000000\n"
+ " 0.000000 0.000000 0.500000\n"
+ " 0.250000 0.000000 0.500000\n"
+ " 0.500000 0.000000 0.500000\n"
+ " 0.000000 0.500000 0.500000\n"
+ " 0.250000 0.500000 0.500000\n"
+ " 0.500000 0.500000 0.500000\n"
+ " 0.000000 1.000000 0.500000\n"
+ " 0.250000 1.000000 0.500000\n"
+ " 0.500000 1.000000 0.500000\n"
+ " 0.000000 0.000000 1.000000\n"
+ " 0.250000 0.000000 1.000000\n"
+ " 0.500000 0.000000 1.000000\n"
+ " 0.000000 0.500000 1.000000\n"
+ " 0.250000 0.500000 1.000000\n"
+ " 0.500000 0.500000 1.000000\n"
+ " 0.000000 1.000000 1.000000\n"
+ " 0.250000 1.000000 1.000000\n"
+ " 0.500000 1.000000 1.000000\n"
+ "\n"
+ "# end\n";
+
+ std::istringstream lutIStream;
+ lutIStream.str(luttext);
+
+ // Read file
+ OCIO::LocalFileFormat tester;
+ OCIO::CachedFileRcPtr cachedFile;
+ OIIO_CHECK_NO_THOW(cachedFile = tester.Read(lutIStream));
+ OCIO::LocalCachedFileRcPtr lut = OCIO::DynamicPtrCast<OCIO::LocalCachedFile>(cachedFile);
+
+ OIIO_CHECK_ASSERT(!lut->has1D);
+ OIIO_CHECK_ASSERT(lut->has3D);
+
+ float data[4*3] = { 0.1f, 0.2f, 0.3f, 0.0f,
+ 1.0f, 0.5f, 0.123456f, 0.0f,
+ -1.0f, 1.5f, 0.5f, 0.0f };
+
+ float result[4*3] = { 0.05f, 0.2f, 0.3f, 0.0f,
+ 0.50f, 0.5f, 0.123456f, 0.0f,
+ 0.0f, 1.5f, 0.5f, 0.0f };
+
+ OCIO::OpRcPtrVec ops;
+ if(lut->has1D)
+ {
+ CreateLut1DOp(ops, lut->lut1D,
+ OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_FORWARD);
+ }
+ if(lut->has3D)
+ {
+ CreateLut3DOp(ops, lut->lut3D,
+ OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_FORWARD);
+ }
+ FinalizeOpVec(ops);
+
+
+ // Apply the result
+ for(OCIO::OpRcPtrVec::size_type i = 0, size = ops.size(); i < size; ++i)
+ {
+ ops[i]->apply(data, 3);
+ }
+
+ for(int i=0; i<3; ++i)
+ {
+ OIIO_CHECK_CLOSE( data[i], result[i], 1.0e-6 );
+ }
+}
+
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/FileFormatVF.cpp b/src/core/FileFormatVF.cpp
new file mode 100644
index 0000000..cb46ef0
--- /dev/null
+++ b/src/core/FileFormatVF.cpp
@@ -0,0 +1,297 @@
+/*
+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 <iostream>
+#include <iterator>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Lut3DOp.h"
+#include "MatrixOps.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ class LocalCachedFile : public CachedFile
+ {
+ public:
+ LocalCachedFile () :
+ useMatrix(false)
+ {
+ lut3D = Lut3D::Create();
+ memset(m44, 0, 16*sizeof(float));
+ };
+ ~LocalCachedFile() {};
+
+ Lut3DRcPtr lut3D;
+ float m44[16];
+ bool useMatrix;
+ };
+
+ 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 = "nukevf";
+ info.extension = "vf";
+ 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 .vf lut");
+ }
+
+ // Validate the file type
+ std::string line;
+ if(!nextline(istream, line) ||
+ !pystring::startswith(pystring::lower(line), "#inventor"))
+ {
+ throw Exception("Lut doesn't seem to be a .vf lut. Expecting '#Inventor V2.1 ascii'.");
+ }
+
+ // Parse the file
+ std::vector<float> raw3d;
+ int size3d[] = { 0, 0, 0 };
+ std::vector<float> global_transform;
+
+ {
+ std::vector<std::string> parts;
+ std::vector<float> tmpfloats;
+
+ bool in3d = false;
+
+ while(nextline(istream, line))
+ {
+ // Strip, lowercase, and split the line
+ pystring::split(pystring::lower(pystring::strip(line)), parts);
+
+ if(parts.empty()) continue;
+
+ if(pystring::startswith(parts[0],"#")) continue;
+
+ if(!in3d)
+ {
+ if(parts[0] == "grid_size")
+ {
+ if(parts.size() != 4 ||
+ !StringToInt( &size3d[0], parts[1].c_str()) ||
+ !StringToInt( &size3d[1], parts[2].c_str()) ||
+ !StringToInt( &size3d[2], parts[3].c_str()))
+ {
+ throw Exception("Malformed grid_size tag in .vf lut.");
+ }
+
+ raw3d.reserve(3*size3d[0]*size3d[1]*size3d[2]);
+ }
+ else if(parts[0] == "global_transform")
+ {
+ if(parts.size() != 17)
+ {
+ throw Exception("Malformed global_transform tag. 16 floats expected.");
+ }
+
+ parts.erase(parts.begin()); // Drop the 1st entry. (the tag)
+ if(!StringVecToFloatVec(global_transform, parts) || global_transform.size() != 16)
+ {
+ throw Exception("Malformed global_transform tag. Could not convert to float array.");
+ }
+ }
+ // TODO: element_size (aka scale3)
+ // TODO: world_origin (aka translate3)
+ else if(parts[0] == "data")
+ {
+ in3d = true;
+ }
+ }
+ else // (in3d)
+ {
+ if(StringVecToFloatVec(tmpfloats, parts) && (tmpfloats.size() == 3))
+ {
+ raw3d.push_back(tmpfloats[0]);
+ raw3d.push_back(tmpfloats[1]);
+ raw3d.push_back(tmpfloats[2]);
+ }
+ }
+ }
+ }
+
+ // Interpret the parsed data, validate lut sizes
+ if(size3d[0]*size3d[1]*size3d[2] != static_cast<int>(raw3d.size()/3))
+ {
+ std::ostringstream os;
+ os << "Parse error in .vf lut. ";
+ os << "Incorrect number of lut3d entries. ";
+ os << "Found " << raw3d.size()/3 << ", expected " << size3d[0]*size3d[1]*size3d[2] << ".";
+ throw Exception(os.str().c_str());
+ }
+
+ if(size3d[0]*size3d[1]*size3d[2] == 0)
+ {
+ std::ostringstream os;
+ os << "Parse error in .vf lut. ";
+ os << "No 3D Lut entries found.";
+ throw Exception(os.str().c_str());
+ }
+
+ LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
+
+ // Setup the global matrix.
+ // (Nuke pre-scales this by the 3dlut size, so we must undo that here)
+ if(global_transform.size() == 16)
+ {
+ for(int i=0; i<4; ++i)
+ {
+ global_transform[4*i+0] *= static_cast<float>(size3d[0]);
+ global_transform[4*i+1] *= static_cast<float>(size3d[1]);
+ global_transform[4*i+2] *= static_cast<float>(size3d[2]);
+ }
+
+ memcpy(cachedFile->m44, &global_transform[0], 16*sizeof(float));
+ cachedFile->useMatrix = true;
+ }
+
+ // Reformat 3D data
+ cachedFile->lut3D->size[0] = size3d[0];
+ cachedFile->lut3D->size[1] = size3d[1];
+ cachedFile->lut3D->size[2] = size3d[2];
+ cachedFile->lut3D->lut.reserve(raw3d.size());
+
+ for(int rIndex=0; rIndex<size3d[0]; ++rIndex)
+ {
+ for(int gIndex=0; gIndex<size3d[1]; ++gIndex)
+ {
+ for(int bIndex=0; bIndex<size3d[2]; ++bIndex)
+ {
+ int i = GetLut3DIndex_B(rIndex, gIndex, bIndex,
+ size3d[0], size3d[1], size3d[2]);
+
+ cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+0]));
+ cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+1]));
+ cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+2]));
+ }
+ }
+ }
+
+ 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 .vf 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());
+ }
+
+ if(newDir == TRANSFORM_DIR_FORWARD)
+ {
+ if(cachedFile->useMatrix)
+ {
+ CreateMatrixOp(ops, cachedFile->m44, newDir);
+ }
+
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+ }
+ else if(newDir == TRANSFORM_DIR_INVERSE)
+ {
+ CreateLut3DOp(ops, cachedFile->lut3D,
+ fileTransform.getInterpolation(), newDir);
+
+ if(cachedFile->useMatrix)
+ {
+ CreateMatrixOp(ops, cachedFile->m44, newDir);
+ }
+ }
+ }
+ }
+
+ FileFormat * CreateFileFormatVF()
+ {
+ return new LocalFileFormat();
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+// TODO: Unit test
diff --git a/src/core/FileTransform.cpp b/src/core/FileTransform.cpp
new file mode 100644
index 0000000..45b5a3e
--- /dev/null
+++ b/src/core/FileTransform.cpp
@@ -0,0 +1,579 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "Logging.h"
+#include "Mutex.h"
+#include "NoOps.h"
+#include "PathUtils.h"
+#include "pystring/pystring.h"
+
+#include <fstream>
+#include <map>
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+ FileTransformRcPtr FileTransform::Create()
+ {
+ return FileTransformRcPtr(new FileTransform(), &deleter);
+ }
+
+ void FileTransform::deleter(FileTransform* t)
+ {
+ delete t;
+ }
+
+
+ class FileTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+ std::string src_;
+ std::string cccid_;
+ Interpolation interp_;
+
+ Impl() :
+ dir_(TRANSFORM_DIR_FORWARD),
+ interp_(INTERP_UNKNOWN)
+ { }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+ src_ = rhs.src_;
+ cccid_ = rhs.cccid_;
+ interp_ = rhs.interp_;
+ return *this;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ FileTransform::FileTransform()
+ : m_impl(new FileTransform::Impl)
+ {
+ }
+
+ TransformRcPtr FileTransform::createEditableCopy() const
+ {
+ FileTransformRcPtr transform = FileTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ FileTransform::~FileTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ FileTransform& FileTransform::operator= (const FileTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection FileTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void FileTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+ const char * FileTransform::getSrc() const
+ {
+ return getImpl()->src_.c_str();
+ }
+
+ void FileTransform::setSrc(const char * src)
+ {
+ getImpl()->src_ = src;
+ }
+
+ const char * FileTransform::getCCCId() const
+ {
+ return getImpl()->cccid_.c_str();
+ }
+
+ void FileTransform::setCCCId(const char * cccid)
+ {
+ getImpl()->cccid_ = cccid;
+ }
+
+ Interpolation FileTransform::getInterpolation() const
+ {
+ return getImpl()->interp_;
+ }
+
+ void FileTransform::setInterpolation(Interpolation interp)
+ {
+ getImpl()->interp_ = interp;
+ }
+
+ int FileTransform::getNumFormats()
+ {
+ return FormatRegistry::GetInstance().getNumFormats(FORMAT_CAPABILITY_READ);
+ }
+
+ const char * FileTransform::getFormatNameByIndex(int index)
+ {
+ return FormatRegistry::GetInstance().getFormatNameByIndex(FORMAT_CAPABILITY_READ, index);
+ }
+
+ const char * FileTransform::getFormatExtensionByIndex(int index)
+ {
+ return FormatRegistry::GetInstance().getFormatExtensionByIndex(FORMAT_CAPABILITY_READ, index);
+ }
+
+ std::ostream& operator<< (std::ostream& os, const FileTransform& t)
+ {
+ os << "<FileTransform ";
+ os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
+ os << "interpolation=" << InterpolationToString(t.getInterpolation()) << ", ";
+ os << "src='" << t.getSrc() << "', ";
+ os << "cccid='" << t.getCCCId() << "'";
+ os << ">";
+
+ return os;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ // NOTE: You must be mindful when editing this function.
+ // to be resiliant to the static initialization order 'fiasco'
+ //
+ // See
+ // http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14
+ // http://stackoverflow.com/questions/335369/finding-c-static-initialization-order-problems
+ // for more info.
+
+ namespace
+ {
+ FormatRegistry* g_formatRegistry = NULL;
+ Mutex g_formatRegistryLock;
+ }
+
+ FormatRegistry & FormatRegistry::GetInstance()
+ {
+ AutoMutex lock(g_formatRegistryLock);
+
+ if(!g_formatRegistry)
+ {
+ g_formatRegistry = new FormatRegistry();
+ }
+
+ return *g_formatRegistry;
+ }
+
+ FormatRegistry::FormatRegistry()
+ {
+ registerFileFormat(CreateFileFormat3DL());
+ registerFileFormat(CreateFileFormatCCC());
+ registerFileFormat(CreateFileFormatCC());
+ registerFileFormat(CreateFileFormatCSP());
+ registerFileFormat(CreateFileFormatHDL());
+ registerFileFormat(CreateFileFormatIridasItx());
+ registerFileFormat(CreateFileFormatIridasCube());
+ registerFileFormat(CreateFileFormatIridasLook());
+ registerFileFormat(CreateFileFormatPandora());
+ registerFileFormat(CreateFileFormatSpi1D());
+ registerFileFormat(CreateFileFormatSpi3D());
+ registerFileFormat(CreateFileFormatSpiMtx());
+ registerFileFormat(CreateFileFormatTruelight());
+ registerFileFormat(CreateFileFormatVF());
+ }
+
+ FormatRegistry::~FormatRegistry()
+ {
+ }
+
+ FileFormat* FormatRegistry::getFileFormatByName(const std::string & name) const
+ {
+ FileFormatMap::const_iterator iter = m_formatsByName.find(
+ pystring::lower(name));
+ if(iter != m_formatsByName.end())
+ return iter->second;
+ return NULL;
+ }
+
+ FileFormat* FormatRegistry::getFileFormatForExtension(const std::string & extension) const
+ {
+ FileFormatMap::const_iterator iter = m_formatsByExtension.find(
+ pystring::lower(extension));
+ if(iter != m_formatsByExtension.end())
+ return iter->second;
+ return NULL;
+ }
+
+ void FormatRegistry::registerFileFormat(FileFormat* format)
+ {
+ FormatInfoVec formatInfoVec;
+ format->GetFormatInfo(formatInfoVec);
+
+ if(formatInfoVec.empty())
+ {
+ std::ostringstream os;
+ os << "FileFormat Registry error. ";
+ os << "A file format did not provide the required format info.";
+ throw Exception(os.str().c_str());
+ }
+
+ for(unsigned int i=0; i<formatInfoVec.size(); ++i)
+ {
+ if(formatInfoVec[i].capabilities == FORMAT_CAPABILITY_NONE)
+ {
+ std::ostringstream os;
+ os << "FileFormat Registry error. ";
+ os << "A file format does not define either reading or writing.";
+ throw Exception(os.str().c_str());
+ }
+
+ if(getFileFormatByName(formatInfoVec[i].name))
+ {
+ std::ostringstream os;
+ os << "Cannot register multiple file formats named, '";
+ os << formatInfoVec[i].name << "'.";
+ throw Exception(os.str().c_str());
+ }
+
+ m_formatsByName[formatInfoVec[i].name] = format;
+
+ // For now, dont worry if multiple formats register the same extension
+ // TODO: keep track of all of em! (make the value a vector)
+ m_formatsByExtension[formatInfoVec[i].extension] = format;
+
+ if(formatInfoVec[i].capabilities & FORMAT_CAPABILITY_READ)
+ {
+ m_readFormatNames.push_back(formatInfoVec[i].name);
+ m_readFormatExtensions.push_back(formatInfoVec[i].extension);
+ }
+
+ if(formatInfoVec[i].capabilities & FORMAT_CAPABILITY_WRITE)
+ {
+ m_writeFormatNames.push_back(formatInfoVec[i].name);
+ m_writeFormatExtensions.push_back(formatInfoVec[i].extension);
+ }
+ }
+
+ m_rawFormats.push_back(format);
+ }
+
+ int FormatRegistry::getNumRawFormats() const
+ {
+ return static_cast<int>(m_rawFormats.size());
+ }
+
+ FileFormat* FormatRegistry::getRawFormatByIndex(int index) const
+ {
+ if(index<0 || index>=getNumRawFormats())
+ {
+ return NULL;
+ }
+
+ return m_rawFormats[index];
+ }
+
+ int FormatRegistry::getNumFormats(int capability) const
+ {
+ if(capability == FORMAT_CAPABILITY_READ)
+ {
+ return static_cast<int>(m_readFormatNames.size());
+ }
+ else if(capability == FORMAT_CAPABILITY_WRITE)
+ {
+ return static_cast<int>(m_writeFormatNames.size());
+ }
+ return 0;
+ }
+
+ const char * FormatRegistry::getFormatNameByIndex(int capability, int index) const
+ {
+ if(capability == FORMAT_CAPABILITY_READ)
+ {
+ if(index<0 || index>=static_cast<int>(m_readFormatNames.size()))
+ {
+ return "";
+ }
+ return m_readFormatNames[index].c_str();
+ }
+ else if(capability == FORMAT_CAPABILITY_WRITE)
+ {
+ if(index<0 || index>=static_cast<int>(m_readFormatNames.size()))
+ {
+ return "";
+ }
+ return m_writeFormatNames[index].c_str();
+ }
+ return "";
+ }
+
+ const char * FormatRegistry::getFormatExtensionByIndex(int capability, int index) const
+ {
+ if(capability == FORMAT_CAPABILITY_READ)
+ {
+ if(index<0 || index>=static_cast<int>(m_readFormatExtensions.size()))
+ {
+ return "";
+ }
+ return m_readFormatExtensions[index].c_str();
+ }
+ else if(capability == FORMAT_CAPABILITY_WRITE)
+ {
+ if(index<0 || index>=static_cast<int>(m_writeFormatExtensions.size()))
+ {
+ return "";
+ }
+ return m_writeFormatExtensions[index].c_str();
+ }
+ return "";
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ FileFormat::~FileFormat()
+ {
+
+ }
+
+ std::string FileFormat::getName() const
+ {
+ FormatInfoVec infoVec;
+ GetFormatInfo(infoVec);
+ if(infoVec.size()>0)
+ {
+ return infoVec[0].name;
+ }
+ return "Unknown Format";
+ }
+
+
+
+ void FileFormat::Write(const Baker & /*baker*/,
+ const std::string & formatName,
+ std::ostream & /*ostream*/) const
+ {
+ std::ostringstream os;
+ os << "Format " << formatName << " does not support writing.";
+ throw Exception(os.str().c_str());
+ }
+
+ namespace
+ {
+ typedef std::pair<FileFormat*, CachedFileRcPtr> FileCachePair;
+ typedef std::map<std::string, FileCachePair> FileCacheMap;
+
+ FileCacheMap g_fileCache;
+ Mutex g_fileCacheLock;
+
+ // Get the FileFormat, CachedFilePtr
+ // or throw an exception.
+
+ FileCachePair GetFile(const std::string & filepath)
+ {
+ // Acquire fileCache mutex
+ AutoMutex lock(g_fileCacheLock);
+
+ FileCacheMap::iterator iter = g_fileCache.find(filepath);
+ if(iter != g_fileCache.end())
+ {
+ return iter->second;
+ }
+
+ // We did not find the file in the cache; let's read it.
+ {
+ std::ostringstream os;
+ os << "Opening " << filepath;
+ LogDebug(os.str());
+ }
+
+ // Open the filePath
+ std::ifstream filestream;
+ filestream.open(filepath.c_str(), std::ios_base::in);
+ if (!filestream.good())
+ {
+ std::ostringstream os;
+ os << "The specified FileTransform srcfile, '";
+ os << filepath <<"', could not be opened. ";
+ os << "Please confirm the file exists with appropriate read";
+ os << " permissions.";
+ throw Exception(os.str().c_str());
+ }
+
+
+ // Try the initial format.
+ std::string primaryErrorText;
+ std::string root, extension;
+ pystring::os::path::splitext(root, extension, filepath);
+ extension = pystring::replace(extension,".","",1); // remove the leading '.'
+
+ FileFormat * primaryFormat = FormatRegistry::GetInstance().getFileFormatForExtension(extension);
+
+ if(primaryFormat)
+ {
+ try
+ {
+ CachedFileRcPtr cachedFile = primaryFormat->Read(filestream);
+
+ // Add the result to our cache, return it.
+ FileCachePair pair = std::make_pair(primaryFormat, cachedFile);
+ g_fileCache[filepath] = pair;
+
+ if(IsDebugLoggingEnabled())
+ {
+ std::ostringstream os;
+ os << " Loaded primary format ";
+ os << primaryFormat->getName();
+ LogDebug(os.str());
+ }
+
+ return pair;
+ }
+ catch(std::exception & e)
+ {
+ primaryErrorText = e.what();
+ filestream.clear();
+ filestream.seekg( std::ifstream::beg );
+
+ if(IsDebugLoggingEnabled())
+ {
+ std::ostringstream os;
+ os << " Failed primary format ";
+ os << primaryFormat->getName();
+ os << ": " << e.what();
+ LogDebug(os.str());
+ }
+ }
+ }
+
+ // If this fails, try all other formats
+ FormatRegistry & formats = FormatRegistry::GetInstance();
+ for(int findex = 0; findex<formats.getNumRawFormats(); ++findex)
+ {
+ FileFormat * altFormat = formats.getRawFormatByIndex(findex);
+
+ // Dont bother trying the primaryFormat twice.
+ if(altFormat == primaryFormat) continue;
+
+ try
+ {
+ CachedFileRcPtr cachedFile = altFormat->Read(filestream);
+
+ // Add the result to our cache, return it.
+ FileCachePair pair = std::make_pair(altFormat, cachedFile);
+ g_fileCache[filepath] = pair;
+
+ if(IsDebugLoggingEnabled())
+ {
+ std::ostringstream os;
+ os << " Loaded alt format ";
+ os << altFormat->getName();
+ LogDebug(os.str());
+ }
+
+ return pair;
+ }
+ catch(std::exception & e)
+ {
+ filestream.clear();
+ filestream.seekg( std::ifstream::beg );
+
+ if(IsDebugLoggingEnabled())
+ {
+ std::ostringstream os;
+ os << " Failed alt format ";
+ os << altFormat->getName();
+ os << ": " << e.what();
+ LogDebug(os.str());
+ }
+ }
+ }
+
+ // No formats succeeded. Error out with a sensible message.
+ if(primaryFormat)
+ {
+ std::ostringstream os;
+ os << "The specified transform file '";
+ os << filepath <<"' could not be loaded. ";
+ os << primaryErrorText;
+
+ throw Exception(os.str().c_str());
+ }
+ else
+ {
+ std::ostringstream os;
+ os << "The specified transform file '";
+ os << filepath <<"' does not appear to be a valid, known LUT file format.";
+ throw Exception(os.str().c_str());
+ }
+ }
+ }
+
+ void ClearFileTransformCaches()
+ {
+ AutoMutex lock(g_fileCacheLock);
+ g_fileCache.clear();
+ }
+
+ void BuildFileOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const FileTransform& fileTransform,
+ TransformDirection dir)
+ {
+ std::string src = fileTransform.getSrc();
+ if(src.empty())
+ {
+ std::ostringstream os;
+ os << "The transform file has not been specified.";
+ throw Exception(os.str().c_str());
+ }
+
+ std::string filepath = context->resolveFileLocation(src.c_str());
+ CreateFileNoOp(ops, filepath);
+
+ FileCachePair cachePair = GetFile(filepath);
+ FileFormat* format = cachePair.first;
+ CachedFileRcPtr cachedFile = cachePair.second;
+
+ format->BuildFileOps(ops,
+ config, context,
+ cachedFile, fileTransform,
+ dir);
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/FileTransform.h b/src/core/FileTransform.h
new file mode 100644
index 0000000..3c8f070
--- /dev/null
+++ b/src/core/FileTransform.h
@@ -0,0 +1,156 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_FILETRANSFORM_H
+#define INCLUDED_OCIO_FILETRANSFORM_H
+
+#include <map>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Op.h"
+#include "Processor.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ void ClearFileTransformCaches();
+
+ class CachedFile
+ {
+ public:
+ CachedFile() {};
+ virtual ~CachedFile() {};
+ };
+
+ typedef OCIO_SHARED_PTR<CachedFile> CachedFileRcPtr;
+
+ const int FORMAT_CAPABILITY_NONE = 0;
+ const int FORMAT_CAPABILITY_READ = 1;
+ const int FORMAT_CAPABILITY_WRITE = 2;
+ const int FORMAT_CAPABILITY_ALL = (FORMAT_CAPABILITY_READ | FORMAT_CAPABILITY_WRITE);
+
+ struct FormatInfo
+ {
+ std::string name; // name must be globally unique
+ std::string extension; // extension does not need to be unique
+ int capabilities;
+
+ FormatInfo():
+ capabilities(FORMAT_CAPABILITY_NONE)
+ { }
+ };
+
+ typedef std::vector<FormatInfo> FormatInfoVec;
+
+ class FileFormat
+ {
+ public:
+ virtual ~FileFormat();
+
+ virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const = 0;
+
+ virtual CachedFileRcPtr Read(std::istream & istream) const = 0;
+
+ virtual void Write(const Baker & baker,
+ const std::string & formatName,
+ std::ostream & ostream) const;
+
+ virtual void BuildFileOps(OpRcPtrVec & ops,
+ const Config & config,
+ const ConstContextRcPtr & context,
+ CachedFileRcPtr cachedFile,
+ const FileTransform & fileTransform,
+ TransformDirection dir) const = 0;
+
+ // For logging purposes
+ std::string getName() const;
+ private:
+ FileFormat& operator= (const FileFormat &);
+ };
+
+ typedef std::map<std::string, FileFormat*> FileFormatMap;
+ typedef std::vector<FileFormat*> FileFormatVector;
+
+ // TODO: This interface is ugly. What private API is actually appropriate?
+ // Maybe, instead of exposing the raw formats, we wrap it?
+ // FileCachePair GetFile(const std::string & filepath) and all
+ // lookups will move internal
+
+ class FormatRegistry
+ {
+ public:
+ static FormatRegistry & GetInstance();
+
+ // TODO: Make these return a vector of possible formats
+ FileFormat* getFileFormatByName(const std::string & name) const;
+ FileFormat* getFileFormatForExtension(const std::string & extension) const;
+
+ int getNumRawFormats() const;
+ FileFormat* getRawFormatByIndex(int index) const;
+
+ int getNumFormats(int capability) const;
+ const char * getFormatNameByIndex(int capability, int index) const;
+ const char * getFormatExtensionByIndex(int capability, int index) const;
+ private:
+ FormatRegistry();
+ ~FormatRegistry();
+
+ void registerFileFormat(FileFormat* format);
+
+ FileFormatMap m_formatsByName;
+ FileFormatMap m_formatsByExtension;
+ FileFormatVector m_rawFormats;
+
+ typedef std::vector<std::string> StringVec;
+ StringVec m_readFormatNames;
+ StringVec m_readFormatExtensions;
+ StringVec m_writeFormatNames;
+ StringVec m_writeFormatExtensions;
+ };
+
+ // Registry Builders
+ FileFormat * CreateFileFormat3DL();
+ FileFormat * CreateFileFormatCCC();
+ FileFormat * CreateFileFormatCC();
+ FileFormat * CreateFileFormatCSP();
+ FileFormat * CreateFileFormatHDL();
+ FileFormat * CreateFileFormatIridasItx();
+ FileFormat * CreateFileFormatIridasCube();
+ FileFormat * CreateFileFormatIridasLook();
+ FileFormat * CreateFileFormatPandora();
+ FileFormat * CreateFileFormatSpi1D();
+ FileFormat * CreateFileFormatSpi3D();
+ FileFormat * CreateFileFormatSpiMtx();
+ FileFormat * CreateFileFormatTruelight();
+ FileFormat * CreateFileFormatVF();
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/GpuShaderDesc.cpp b/src/core/GpuShaderDesc.cpp
new file mode 100644
index 0000000..3b20845
--- /dev/null
+++ b/src/core/GpuShaderDesc.cpp
@@ -0,0 +1,131 @@
+/*
+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 <sstream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Mutex.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ class GpuShaderDesc::Impl
+ {
+ public:
+ GpuLanguage language_;
+ std::string functionName_;
+ int lut3DEdgeLen_;
+
+ mutable std::string cacheID_;
+ mutable Mutex cacheIDMutex_;
+
+ Impl() :
+ language_(GPU_LANGUAGE_UNKNOWN),
+ lut3DEdgeLen_(0)
+ {
+ }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ language_ = rhs.language_;
+ functionName_ = rhs.functionName_;
+ lut3DEdgeLen_ = rhs.lut3DEdgeLen_;
+ cacheID_ = rhs.cacheID_;
+ return *this;
+ }
+ };
+
+
+ GpuShaderDesc::GpuShaderDesc()
+ : m_impl(new GpuShaderDesc::Impl)
+ {
+ }
+
+ GpuShaderDesc::~GpuShaderDesc()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+
+ void GpuShaderDesc::setLanguage(GpuLanguage lang)
+ {
+ AutoMutex lock(getImpl()->cacheIDMutex_);
+ getImpl()->language_ = lang;
+ getImpl()->cacheID_ = "";
+ }
+
+ GpuLanguage GpuShaderDesc::getLanguage() const
+ {
+ return getImpl()->language_;
+ }
+
+ void GpuShaderDesc::setFunctionName(const char * name)
+ {
+ AutoMutex lock(getImpl()->cacheIDMutex_);
+ getImpl()->functionName_ = name;
+ getImpl()->cacheID_ = "";
+ }
+
+ const char * GpuShaderDesc::getFunctionName() const
+ {
+ return getImpl()->functionName_.c_str();
+ }
+
+ void GpuShaderDesc::setLut3DEdgeLen(int len)
+ {
+ AutoMutex lock(getImpl()->cacheIDMutex_);
+ getImpl()->lut3DEdgeLen_ = len;
+ getImpl()->cacheID_ = "";
+ }
+
+ int GpuShaderDesc::getLut3DEdgeLen() const
+ {
+ return getImpl()->lut3DEdgeLen_;
+ }
+
+ const char * GpuShaderDesc::getCacheID() const
+ {
+ AutoMutex lock(getImpl()->cacheIDMutex_);
+
+ if(getImpl()->cacheID_.empty())
+ {
+ std::ostringstream os;
+ os << GpuLanguageToString(getImpl()->language_) << " ";
+ os << getImpl()->functionName_ << " ";
+ os << getImpl()->lut3DEdgeLen_;
+ getImpl()->cacheID_ = os.str();
+ }
+
+ return getImpl()->cacheID_.c_str();
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/GpuShaderUtils.cpp b/src/core/GpuShaderUtils.cpp
new file mode 100644
index 0000000..cb8d67b
--- /dev/null
+++ b/src/core/GpuShaderUtils.cpp
@@ -0,0 +1,193 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "GpuShaderUtils.h"
+#include "MathUtils.h"
+
+#include <cmath>
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+ void Write_half4x4(std::ostream & os, const float * m44, GpuLanguage lang)
+ {
+ if(lang == GPU_LANGUAGE_CG)
+ {
+ os << "half4x4(";
+ for(int i=0; i<16; i++)
+ {
+ if(i!=0) os << ", ";
+ os << ClampToNormHalf(m44[i]);
+ }
+ os << ")";
+ }
+ else if(lang == GPU_LANGUAGE_GLSL_1_0 || lang == GPU_LANGUAGE_GLSL_1_3)
+ {
+ os << "mat4(";
+ for(int i=0; i<16; i++)
+ {
+ if(i!=0) os << ", ";
+ os << m44[i]; // Clamping to half is not necessary
+ }
+ os << ")";
+ }
+ else
+ {
+ throw Exception("Unsupported shader language.");
+ }
+ }
+
+ void Write_half4(std::ostream & os, const float * v4, GpuLanguage lang)
+ {
+ if(lang == GPU_LANGUAGE_CG)
+ {
+ os << "half4(";
+ for(int i=0; i<4; i++)
+ {
+ if(i!=0) os << ", ";
+ os << ClampToNormHalf(v4[i]);
+ }
+ os << ")";
+ }
+ else if(lang == GPU_LANGUAGE_GLSL_1_0 || lang == GPU_LANGUAGE_GLSL_1_3)
+ {
+ os << "vec4(";
+ for(int i=0; i<4; i++)
+ {
+ if(i!=0) os << ", ";
+ os << v4[i]; // Clamping to half is not necessary
+ }
+ os << ")";
+ }
+ else
+ {
+ throw Exception("Unsupported shader language.");
+ }
+ }
+
+ void Write_half3(std::ostream & os, const float * v3, GpuLanguage lang)
+ {
+ if(lang == GPU_LANGUAGE_CG)
+ {
+ os << "half3(";
+ for(int i=0; i<3; i++)
+ {
+ if(i!=0) os << ", ";
+ os << ClampToNormHalf(v3[i]);
+ }
+ os << ")";
+ }
+ else if(lang == GPU_LANGUAGE_GLSL_1_0 || lang == GPU_LANGUAGE_GLSL_1_3)
+ {
+ os << "vec3(";
+ for(int i=0; i<3; i++)
+ {
+ if(i!=0) os << ", ";
+ os << v3[i]; // Clamping to half is not necessary
+ }
+ os << ")";
+ }
+ else
+ {
+ throw Exception("Unsupported shader language.");
+ }
+ }
+
+
+
+
+ std::string GpuTextHalf4x4(const float * m44, GpuLanguage lang)
+ {
+ std::ostringstream os;
+ Write_half4x4(os, m44, lang);
+ return os.str();
+ }
+
+ std::string GpuTextHalf4(const float * v4, GpuLanguage lang)
+ {
+ std::ostringstream os;
+ Write_half4(os, v4, lang);
+ return os.str();
+ }
+
+ std::string GpuTextHalf3(const float * v3, GpuLanguage lang)
+ {
+ std::ostringstream os;
+ Write_half3(os, v3, lang);
+ return os.str();
+ }
+
+ // Note that Cg and GLSL have opposite ordering for vec/mtx multiplication
+ void Write_mtx_x_vec(std::ostream & os,
+ const std::string & mtx, const std::string & vec,
+ GpuLanguage lang)
+ {
+ if(lang == GPU_LANGUAGE_CG)
+ {
+ os << "mul( " << mtx << ", " << vec << ")";
+ }
+ else if(lang == GPU_LANGUAGE_GLSL_1_0 || lang == GPU_LANGUAGE_GLSL_1_3)
+ {
+ os << vec << " * " << mtx;
+ }
+ else
+ {
+ throw Exception("Unsupported shader language.");
+ }
+ }
+
+
+ void Write_sampleLut3D_rgb(std::ostream & os, const std::string& variableName,
+ const std::string& lutName, int lut3DEdgeLen,
+ GpuLanguage lang)
+ {
+ float m = ((float) lut3DEdgeLen-1.0f) / (float) lut3DEdgeLen;
+ float b = 1.0f / (2.0f * (float) lut3DEdgeLen);
+
+ if(lang == GPU_LANGUAGE_CG)
+ {
+ os << "tex3D(";
+ os << lutName << ", ";
+ os << m << " * " << variableName << ".rgb + " << b << ").rgb;" << std::endl;
+ }
+ else if(lang == GPU_LANGUAGE_GLSL_1_0 || lang == GPU_LANGUAGE_GLSL_1_3)
+ {
+ os << "texture3D(";
+ os << lutName << ", ";
+ os << m << " * " << variableName << ".rgb + " << b << ").rgb;" << std::endl;
+ }
+ else
+ {
+ throw Exception("Unsupported shader language.");
+ }
+ }
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/GpuShaderUtils.h b/src/core/GpuShaderUtils.h
new file mode 100644
index 0000000..43d16a4
--- /dev/null
+++ b/src/core/GpuShaderUtils.h
@@ -0,0 +1,58 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_GPUSHADERUTILS_H
+#define INCLUDED_OCIO_GPUSHADERUTILS_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+ std::string GpuTextHalf4x4(const float * m44, GpuLanguage lang);
+ std::string GpuTextHalf4(const float * v4, GpuLanguage lang);
+ std::string GpuTextHalf3(const float * v3, GpuLanguage lang);
+
+ void Write_mtx_x_vec(std::ostream & os,
+ const std::string & mtx, const std::string & vec,
+ GpuLanguage lang);
+
+ void Write_half4x4(std::ostream & os, const float * m44, GpuLanguage lang);
+ void Write_half4(std::ostream & os, const float * v4, GpuLanguage lang);
+ void Write_half3(std::ostream & os, const float * v3, GpuLanguage lang);
+
+ // returns vec3
+ void Write_sampleLut3D_rgb(std::ostream & os, const std::string & variableName,
+ const std::string & lutName, int lut3DEdgeLen,
+ GpuLanguage lang);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/GroupTransform.cpp b/src/core/GroupTransform.cpp
new file mode 100644
index 0000000..344714a
--- /dev/null
+++ b/src/core/GroupTransform.cpp
@@ -0,0 +1,196 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+#include "OpBuilders.h"
+
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+ GroupTransformRcPtr GroupTransform::Create()
+ {
+ return GroupTransformRcPtr(new GroupTransform(), &deleter);
+ }
+
+ void GroupTransform::deleter(GroupTransform* t)
+ {
+ delete t;
+ }
+
+ namespace
+ {
+ typedef std::vector<TransformRcPtr> TransformRcPtrVec;
+ }
+
+ class GroupTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+ TransformRcPtrVec vec_;
+
+ Impl() :
+ dir_(TRANSFORM_DIR_FORWARD)
+ { }
+
+ ~Impl()
+ {
+ vec_.clear();
+ }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+
+ vec_.clear();
+
+ for(unsigned int i=0; i<rhs.vec_.size(); ++i)
+ {
+ vec_.push_back(rhs.vec_[i]->createEditableCopy());
+ }
+
+ return *this;
+ }
+ };
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ GroupTransform::GroupTransform()
+ : m_impl(new GroupTransform::Impl)
+ {
+ }
+
+ TransformRcPtr GroupTransform::createEditableCopy() const
+ {
+ GroupTransformRcPtr transform = GroupTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ GroupTransform::~GroupTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ GroupTransform& GroupTransform::operator= (const GroupTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection GroupTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void GroupTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+ int GroupTransform::size() const
+ {
+ return static_cast<int>(getImpl()->vec_.size());
+ }
+
+ ConstTransformRcPtr GroupTransform::getTransform(int index) const
+ {
+ if(index < 0 || index >= (int)getImpl()->vec_.size())
+ {
+ std::ostringstream os;
+ os << "Invalid transform index " << index << ".";
+ throw Exception(os.str().c_str());
+ }
+
+ return getImpl()->vec_[index];
+ }
+
+ void GroupTransform::push_back(const ConstTransformRcPtr& transform)
+ {
+ getImpl()->vec_.push_back(transform->createEditableCopy());
+ }
+
+ void GroupTransform::clear()
+ {
+ getImpl()->vec_.clear();
+ }
+
+ bool GroupTransform::empty() const
+ {
+ return getImpl()->vec_.empty();
+ }
+
+ std::ostream& operator<< (std::ostream& os, const GroupTransform& groupTransform)
+ {
+ for(int i=0; i<groupTransform.size(); ++i)
+ {
+ if(i!=groupTransform.size()-1) os << "\n";
+ ConstTransformRcPtr transform = groupTransform.getTransform(i);
+ os << "\t" << *transform;
+ }
+ return os;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ void BuildGroupOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const GroupTransform& groupTransform,
+ TransformDirection dir)
+ {
+ TransformDirection combinedDir = CombineTransformDirections(dir,
+ groupTransform.getDirection());
+
+ if(combinedDir == TRANSFORM_DIR_FORWARD)
+ {
+ for(int i=0; i<groupTransform.size(); ++i)
+ {
+ ConstTransformRcPtr childTransform = groupTransform.getTransform(i);
+ BuildOps(ops, config, context, childTransform, TRANSFORM_DIR_FORWARD);
+ }
+ }
+ else if(combinedDir == TRANSFORM_DIR_INVERSE)
+ {
+ for(int i=groupTransform.size()-1; i>=0; --i)
+ {
+ ConstTransformRcPtr childTransform = groupTransform.getTransform(i);
+ BuildOps(ops, config, context, childTransform, TRANSFORM_DIR_INVERSE);
+ }
+ }
+ }
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/HashUtils.cpp b/src/core/HashUtils.cpp
new file mode 100644
index 0000000..5b9acd1
--- /dev/null
+++ b/src/core/HashUtils.cpp
@@ -0,0 +1,71 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "HashUtils.h"
+#include "md5/md5.h"
+
+#include <sstream>
+#include <iostream>
+
+OCIO_NAMESPACE_ENTER
+{
+ std::string CacheIDHash(const char * array, int size)
+ {
+ md5_state_t state;
+ md5_byte_t digest[16];
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)array, size);
+ md5_finish(&state, digest);
+
+ return GetPrintableHash(digest);
+ }
+
+ std::string GetPrintableHash(const md5_byte_t * digest)
+ {
+ static char charmap[] = "0123456789abcdef";
+
+ char printableResult[34];
+ char *ptr = printableResult;
+
+ // build a printable string from unprintable chars. first character
+ // of hashed cache IDs is '$', to later check if it's already been hashed
+ *ptr++ = '$';
+ for (int i=0;i<16;++i)
+ {
+ *ptr++ = charmap[(digest[i] & 0x0F)];
+ *ptr++ = charmap[(digest[i] >> 4)];
+ }
+ *ptr++ = 0;
+
+ return std::string(printableResult);
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/HashUtils.h b/src/core/HashUtils.h
new file mode 100644
index 0000000..58481db
--- /dev/null
+++ b/src/core/HashUtils.h
@@ -0,0 +1,48 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_HASHUTILS_H
+#define INCLUDED_OCIO_HASHUTILS_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "md5/md5.h"
+#include <string>
+
+OCIO_NAMESPACE_ENTER
+{
+ std::string CacheIDHash(const char * array, int size);
+
+ // TODO: get rid of md5.h include, make this a generic byte array
+ std::string GetPrintableHash(const md5_byte_t * digest);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
+
diff --git a/src/core/ImageDesc.cpp b/src/core/ImageDesc.cpp
new file mode 100644
index 0000000..63156c8
--- /dev/null
+++ b/src/core/ImageDesc.cpp
@@ -0,0 +1,392 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include <cstdlib>
+#include <sstream>
+
+#include "ImagePacking.h"
+
+OCIO_NAMESPACE_ENTER
+{
+
+ std::ostream& operator<< (std::ostream& os, const ImageDesc& img)
+ {
+ if(const PackedImageDesc * packedImg = dynamic_cast<const PackedImageDesc*>(&img))
+ {
+ os << "<PackedImageDesc ";
+ os << "data=" << packedImg->getData() << ", ";
+ os << "width=" << packedImg->getWidth() << ", ";
+ os << "height=" << packedImg->getHeight() << ", ";
+ os << "numChannels=" << packedImg->getNumChannels() << ", ";
+ os << "chanStrideBytes=" << packedImg->getChanStrideBytes() << ", ";
+ os << "xStrideBytes=" << packedImg->getXStrideBytes() << ", ";
+ os << "yStrideBytes=" << packedImg->getYStrideBytes() << "";
+ os << ">";
+ }
+ else if(const PlanarImageDesc * planarImg = dynamic_cast<const PlanarImageDesc*>(&img))
+ {
+ os << "<PlanarImageDesc ";
+ os << "rData=" << planarImg->getRData() << ", ";
+ os << "gData=" << planarImg->getGData() << ", ";
+ os << "bData=" << planarImg->getBData() << ", ";
+ os << "aData=" << planarImg->getAData() << ", ";
+ os << "width=" << packedImg->getWidth() << ", ";
+ os << "height=" << packedImg->getHeight() << ", ";
+ os << "yStrideBytes=" << planarImg->getYStrideBytes() << "";
+ os << ">";
+ }
+ else
+ {
+ os << "<UnknownImageDesc>";
+ }
+
+ return os;
+ }
+
+ ImageDesc::~ImageDesc()
+ {
+
+ }
+
+
+
+ GenericImageDesc::GenericImageDesc():
+ width(0),
+ height(0),
+ xStrideBytes(0),
+ yStrideBytes(0),
+ rData(NULL),
+ gData(NULL),
+ bData(NULL),
+ aData(NULL)
+ { };
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ GenericImageDesc::~GenericImageDesc()
+ { };
+
+ void GenericImageDesc::init(const ImageDesc& img)
+ {
+ if(const PackedImageDesc * packedImg = dynamic_cast<const PackedImageDesc*>(&img))
+ {
+ width = packedImg->getWidth();
+ height = packedImg->getHeight();
+ long numChannels = packedImg->getNumChannels();
+
+ ptrdiff_t chanStrideBytes = packedImg->getChanStrideBytes();
+ xStrideBytes = packedImg->getXStrideBytes();
+ yStrideBytes = packedImg->getYStrideBytes();
+
+ // AutoStrides will already be resolved by here, in the constructor of the ImageDesc
+ if(chanStrideBytes == AutoStride ||
+ xStrideBytes == AutoStride ||
+ yStrideBytes == AutoStride)
+ {
+ throw Exception("Malformed PackedImageDesc: Unresolved AutoStride.");
+ }
+
+ rData = packedImg->getData();
+ gData = reinterpret_cast<float*>( reinterpret_cast<char*>(rData)
+ + chanStrideBytes );
+ bData = reinterpret_cast<float*>( reinterpret_cast<char*>(rData)
+ + 2*chanStrideBytes );
+ if(numChannels >= 4)
+ {
+ aData = reinterpret_cast<float*>( reinterpret_cast<char*>(rData)
+ + 3*chanStrideBytes );
+ }
+
+ if(rData == NULL)
+ {
+ std::ostringstream os;
+ os << "PackedImageDesc Error: A null image ptr was specified.";
+ throw Exception(os.str().c_str());
+ }
+
+ if(width <= 0 || height <= 0)
+ {
+ std::ostringstream os;
+ os << "PackedImageDesc Error: Image dimensions must be positive for both x,y. '";
+ os << width << "x" << height << "' is not allowed.";
+ throw Exception(os.str().c_str());
+ }
+
+ if(numChannels < 3)
+ {
+ std::ostringstream os;
+ os << "PackedImageDesc Error: Image numChannels must be three (or more) (rgb+). '";
+ os << numChannels << "' is not allowed.";
+ throw Exception(os.str().c_str());
+ }
+ }
+ else if(const PlanarImageDesc * planarImg = dynamic_cast<const PlanarImageDesc*>(&img))
+ {
+ width = planarImg->getWidth();
+ height = planarImg->getHeight();
+ xStrideBytes = sizeof(float);
+ yStrideBytes = planarImg->getYStrideBytes();
+
+ // AutoStrides will already be resolved by here, in the constructor of the ImageDesc
+ if(yStrideBytes == AutoStride)
+ {
+ throw Exception("Malformed PlanarImageDesc: Unresolved AutoStride.");
+ }
+
+ rData = planarImg->getRData();
+ gData = planarImg->getGData();
+ bData = planarImg->getBData();
+ aData = planarImg->getAData();
+
+ if(width <= 0 || height <= 0)
+ {
+ std::ostringstream os;
+ os << "PlanarImageDesc Error: Image dimensions must be positive for both x,y. '";
+ os << width << "x" << height << "' is not allowed.";
+ throw Exception(os.str().c_str());
+ }
+
+ if(rData == NULL || gData == NULL || bData == NULL)
+ {
+ std::ostringstream os;
+ os << "PlanarImageDesc Error: Valid ptrs must be passed for all 3 image rgb color channels.";
+ throw Exception(os.str().c_str());
+ }
+ }
+ else
+ {
+ throw Exception("Unknown ImageDesc type.");
+ }
+ }
+
+ bool GenericImageDesc::isPackedRGBA() const
+ {
+ char* rPtr = reinterpret_cast<char*>(rData);
+ char* gPtr = reinterpret_cast<char*>(gData);
+ char* bPtr = reinterpret_cast<char*>(bData);
+ char* aPtr = reinterpret_cast<char*>(aData);
+
+ if(gPtr-rPtr != sizeof(float)) return false;
+ if(bPtr-gPtr != sizeof(float)) return false;
+ if(aPtr && (aPtr-bPtr != sizeof(float))) return false;
+
+ // Confirm xStrideBytes is a pure float-sized packing
+ // (I.e., it will divide evenly)
+ if(xStrideBytes <= 0) return false;
+ div_t result = div((int) xStrideBytes, (int)sizeof(float));
+ if(result.rem != 0) return false;
+
+ int implicitChannels = result.quot;
+ if(implicitChannels != 4) return false;
+
+ return true;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ class PackedImageDesc::Impl
+ {
+ public:
+ float * data_;
+ long width_;
+ long height_;
+ long numChannels_;
+ ptrdiff_t chanStrideBytes_;
+ ptrdiff_t xStrideBytes_;
+ ptrdiff_t yStrideBytes_;
+
+ Impl() :
+ data_(NULL),
+ width_(0),
+ height_(0),
+ numChannels_(0),
+ chanStrideBytes_(0),
+ xStrideBytes_(0),
+ yStrideBytes_(0)
+ {
+ }
+
+ ~Impl()
+ { }
+ };
+
+ PackedImageDesc::PackedImageDesc(float * data,
+ long width, long height,
+ long numChannels,
+ ptrdiff_t chanStrideBytes,
+ ptrdiff_t xStrideBytes,
+ ptrdiff_t yStrideBytes)
+ : m_impl(new PackedImageDesc::Impl)
+ {
+ getImpl()->data_ = data;
+ getImpl()->width_ = width;
+ getImpl()->height_ = height;
+ getImpl()->numChannels_ = numChannels;
+ getImpl()->chanStrideBytes_ = (chanStrideBytes == AutoStride)
+ ? sizeof(float) : chanStrideBytes;
+ getImpl()->xStrideBytes_ = (xStrideBytes == AutoStride)
+ ? sizeof(float)*numChannels : xStrideBytes;
+ getImpl()->yStrideBytes_ = (yStrideBytes == AutoStride)
+ ? sizeof(float)*width*numChannels : yStrideBytes;
+ }
+
+ PackedImageDesc::~PackedImageDesc()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ float * PackedImageDesc::getData() const
+ {
+ return getImpl()->data_;
+ }
+
+ long PackedImageDesc::getWidth() const
+ {
+ return getImpl()->width_;
+ }
+
+ long PackedImageDesc::getHeight() const
+ {
+ return getImpl()->height_;
+ }
+
+ long PackedImageDesc::getNumChannels() const
+ {
+ return getImpl()->numChannels_;
+ }
+
+ ptrdiff_t PackedImageDesc::getChanStrideBytes() const
+ {
+ return getImpl()->chanStrideBytes_;
+ }
+
+ ptrdiff_t PackedImageDesc::getXStrideBytes() const
+ {
+ return getImpl()->xStrideBytes_;
+ }
+
+ ptrdiff_t PackedImageDesc::getYStrideBytes() const
+ {
+ return getImpl()->yStrideBytes_;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ class PlanarImageDesc::Impl
+ {
+ public:
+ float * rData_;
+ float * gData_;
+ float * bData_;
+ float * aData_;
+ long width_;
+ long height_;
+ ptrdiff_t yStrideBytes_;
+
+ Impl() :
+ rData_(NULL),
+ gData_(NULL),
+ bData_(NULL),
+ aData_(NULL),
+ width_(0),
+ height_(0),
+ yStrideBytes_(0)
+ { }
+
+ ~Impl()
+ { }
+ };
+
+
+ PlanarImageDesc::PlanarImageDesc(float * rData, float * gData, float * bData, float * aData,
+ long width, long height,
+ ptrdiff_t yStrideBytes)
+ : m_impl(new PlanarImageDesc::Impl())
+ {
+ getImpl()->rData_ = rData;
+ getImpl()->gData_ = gData;
+ getImpl()->bData_ = bData;
+ getImpl()->aData_ = aData;
+ getImpl()->width_ = width;
+ getImpl()->height_ = height;
+ getImpl()->yStrideBytes_ = (yStrideBytes == AutoStride)
+ ? sizeof(float)*width : yStrideBytes;
+ }
+
+ PlanarImageDesc::~PlanarImageDesc()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ float* PlanarImageDesc::getRData() const
+ {
+ return getImpl()->rData_;
+ }
+
+ float* PlanarImageDesc::getGData() const
+ {
+ return getImpl()->gData_;
+ }
+
+ float* PlanarImageDesc::getBData() const
+ {
+ return getImpl()->bData_;
+ }
+
+ float* PlanarImageDesc::getAData() const
+ {
+ return getImpl()->aData_;
+ }
+
+ long PlanarImageDesc::getWidth() const
+ {
+ return getImpl()->width_;
+ }
+
+ long PlanarImageDesc::getHeight() const
+ {
+ return getImpl()->height_;
+ }
+
+ ptrdiff_t PlanarImageDesc::getYStrideBytes() const
+ {
+ return getImpl()->yStrideBytes_;
+ }
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/ImagePacking.cpp b/src/core/ImagePacking.cpp
new file mode 100644
index 0000000..256d25c
--- /dev/null
+++ b/src/core/ImagePacking.cpp
@@ -0,0 +1,407 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include <sstream>
+#include <iostream>
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+
+#include "ImagePacking.h"
+
+OCIO_NAMESPACE_ENTER
+{
+
+ namespace
+ {
+ // GENERIC CASE, SLOW BUT ALWAYS WORKS
+
+ void PackRGBAFromImageDesc_Generic(const GenericImageDesc& srcImg,
+ float* outputBuffer,
+ int* numPixelsCopied,
+ int outputBufferSize,
+ long imagePixelStartIndex)
+ {
+ assert(outputBuffer);
+ assert(numPixelsCopied);
+
+ long imgWidth = srcImg.width;
+ long imgHeight = srcImg.height;
+ long imgPixels = imgWidth * imgHeight;
+
+ if(imagePixelStartIndex<0 || imagePixelStartIndex>=imgPixels)
+ {
+ *numPixelsCopied = 0;
+ return;
+ }
+
+ ptrdiff_t xStrideBytes = srcImg.xStrideBytes;
+ ptrdiff_t yStrideBytes = srcImg.yStrideBytes;
+ long yIndex = imagePixelStartIndex / imgWidth;
+ long xIndex = imagePixelStartIndex % imgWidth;
+
+ // Figure out our initial ptr positions
+ char* rRow = reinterpret_cast<char*>(srcImg.rData) +
+ yStrideBytes * yIndex;
+ char* gRow = reinterpret_cast<char*>(srcImg.gData) +
+ yStrideBytes * yIndex;
+ char* bRow = reinterpret_cast<char*>(srcImg.bData) +
+ yStrideBytes * yIndex;
+ char* aRow = NULL;
+
+ float* rPtr = reinterpret_cast<float*>(rRow + xStrideBytes*xIndex);
+ float* gPtr = reinterpret_cast<float*>(gRow + xStrideBytes*xIndex);
+ float* bPtr = reinterpret_cast<float*>(bRow + xStrideBytes*xIndex);
+ float* aPtr = NULL;
+
+ if(srcImg.aData)
+ {
+ aRow = reinterpret_cast<char*>(srcImg.aData) + yStrideBytes * yIndex;
+ aPtr = reinterpret_cast<float*>(aRow + xStrideBytes*xIndex);
+ }
+
+ if(aPtr)
+ {
+ int pixelsCopied = 0;
+ while(pixelsCopied < outputBufferSize)
+ {
+ outputBuffer[4*pixelsCopied] = *rPtr;
+ outputBuffer[4*pixelsCopied+1] = *gPtr;
+ outputBuffer[4*pixelsCopied+2] = *bPtr;
+ outputBuffer[4*pixelsCopied+3] = *aPtr;
+ pixelsCopied++;
+ xIndex++;
+
+ // Jump to the next scanline
+ if(xIndex == imgWidth)
+ {
+ yIndex += 1;
+ if(yIndex == imgHeight)
+ {
+ *numPixelsCopied = pixelsCopied;
+ return;
+ }
+
+ xIndex = 0;
+ rRow += yStrideBytes;
+ gRow += yStrideBytes;
+ bRow += yStrideBytes;
+ aRow += yStrideBytes;
+
+ rPtr = reinterpret_cast<float*>(rRow);
+ gPtr = reinterpret_cast<float*>(gRow);
+ bPtr = reinterpret_cast<float*>(bRow);
+ aPtr = reinterpret_cast<float*>(aRow);
+ }
+ // Jump to the next pixel
+ else
+ {
+ rPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(rPtr) + xStrideBytes);
+ gPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(gPtr) + xStrideBytes);
+ bPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(bPtr) + xStrideBytes);
+ aPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(aPtr) + xStrideBytes);
+ }
+ }
+
+ *numPixelsCopied = pixelsCopied;
+ }
+ else
+ {
+ int pixelsCopied = 0;
+ while(pixelsCopied < outputBufferSize)
+ {
+ outputBuffer[4*pixelsCopied] = *rPtr;
+ outputBuffer[4*pixelsCopied+1] = *gPtr;
+ outputBuffer[4*pixelsCopied+2] = *bPtr;
+ outputBuffer[4*pixelsCopied+3] = 0.0;
+ pixelsCopied++;
+ xIndex++;
+
+ // Jump to the next scanline
+ if(xIndex == imgWidth)
+ {
+ yIndex += 1;
+ if(yIndex == imgHeight)
+ {
+ *numPixelsCopied = pixelsCopied;
+ return;
+ }
+
+ xIndex = 0;
+ rRow += yStrideBytes;
+ gRow += yStrideBytes;
+ bRow += yStrideBytes;
+
+ rPtr = reinterpret_cast<float*>(rRow);
+ gPtr = reinterpret_cast<float*>(gRow);
+ bPtr = reinterpret_cast<float*>(bRow);
+ }
+ // Jump to the next pixel
+ else
+ {
+ rPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(rPtr) + xStrideBytes);
+ gPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(gPtr) + xStrideBytes);
+ bPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(bPtr) + xStrideBytes);
+ }
+ }
+
+ *numPixelsCopied = pixelsCopied;
+ }
+ }
+
+ void UnpackRGBAToImageDesc_Generic(GenericImageDesc& dstImg,
+ float* inputBuffer,
+ int numPixelsToUnpack,
+ long imagePixelStartIndex)
+ {
+ assert(inputBuffer);
+
+ long imgWidth = dstImg.width;
+ long imgHeight = dstImg.height;
+ long imgPixels = imgWidth * imgHeight;
+
+ if(imagePixelStartIndex<0 || imagePixelStartIndex>=imgPixels)
+ {
+ return;
+ }
+
+ ptrdiff_t xStrideBytes = dstImg.xStrideBytes;
+ ptrdiff_t yStrideBytes = dstImg.yStrideBytes;
+ long yIndex = imagePixelStartIndex / imgWidth;
+ long xIndex = imagePixelStartIndex % imgWidth;
+
+ // Figure out our initial ptr positions
+ char* rRow = reinterpret_cast<char*>(dstImg.rData) +
+ yStrideBytes * yIndex;
+ char* gRow = reinterpret_cast<char*>(dstImg.gData) +
+ yStrideBytes * yIndex;
+ char* bRow = reinterpret_cast<char*>(dstImg.bData) +
+ yStrideBytes * yIndex;
+ char* aRow = NULL;
+
+ float* rPtr = reinterpret_cast<float*>(rRow + xStrideBytes*xIndex);
+ float* gPtr = reinterpret_cast<float*>(gRow + xStrideBytes*xIndex);
+ float* bPtr = reinterpret_cast<float*>(bRow + xStrideBytes*xIndex);
+ float* aPtr = NULL;
+
+ if(dstImg.aData)
+ {
+ aRow = reinterpret_cast<char*>(dstImg.aData) + yStrideBytes * yIndex;
+ aPtr = reinterpret_cast<float*>(aRow + xStrideBytes*xIndex);
+ }
+
+ if(aPtr)
+ {
+ int pixelsCopied = 0;
+ while(pixelsCopied < numPixelsToUnpack)
+ {
+ *rPtr = inputBuffer[4*pixelsCopied];
+ *gPtr = inputBuffer[4*pixelsCopied+1];
+ *bPtr = inputBuffer[4*pixelsCopied+2];
+ *aPtr = inputBuffer[4*pixelsCopied+3];
+
+ pixelsCopied++;
+ xIndex++;
+
+ // Jump to the next scanline
+ if(xIndex == imgWidth)
+ {
+ yIndex += 1;
+ if(yIndex == imgHeight)
+ {
+ return;
+ }
+
+ xIndex = 0;
+ rRow += yStrideBytes;
+ gRow += yStrideBytes;
+ bRow += yStrideBytes;
+ aRow += yStrideBytes;
+
+ rPtr = reinterpret_cast<float*>(rRow);
+ gPtr = reinterpret_cast<float*>(gRow);
+ bPtr = reinterpret_cast<float*>(bRow);
+ aPtr = reinterpret_cast<float*>(aRow);
+ }
+ // Jump to the next pixel
+ else
+ {
+ rPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(rPtr) + xStrideBytes);
+ gPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(gPtr) + xStrideBytes);
+ bPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(bPtr) + xStrideBytes);
+ aPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(aPtr) + xStrideBytes);
+ }
+ }
+ }
+ else
+ {
+ int pixelsCopied = 0;
+ while(pixelsCopied < numPixelsToUnpack)
+ {
+ *rPtr = inputBuffer[4*pixelsCopied];
+ *gPtr = inputBuffer[4*pixelsCopied+1];
+ *bPtr = inputBuffer[4*pixelsCopied+2];
+
+ pixelsCopied++;
+ xIndex++;
+
+ // Jump to the next scanline
+ if(xIndex == imgWidth)
+ {
+ yIndex += 1;
+ if(yIndex == imgHeight)
+ {
+ return;
+ }
+
+ xIndex = 0;
+ rRow += yStrideBytes;
+ gRow += yStrideBytes;
+ bRow += yStrideBytes;
+
+ rPtr = reinterpret_cast<float*>(rRow);
+ gPtr = reinterpret_cast<float*>(gRow);
+ bPtr = reinterpret_cast<float*>(bRow);
+ }
+ // Jump to the next pixel
+ else
+ {
+ rPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(rPtr) + xStrideBytes);
+ gPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(gPtr) + xStrideBytes);
+ bPtr = reinterpret_cast<float*>(
+ reinterpret_cast<char*>(bPtr) + xStrideBytes);
+ }
+ }
+ }
+ }
+
+ }
+
+
+ /*
+ namespace
+ {
+
+ void PackRGBAFromImageDesc_RGBAMemcpy(const GenericImageDesc& srcImg,
+ float* outputBuffer,
+ int* numPixelsCopied,
+ int outputBufferSize,
+ int imagePixelStartIndex)
+ {
+ assert(outputBuffer);
+ assert(numPixelsCopied);
+
+ long imgWidth = srcImg.getWidth();
+ long imgHeight = srcImg.getHeight();
+ long imgPixels = srcImg.getNumPixels();
+
+ if(imagePixelStartIndex<0 || imagePixelStartIndex>=imgPixels)
+ {
+ *numPixelsCopied = 0;
+ return;
+ }
+
+ ptrdiff_t xStrideBytes = srcImg.getXStrideBytes();
+ ptrdiff_t yStrideBytes = srcImg.getYStrideBytes();
+ int yIndex = imagePixelStartIndex / imgWidth;
+ int xIndex = imagePixelStartIndex % imgWidth;
+
+ // Figure out our initial ptr positions
+ char* imgRow = reinterpret_cast<char*>(srcImg.getRData()) +
+ yStrideBytes * yIndex;
+
+ char* imgPtr = imgRow + xStrideBytes*xIndex;
+
+ int totalPixelsCopied = 0;
+ int pixelsRemainingToCopy = outputBufferSize;
+
+ while(pixelsRemainingToCopy>0 && yIndex < imgHeight)
+ {
+ int imgScanlinePixels = imgWidth - xIndex;
+ int numPixels = std::min(imgScanlinePixels,
+ pixelsRemainingToCopy);
+ memcpy(outputBuffer+totalPixelsCopied,
+ imgPtr, numPixels);
+
+ yIndex += 1;
+ xIndex = 0;
+
+ imgRow += yStrideBytes;
+ imgPtr = imgRow;
+ totalPixelsCopied += numPixels;
+ pixelsRemainingToCopy -= numPixels;
+ }
+
+ if(numPixelsCopied) *numPixelsCopied = totalPixelsCopied;
+ }
+ }
+ */
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // TODO: Add optimized codepaths to image packing / unpacking
+
+ void PackRGBAFromImageDesc(const GenericImageDesc& srcImg,
+ float* outputBuffer,
+ int* numPixelsCopied,
+ int outputBufferSize,
+ long imagePixelStartIndex)
+ {
+ PackRGBAFromImageDesc_Generic(srcImg, outputBuffer,
+ numPixelsCopied,
+ outputBufferSize,
+ imagePixelStartIndex);
+ }
+
+
+ void UnpackRGBAToImageDesc(GenericImageDesc& dstImg,
+ float* inputBuffer,
+ int numPixelsToUnpack,
+ long imagePixelStartIndex)
+ {
+ UnpackRGBAToImageDesc_Generic(dstImg, inputBuffer,
+ numPixelsToUnpack,
+ imagePixelStartIndex);
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/ImagePacking.h b/src/core/ImagePacking.h
new file mode 100644
index 0000000..a03d1ea
--- /dev/null
+++ b/src/core/ImagePacking.h
@@ -0,0 +1,71 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_IMAGEPACKING_H
+#define INCLUDED_OCIO_IMAGEPACKING_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+OCIO_NAMESPACE_ENTER
+{
+ struct GenericImageDesc
+ {
+ long width;
+ long height;
+ ptrdiff_t xStrideBytes;
+ ptrdiff_t yStrideBytes;
+
+ float* rData;
+ float* gData;
+ float* bData;
+ float* aData;
+
+ GenericImageDesc();
+ ~GenericImageDesc();
+
+ // Resolves all AutoStride
+ void init(const ImageDesc& img);
+
+ bool isPackedRGBA() const;
+ };
+
+ void PackRGBAFromImageDesc(const GenericImageDesc& srcImg,
+ float* outputBuffer,
+ int* numPixelsCopied,
+ int outputBufferSize,
+ long imagePixelStartIndex);
+
+ void UnpackRGBAToImageDesc(GenericImageDesc& dstImg,
+ float* inputBuffer,
+ int numPixelsToUnpack,
+ long imagePixelStartIndex);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/LogOps.cpp b/src/core/LogOps.cpp
new file mode 100644
index 0000000..bc3174e
--- /dev/null
+++ b/src/core/LogOps.cpp
@@ -0,0 +1,499 @@
+/*
+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 <cmath>
+#include <cstring>
+#include <iostream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "GpuShaderUtils.h"
+#include "LogOps.h"
+#include "MathUtils.h"
+
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ const float FLTMIN = std::numeric_limits<float>::min();
+
+ const int FLOAT_DECIMALS = 7;
+
+ // k * log(mx+b, base) + kb
+ // the caller is responsible for base != 1.0
+ // TODO: pull the precomputation into the caller?
+
+ void ApplyLinToLog(float* rgbaBuffer, long numPixels,
+ const float * k,
+ const float * m,
+ const float * b,
+ const float * base,
+ const float * kb)
+ {
+ // We account for the change of base by rolling the multiplier
+ // in with 'k'
+
+ float knew[3] = { k[0] / logf(base[0]),
+ k[1] / logf(base[1]),
+ k[2] / logf(base[0]) };
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ rgbaBuffer[0] = knew[0] * logf(std::max(m[0]*rgbaBuffer[0] + b[0], FLTMIN)) + kb[0];
+ rgbaBuffer[1] = knew[1] * logf(std::max(m[1]*rgbaBuffer[1] + b[1], FLTMIN)) + kb[1];
+ rgbaBuffer[2] = knew[2] * logf(std::max(m[2]*rgbaBuffer[2] + b[2], FLTMIN)) + kb[2];
+
+ rgbaBuffer += 4;
+ }
+ }
+
+ // the caller is responsible for m != 0
+ // the caller is responsible for k != 0
+ // TODO: pull the precomputation into the caller?
+
+ void ApplyLogToLin(float* rgbaBuffer, long numPixels,
+ const float * k,
+ const float * m,
+ const float * b,
+ const float * base,
+ const float * kb)
+ {
+ float kinv[3] = { 1.0f / k[0],
+ 1.0f / k[1],
+ 1.0f / k[2] };
+
+ float minv[3] = { 1.0f / m[0],
+ 1.0f / m[1],
+ 1.0f / m[2] };
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ rgbaBuffer[0] = minv[0] * (powf(base[0], kinv[0]*(rgbaBuffer[0]-kb[0])) - b[0]);
+ rgbaBuffer[1] = minv[1] * (powf(base[1], kinv[1]*(rgbaBuffer[1]-kb[1])) - b[1]);
+ rgbaBuffer[2] = minv[2] * (powf(base[2], kinv[2]*(rgbaBuffer[2]-kb[2])) - b[2]);
+
+ rgbaBuffer += 4;
+ }
+ }
+
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ namespace
+ {
+ class LogOp : public Op
+ {
+ public:
+ LogOp(const float * k,
+ const float * m,
+ const float * b,
+ const float * base,
+ const float * kb,
+ TransformDirection direction);
+ virtual ~LogOp();
+
+ virtual OpRcPtr clone() const;
+
+ virtual std::string getInfo() const;
+ virtual std::string getCacheID() const;
+
+ virtual bool isNoOp() const;
+ virtual bool isSameType(const OpRcPtr & op) const;
+ virtual bool isInverse(const OpRcPtr & op) const;
+ virtual bool hasChannelCrosstalk() const;
+ virtual void finalize();
+ virtual void apply(float* rgbaBuffer, long numPixels) const;
+
+ virtual bool supportsGpuShader() const;
+ virtual void writeGpuShader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc) const;
+
+ private:
+ float m_k[3];
+ float m_m[3];
+ float m_b[3];
+ float m_base[3];
+ float m_kb[3];
+ TransformDirection m_direction;
+
+ std::string m_cacheID;
+ };
+
+ typedef OCIO_SHARED_PTR<LogOp> LogOpRcPtr;
+
+ LogOp::LogOp(const float * k,
+ const float * m,
+ const float * b,
+ const float * base,
+ const float * kb,
+ TransformDirection direction):
+ Op(),
+ m_direction(direction)
+ {
+ if(m_direction == TRANSFORM_DIR_UNKNOWN)
+ {
+ throw Exception("Cannot apply LogOp op, unspecified transform direction.");
+ }
+
+ memcpy(m_k, k, sizeof(float)*3);
+ memcpy(m_m, m, sizeof(float)*3);
+ memcpy(m_b, b, sizeof(float)*3);
+ memcpy(m_base, base, sizeof(float)*3);
+ memcpy(m_kb, kb, sizeof(float)*3);
+ }
+
+ OpRcPtr LogOp::clone() const
+ {
+ OpRcPtr op = OpRcPtr(new LogOp(m_k, m_m, m_b, m_base, m_kb, m_direction));
+ return op;
+ }
+
+ LogOp::~LogOp()
+ { }
+
+ std::string LogOp::getInfo() const
+ {
+ return "<LogOp>";
+ }
+
+ std::string LogOp::getCacheID() const
+ {
+ return m_cacheID;
+ }
+
+ bool LogOp::isNoOp() const
+ {
+ return false;
+ }
+
+ bool LogOp::isSameType(const OpRcPtr & op) const
+ {
+ LogOpRcPtr typedRcPtr = DynamicPtrCast<LogOp>(op);
+ if(!typedRcPtr) return false;
+ return true;
+ }
+
+ bool LogOp::isInverse(const OpRcPtr & op) const
+ {
+ LogOpRcPtr typedRcPtr = DynamicPtrCast<LogOp>(op);
+ if(!typedRcPtr) return false;
+
+ if(GetInverseTransformDirection(m_direction) != typedRcPtr->m_direction)
+ return false;
+
+ float error = std::numeric_limits<float>::min();
+ if(!VecsEqualWithRelError(m_k, 3, typedRcPtr->m_k, 3, error))
+ return false;
+ if(!VecsEqualWithRelError(m_m, 3, typedRcPtr->m_m, 3, error))
+ return false;
+ if(!VecsEqualWithRelError(m_b, 3, typedRcPtr->m_b, 3, error))
+ return false;
+ if(!VecsEqualWithRelError(m_base, 3, typedRcPtr->m_base, 3, error))
+ return false;
+ if(!VecsEqualWithRelError(m_kb, 3, typedRcPtr->m_kb, 3, error))
+ return false;
+
+ return true;
+ }
+
+ bool LogOp::hasChannelCrosstalk() const
+ {
+ return false;
+ }
+
+ void LogOp::finalize()
+ {
+ if(m_direction == TRANSFORM_DIR_FORWARD)
+ {
+ if(VecContainsOne(m_base, 3))
+ throw Exception("LogOp Exception, base cannot be 1.");
+ }
+ else if(m_direction == TRANSFORM_DIR_INVERSE)
+ {
+ if(VecContainsZero(m_m, 3))
+ throw Exception("LogOp Exception, m (slope) cannot be 0.");
+ if(VecContainsZero(m_k, 3))
+ throw Exception("LogOp Exception, k (multiplier) cannot be 0.");
+ }
+
+ std::ostringstream cacheIDStream;
+ cacheIDStream << "<LogOp ";
+ cacheIDStream.precision(FLOAT_DECIMALS);
+ for(int i=0; i<3; ++i)
+ {
+ cacheIDStream << m_k[i] << " ";
+ cacheIDStream << m_m[i] << " ";
+ cacheIDStream << m_b[i] << " ";
+ cacheIDStream << m_base[i] << " ";
+ cacheIDStream << m_kb[i] << " ";
+ }
+
+ cacheIDStream << TransformDirectionToString(m_direction) << " ";
+ cacheIDStream << ">";
+
+ m_cacheID = cacheIDStream.str();
+ }
+
+ void LogOp::apply(float* rgbaBuffer, long numPixels) const
+ {
+ if(m_direction == TRANSFORM_DIR_FORWARD)
+ {
+ ApplyLinToLog(rgbaBuffer, numPixels,
+ m_k, m_m, m_b, m_base, m_kb);
+ }
+ else if(m_direction == TRANSFORM_DIR_INVERSE)
+ {
+ ApplyLogToLin(rgbaBuffer, numPixels,
+ m_k, m_m, m_b, m_base, m_kb);
+ }
+ } // Op::process
+
+ bool LogOp::supportsGpuShader() const
+ {
+ return true;
+ }
+
+ void LogOp::writeGpuShader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc) const
+ {
+ GpuLanguage lang = shaderDesc.getLanguage();
+
+ if(m_direction == TRANSFORM_DIR_FORWARD)
+ {
+ // Lin To Log
+ // k * log(mx+b, base) + kb
+
+ // We account for the change of base by rolling the multiplier
+ // in with 'k'
+
+ float knew[3] = { m_k[0] / logf(m_base[0]),
+ m_k[1] / logf(m_base[1]),
+ m_k[2] / logf(m_base[0]) };
+
+ float clampMin[3] = { FLTMIN, FLTMIN, FLTMIN };
+
+ // TODO: Switch to f32 for internal Cg processing?
+ if(lang == GPU_LANGUAGE_CG)
+ {
+ clampMin[0] = static_cast<float>(GetHalfNormMin());
+ clampMin[1] = static_cast<float>(GetHalfNormMin());
+ clampMin[2] = static_cast<float>(GetHalfNormMin());
+ }
+
+ // Decompose into 2 steps
+ // 1) clamp(mx+b)
+ // 2) knew * log(x) + kb
+
+ shader << pixelName << ".rgb = ";
+ shader << "max(" << GpuTextHalf3(clampMin,lang) << ", ";
+ shader << GpuTextHalf3(m_m,lang) << " * ";
+ shader << pixelName << ".rgb + ";
+ shader << GpuTextHalf3(m_b,lang) << ");\n";
+
+ shader << pixelName << ".rgb = ";
+ shader << GpuTextHalf3(knew,lang) << " * ";
+ shader << "log(" << pixelName << ".rgb) + ";
+ shader << GpuTextHalf3(m_kb,lang) << ";\n";
+ }
+ else if(m_direction == TRANSFORM_DIR_INVERSE)
+ {
+ float kinv[3] = { 1.0f / m_k[0],
+ 1.0f / m_k[1],
+ 1.0f / m_k[2] };
+
+ float minv[3] = { 1.0f / m_m[0],
+ 1.0f / m_m[1],
+ 1.0f / m_m[2] };
+
+ // Decompose into 3 steps
+ // 1) kinv * ( x - kb)
+ // 2) pow(base, x)
+ // 3) minv * (x - b)
+
+ shader << pixelName << ".rgb = ";
+ shader << GpuTextHalf3(kinv,lang) << " * (";
+ shader << pixelName << ".rgb - ";
+ shader << GpuTextHalf3(m_kb,lang) << ");\n";
+
+ shader << pixelName << ".rgb = pow(";
+ shader << GpuTextHalf3(m_base,lang) << ", ";
+ shader << pixelName << ".rgb);\n";
+
+ shader << pixelName << ".rgb = ";
+ shader << GpuTextHalf3(minv,lang) << " * (";
+ shader << pixelName << ".rgb - ";
+ shader << GpuTextHalf3(m_b,lang) << ");\n";
+ }
+ }
+
+ } // Anon namespace
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ void CreateLogOp(OpRcPtrVec & ops,
+ const float * k,
+ const float * m,
+ const float * b,
+ const float * base,
+ const float * kb,
+ TransformDirection direction)
+ {
+ ops.push_back( LogOpRcPtr(new LogOp(k, m, b, base, kb, direction)) );
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+OIIO_ADD_TEST(LogOps, LinToLog)
+{
+ float k[3] = { 0.18f, 0.18f, 0.18f };
+ float m[3] = { 2.0f, 2.0f, 2.0f };
+ float b[3] = { 0.1f, 0.1f, 0.1f };
+ float base[3] = { 10.0f, 10.0f, 10.0f };
+ float kb[3] = { 1.0f, 1.0f, 1.0f };
+
+
+ float data[8] = { 0.01f, 0.1f, 1.0f, 1.0f,
+ 10.0f, 100.0f, 1000.0f, 1.0f, };
+
+ float result[8] = { 0.8342526242885725f,
+ 0.90588182584953925f,
+ 1.057999473052105462f,
+ 1.0f,
+ 1.23457529033568797f,
+ 1.41422447595451795f,
+ 1.59418930777214063f,
+ 1.0f };
+
+ OCIO::OpRcPtrVec ops;
+ CreateLogOp(ops, k, m, b, base, kb, OCIO::TRANSFORM_DIR_FORWARD);
+
+ FinalizeOpVec(ops);
+
+ // Apply the result
+ for(OCIO::OpRcPtrVec::size_type i = 0, size = ops.size(); i < size; ++i)
+ {
+ ops[i]->apply(data, 2);
+ }
+
+ for(int i=0; i<8; ++i)
+ {
+ OIIO_CHECK_CLOSE( data[i], result[i], 1.0e-3 );
+ }
+}
+
+OIIO_ADD_TEST(LogOps, LogToLin)
+{
+ float k[3] = { 0.18f, 0.18f, 0.18f };
+ float m[3] = { 2.0f, 2.0f, 2.0f };
+ float b[3] = { 0.1f, 0.1f, 0.1f };
+ float base[3] = { 10.0f, 10.0f, 10.0f };
+ float kb[3] = { 1.0f, 1.0f, 1.0f };
+
+ float data[8] = { 0.8342526242885725f,
+ 0.90588182584953925f,
+ 1.057999473052105462f,
+ 1.0f,
+ 1.23457529033568797f,
+ 1.41422447595451795f,
+ 1.59418930777214063f,
+ 1.0f };
+
+ float result[8] = { 0.01f, 0.1f, 1.0f, 1.0f,
+ 10.0f, 100.0f, 1000.0f, 1.0f, };
+
+ OCIO::OpRcPtrVec ops;
+ CreateLogOp(ops, k, m, b, base, kb, OCIO::TRANSFORM_DIR_INVERSE);
+
+ FinalizeOpVec(ops);
+
+ // Apply the result
+ for(OCIO::OpRcPtrVec::size_type i = 0, size = ops.size(); i < size; ++i)
+ {
+ ops[i]->apply(data, 2);
+ }
+
+ for(int i=0; i<8; ++i)
+ {
+ OIIO_CHECK_CLOSE( data[i], result[i], 1.0e-3 );
+ }
+}
+
+OIIO_ADD_TEST(LogOps, Inverse)
+{
+ float k[3] = { 0.18f, 0.18f, 0.18f };
+ float m[3] = { 2.0f, 2.0f, 2.0f };
+ float b[3] = { 0.1f, 0.1f, 0.1f };
+ float base[3] = { 10.0f, 10.0f, 10.0f };
+ float kb[3] = { 1.0f, 1.0f, 1.0f };
+
+ OCIO::OpRcPtrVec ops;
+ CreateLogOp(ops, k, m, b, base, kb, OCIO::TRANSFORM_DIR_FORWARD);
+
+ CreateLogOp(ops, k, m, b, base, kb, OCIO::TRANSFORM_DIR_INVERSE);
+
+ base[0] += 1e-5f;
+ CreateLogOp(ops, k, m, b, base, kb, OCIO::TRANSFORM_DIR_INVERSE);
+ CreateLogOp(ops, k, m, b, base, kb, OCIO::TRANSFORM_DIR_FORWARD);
+
+ OIIO_CHECK_EQUAL(ops.size(), 4);
+
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[1]));
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[2]));
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[3]->clone()));
+
+ OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[0]), false);
+ OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[1]), true);
+ OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[2]), false);
+ OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[3]), false);
+
+ OIIO_CHECK_EQUAL(ops[1]->isInverse(ops[0]), true);
+ OIIO_CHECK_EQUAL(ops[1]->isInverse(ops[2]), false);
+ OIIO_CHECK_EQUAL(ops[1]->isInverse(ops[3]), false);
+
+ OIIO_CHECK_EQUAL(ops[2]->isInverse(ops[2]), false);
+ OIIO_CHECK_EQUAL(ops[2]->isInverse(ops[3]), true);
+
+ OIIO_CHECK_EQUAL(ops[3]->isInverse(ops[3]), false);
+}
+
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/LogOps.h b/src/core/LogOps.h
new file mode 100644
index 0000000..0356fd8
--- /dev/null
+++ b/src/core/LogOps.h
@@ -0,0 +1,57 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_LOGOPS_H
+#define INCLUDED_OCIO_LOGOPS_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Op.h"
+
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+ // output = k * log(mx+b, base) + kb
+ // This does not affect alpha
+ // In the forward direction this is lin->log
+ // All input vectors are size 3 (including base)
+
+ void CreateLogOp(OpRcPtrVec & ops,
+ const float * k,
+ const float * m,
+ const float * b,
+ const float * base,
+ const float * kb,
+ TransformDirection direction);
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/LogTransform.cpp b/src/core/LogTransform.cpp
new file mode 100644
index 0000000..8ed7bb0
--- /dev/null
+++ b/src/core/LogTransform.cpp
@@ -0,0 +1,157 @@
+/*
+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 <cstring>
+#include <sstream>
+#include <vector>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "LogOps.h"
+#include "OpBuilders.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ LogTransformRcPtr LogTransform::Create()
+ {
+ return LogTransformRcPtr(new LogTransform(), &deleter);
+ }
+
+ void LogTransform::deleter(LogTransform* t)
+ {
+ delete t;
+ }
+
+
+ class LogTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+ float base_;
+
+ Impl() :
+ dir_(TRANSFORM_DIR_FORWARD),
+ base_(2.0)
+ { }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+ base_ = rhs.base_;
+ return *this;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ LogTransform::LogTransform()
+ : m_impl(new LogTransform::Impl)
+ {
+ }
+
+ TransformRcPtr LogTransform::createEditableCopy() const
+ {
+ LogTransformRcPtr transform = LogTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ LogTransform::~LogTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ LogTransform& LogTransform::operator= (const LogTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection LogTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void LogTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+
+ float LogTransform::getBase() const
+ {
+ return getImpl()->base_;
+ }
+
+ void LogTransform::setBase(float val)
+ {
+ getImpl()->base_ = val;
+ }
+
+ std::ostream& operator<< (std::ostream& os, const LogTransform& t)
+ {
+ os << "<LogTransform ";
+ os << "base=" << t.getBase() << ", ";
+ os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
+ os << ">\n";
+
+ return os;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ void BuildLogOps(OpRcPtrVec & ops,
+ const Config& /*config*/,
+ const LogTransform& transform,
+ TransformDirection dir)
+ {
+ TransformDirection combinedDir = CombineTransformDirections(dir,
+ transform.getDirection());
+
+ float basescalar = transform.getBase();
+ float base[3] = { basescalar, basescalar, basescalar };
+
+ float k[3] = { 1.0f, 1.0f, 1.0f };
+ float m[3] = { 1.0f, 1.0f, 1.0f };
+ float b[3] = { 0.0f, 0.0f, 0.0f };
+ float kb[3] = { 0.0f, 0.0f, 0.0f };
+
+ // output = k * log(mx+b, base) + kb
+ CreateLogOp(ops,
+ k, m, b, base, kb,
+ combinedDir);
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/Logging.cpp b/src/core/Logging.cpp
new file mode 100644
index 0000000..7846ac2
--- /dev/null
+++ b/src/core/Logging.cpp
@@ -0,0 +1,156 @@
+/*
+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 <cstdlib>
+#include <iostream>
+#include <sstream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Logging.h"
+#include "Mutex.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ const char * OCIO_LOGGING_LEVEL_ENVVAR = "OCIO_LOGGING_LEVEL";
+ const LoggingLevel OCIO_DEFAULT_LOGGING_LEVEL = LOGGING_LEVEL_INFO;
+
+ Mutex g_logmutex;
+ LoggingLevel g_logginglevel = LOGGING_LEVEL_UNKNOWN;
+ bool g_initialized = false;
+ bool g_loggingOverride = false;
+
+ // You must manually acquire the logging mutex before calling this.
+ // This will set g_logginglevel, g_initialized, g_loggingOverride
+ void InitLogging()
+ {
+ if(g_initialized) return;
+
+ g_initialized = true;
+
+ char* levelstr = std::getenv(OCIO_LOGGING_LEVEL_ENVVAR);
+ if(levelstr)
+ {
+ g_loggingOverride = true;
+ g_logginglevel = LoggingLevelFromString(levelstr);
+
+ if(g_logginglevel == LOGGING_LEVEL_UNKNOWN)
+ {
+ std::cerr << "[OpenColorIO Warning]: Invalid $OCIO_LOGGING_LEVEL specified. ";
+ std::cerr << "Options: none (0), warning (1), info (2), debug (3)" << std::endl;
+ g_logginglevel = OCIO_DEFAULT_LOGGING_LEVEL;
+ }
+ }
+ else
+ {
+ g_logginglevel = OCIO_DEFAULT_LOGGING_LEVEL;
+ }
+ }
+ }
+
+ LoggingLevel GetLoggingLevel()
+ {
+ AutoMutex lock(g_logmutex);
+ InitLogging();
+
+ return g_logginglevel;
+ }
+
+ void SetLoggingLevel(LoggingLevel level)
+ {
+ AutoMutex lock(g_logmutex);
+ InitLogging();
+
+ // Calls to SetLoggingLevel are ignored if OCIO_LOGGING_LEVEL_ENVVAR
+ // is specified. This is to allow users to optionally debug OCIO at
+ // runtime even in applications that disable logging.
+
+ if(!g_loggingOverride)
+ {
+ g_logginglevel = level;
+ }
+ }
+
+ void LogWarning(const std::string & text)
+ {
+ AutoMutex lock(g_logmutex);
+ InitLogging();
+
+ if(g_logginglevel<LOGGING_LEVEL_WARNING) return;
+
+ std::vector<std::string> parts;
+ pystring::split( pystring::rstrip(text), parts, "\n");
+
+ for(unsigned int i=0; i<parts.size(); ++i)
+ {
+ std::cerr << "[OpenColorIO Warning]: " << parts[i] << std::endl;
+ }
+ }
+
+ void LogInfo(const std::string & text)
+ {
+ AutoMutex lock(g_logmutex);
+ InitLogging();
+
+ if(g_logginglevel<LOGGING_LEVEL_INFO) return;
+
+ std::vector<std::string> parts;
+ pystring::split( pystring::rstrip(text), parts, "\n");
+
+ for(unsigned int i=0; i<parts.size(); ++i)
+ {
+ std::cerr << "[OpenColorIO Info]: " << parts[i] << std::endl;
+ }
+ }
+
+ void LogDebug(const std::string & text)
+ {
+ AutoMutex lock(g_logmutex);
+ InitLogging();
+
+ if(g_logginglevel<LOGGING_LEVEL_DEBUG) return;
+
+ std::vector<std::string> parts;
+ pystring::split( pystring::rstrip(text), parts, "\n");
+
+ for(unsigned int i=0; i<parts.size(); ++i)
+ {
+ std::cerr << "[OpenColorIO Debug]: " << parts[i] << std::endl;
+ }
+ }
+
+ bool IsDebugLoggingEnabled()
+ {
+ return (GetLoggingLevel()>=LOGGING_LEVEL_DEBUG);
+ }
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/Logging.h b/src/core/Logging.h
new file mode 100644
index 0000000..173e642
--- /dev/null
+++ b/src/core/Logging.h
@@ -0,0 +1,47 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_LOGGING_H
+#define INCLUDED_OCIO_LOGGING_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include <string>
+
+OCIO_NAMESPACE_ENTER
+{
+ void LogWarning(const std::string & text);
+ void LogInfo(const std::string & text);
+ void LogDebug(const std::string & text);
+
+ bool IsDebugLoggingEnabled();
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/Look.cpp b/src/core/Look.cpp
new file mode 100644
index 0000000..fd1bfe7
--- /dev/null
+++ b/src/core/Look.cpp
@@ -0,0 +1,163 @@
+/*
+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 <cstring>
+#include <sstream>
+#include <vector>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+OCIO_NAMESPACE_ENTER
+{
+ LookRcPtr Look::Create()
+ {
+ return LookRcPtr(new Look(), &deleter);
+ }
+
+ void Look::deleter(Look* c)
+ {
+ delete c;
+ }
+
+ class Look::Impl
+ {
+ public:
+ std::string name_;
+ std::string processSpace_;
+ TransformRcPtr transform_;
+ TransformRcPtr inverseTransform_;
+
+ Impl()
+ { }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ name_ = rhs.name_;
+ processSpace_ = rhs.processSpace_;
+
+ transform_ = rhs.transform_;
+ if(transform_) transform_ = transform_->createEditableCopy();
+
+ inverseTransform_ = rhs.inverseTransform_;
+ if(inverseTransform_) inverseTransform_ = inverseTransform_->createEditableCopy();
+
+ return *this;
+ }
+ };
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ Look::Look()
+ : m_impl(new Look::Impl)
+ {
+ }
+
+ Look::~Look()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ LookRcPtr Look::createEditableCopy() const
+ {
+ LookRcPtr cs = Look::Create();
+ *cs->m_impl = *m_impl;
+ return cs;
+ }
+
+ const char * Look::getName() const
+ {
+ return getImpl()->name_.c_str();
+ }
+
+ void Look::setName(const char * name)
+ {
+ getImpl()->name_ = name;
+ }
+
+ const char * Look::getProcessSpace() const
+ {
+ return getImpl()->processSpace_.c_str();
+ }
+
+ void Look::setProcessSpace(const char * processSpace)
+ {
+ getImpl()->processSpace_ = processSpace;
+ }
+
+ ConstTransformRcPtr Look::getTransform() const
+ {
+ return getImpl()->transform_;
+ }
+
+ void Look::setTransform(const ConstTransformRcPtr & transform)
+ {
+ getImpl()->transform_ = transform->createEditableCopy();
+ }
+
+ ConstTransformRcPtr Look::getInverseTransform() const
+ {
+ return getImpl()->inverseTransform_;
+ }
+
+ void Look::setInverseTransform(const ConstTransformRcPtr & transform)
+ {
+ getImpl()->inverseTransform_ = transform->createEditableCopy();
+ }
+
+
+ std::ostream& operator<< (std::ostream& os, const Look& look)
+ {
+ os << "<Look ";
+ os << "name=" << look.getName() << ", ";
+ os << "processSpace=" << look.getProcessSpace() << ", ";
+
+ if(look.getTransform())
+ {
+ os << "\tTransform: ";
+ os << *look.getTransform();
+ }
+
+ if(look.getInverseTransform())
+ {
+ os << "\tInverseTransform: ";
+ os << *look.getInverseTransform();
+ }
+
+ os << ">";
+
+ return os;
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/LookParse.cpp b/src/core/LookParse.cpp
new file mode 100644
index 0000000..3c6db03
--- /dev/null
+++ b/src/core/LookParse.cpp
@@ -0,0 +1,309 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include <algorithm>
+
+#include "LookParse.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+#include <iostream>
+
+OCIO_NAMESPACE_ENTER
+{
+ void LookParseResult::Token::parse(const std::string & str)
+ {
+ // Assert no commas, colons, or | in str.
+
+ if(pystring::startswith(str, "+"))
+ {
+ name = pystring::lstrip(str, "+");
+ dir = TRANSFORM_DIR_FORWARD;
+ }
+ // TODO: Handle --
+ else if(pystring::startswith(str, "-"))
+ {
+ name = pystring::lstrip(str, "-");
+ dir = TRANSFORM_DIR_INVERSE;
+ }
+ else
+ {
+ name = str;
+ dir = TRANSFORM_DIR_FORWARD;
+ }
+ }
+
+ void LookParseResult::Token::serialize(std::ostream & os) const
+ {
+ if(dir==TRANSFORM_DIR_FORWARD)
+ {
+ os << name;
+ }
+ else if(dir==TRANSFORM_DIR_INVERSE)
+ {
+ os << "-" << name;
+ }
+ else
+ {
+ os << "?" << name;
+ }
+ }
+
+ void LookParseResult::serialize(std::ostream & os, const Tokens & tokens)
+ {
+ for(unsigned int i=0; i<tokens.size(); ++i)
+ {
+ if(i!=0) os << ", ";
+ tokens[i].serialize(os);
+ }
+ }
+
+ const LookParseResult::Options & LookParseResult::parse(const std::string & looksstr)
+ {
+ m_options.clear();
+
+ std::string strippedlooks = pystring::strip(looksstr);
+ if(strippedlooks.empty())
+ {
+ return m_options;
+ }
+
+ std::vector<std::string> options;
+ pystring::split(strippedlooks, options, "|");
+
+ std::vector<std::string> vec;
+
+ for(unsigned int optionsindex=0;
+ optionsindex<options.size();
+ ++optionsindex)
+ {
+ LookParseResult::Tokens tokens;
+
+ vec.clear();
+ SplitStringEnvStyle(vec, options[optionsindex].c_str());
+ for(unsigned int i=0; i<vec.size(); ++i)
+ {
+ LookParseResult::Token t;
+ t.parse(vec[i]);
+ tokens.push_back(t);
+ }
+
+ m_options.push_back(tokens);
+ }
+
+ return m_options;
+ }
+
+ const LookParseResult::Options & LookParseResult::getOptions() const
+ {
+ return m_options;
+ }
+
+ bool LookParseResult::empty() const
+ {
+ return m_options.empty();
+ }
+
+ void LookParseResult::reverse()
+ {
+ // m_options itself should NOT be reversed.
+ // The individual looks
+ // need to be applied in the inverse direction. But, the precedence
+ // for which option to apply is to be maintained!
+
+ for(unsigned int optionindex=0;
+ optionindex<m_options.size();
+ ++optionindex)
+ {
+ std::reverse(m_options[optionindex].begin(), m_options[optionindex].end());
+
+ for(unsigned int tokenindex=0;
+ tokenindex<m_options[optionindex].size();
+ ++tokenindex)
+ {
+ m_options[optionindex][tokenindex].dir =
+ GetInverseTransformDirection(
+ m_options[optionindex][tokenindex].dir);
+ }
+ }
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+OCIO_NAMESPACE_USING
+
+#include "UnitTest.h"
+
+OIIO_ADD_TEST(LookParse, Parse)
+{
+ LookParseResult r;
+
+ {
+ const LookParseResult::Options & options = r.parse("");
+ OIIO_CHECK_EQUAL(options.size(), 0);
+ OIIO_CHECK_EQUAL(options.empty(), true);
+ }
+
+ {
+ const LookParseResult::Options & options = r.parse(" ");
+ OIIO_CHECK_EQUAL(options.size(), 0);
+ OIIO_CHECK_EQUAL(options.empty(), true);
+ }
+
+ {
+ const LookParseResult::Options & options = r.parse("cc");
+ OIIO_CHECK_EQUAL(options.size(), 1);
+ OIIO_CHECK_EQUAL(options[0][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[0][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options.empty(), false);
+ }
+
+ {
+ const LookParseResult::Options & options = r.parse("+cc");
+ OIIO_CHECK_EQUAL(options.size(), 1);
+ OIIO_CHECK_EQUAL(options[0][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[0][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options.empty(), false);
+ }
+
+ {
+ const LookParseResult::Options & options = r.parse(" +cc");
+ OIIO_CHECK_EQUAL(options.size(), 1);
+ OIIO_CHECK_EQUAL(options[0][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[0][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options.empty(), false);
+ }
+
+ {
+ const LookParseResult::Options & options = r.parse(" +cc ");
+ OIIO_CHECK_EQUAL(options.size(), 1);
+ OIIO_CHECK_EQUAL(options[0][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[0][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options.empty(), false);
+ }
+
+ {
+ const LookParseResult::Options & options = r.parse("+cc,-di");
+ OIIO_CHECK_EQUAL(options.size(), 1);
+ OIIO_CHECK_EQUAL(options[0].size(), 2);
+ OIIO_CHECK_EQUAL(options[0][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[0][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options[0][1].name, "di");
+ OIIO_CHECK_EQUAL(options[0][1].dir, TRANSFORM_DIR_INVERSE);
+ OIIO_CHECK_EQUAL(options.empty(), false);
+ }
+
+ {
+ const LookParseResult::Options & options = r.parse(" +cc , -di");
+ OIIO_CHECK_EQUAL(options.size(), 1);
+ OIIO_CHECK_EQUAL(options[0].size(), 2);
+ OIIO_CHECK_EQUAL(options[0][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[0][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options[0][1].name, "di");
+ OIIO_CHECK_EQUAL(options[0][1].dir, TRANSFORM_DIR_INVERSE);
+ OIIO_CHECK_EQUAL(options.empty(), false);
+ }
+
+ {
+ const LookParseResult::Options & options = r.parse(" +cc : -di");
+ OIIO_CHECK_EQUAL(options.size(), 1);
+ OIIO_CHECK_EQUAL(options[0].size(), 2);
+ OIIO_CHECK_EQUAL(options[0][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[0][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options[0][1].name, "di");
+ OIIO_CHECK_EQUAL(options[0][1].dir, TRANSFORM_DIR_INVERSE);
+ OIIO_CHECK_EQUAL(options.empty(), false);
+ }
+
+ {
+ const LookParseResult::Options & options = r.parse("+cc, -di |-cc");
+ OIIO_CHECK_EQUAL(options.size(), 2);
+ OIIO_CHECK_EQUAL(options[0].size(), 2);
+ OIIO_CHECK_EQUAL(options[0][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[0][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options[0][1].name, "di");
+ OIIO_CHECK_EQUAL(options[0][1].dir, TRANSFORM_DIR_INVERSE);
+ OIIO_CHECK_EQUAL(options[1].size(), 1);
+ OIIO_CHECK_EQUAL(options.empty(), false);
+ OIIO_CHECK_EQUAL(options[1][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[1][0].dir, TRANSFORM_DIR_INVERSE);
+ }
+
+ {
+ const LookParseResult::Options & options = r.parse("+cc, -di |-cc| ");
+ OIIO_CHECK_EQUAL(options.size(), 3);
+ OIIO_CHECK_EQUAL(options[0].size(), 2);
+ OIIO_CHECK_EQUAL(options[0][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[0][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options[0][1].name, "di");
+ OIIO_CHECK_EQUAL(options[0][1].dir, TRANSFORM_DIR_INVERSE);
+ OIIO_CHECK_EQUAL(options[1].size(), 1);
+ OIIO_CHECK_EQUAL(options.empty(), false);
+ OIIO_CHECK_EQUAL(options[1][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[1][0].dir, TRANSFORM_DIR_INVERSE);
+ OIIO_CHECK_EQUAL(options[2].size(), 1);
+ OIIO_CHECK_EQUAL(options[2][0].name, "");
+ OIIO_CHECK_EQUAL(options[2][0].dir, TRANSFORM_DIR_FORWARD);
+ }
+}
+
+OIIO_ADD_TEST(LookParse, Reverse)
+{
+ LookParseResult r;
+
+ {
+ r.parse("+cc, -di |-cc| ");
+ r.reverse();
+ const LookParseResult::Options & options = r.getOptions();
+
+ OIIO_CHECK_EQUAL(options.size(), 3);
+ OIIO_CHECK_EQUAL(options[0].size(), 2);
+ OIIO_CHECK_EQUAL(options[0][1].name, "cc");
+ OIIO_CHECK_EQUAL(options[0][1].dir, TRANSFORM_DIR_INVERSE);
+ OIIO_CHECK_EQUAL(options[0][0].name, "di");
+ OIIO_CHECK_EQUAL(options[0][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options[1].size(), 1);
+ OIIO_CHECK_EQUAL(options.empty(), false);
+ OIIO_CHECK_EQUAL(options[1][0].name, "cc");
+ OIIO_CHECK_EQUAL(options[1][0].dir, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(options[2].size(), 1);
+ OIIO_CHECK_EQUAL(options[2][0].name, "");
+ OIIO_CHECK_EQUAL(options[2][0].dir, TRANSFORM_DIR_INVERSE);
+ }
+
+
+}
+
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/LookParse.h b/src/core/LookParse.h
new file mode 100644
index 0000000..e867ce7
--- /dev/null
+++ b/src/core/LookParse.h
@@ -0,0 +1,79 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_PARSED_LOOK_H
+#define INCLUDED_OCIO_PARSED_LOOK_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+ // Looks parse structures
+ // This is contains a list, where each option entry corresponds to
+ // an "or" separated looks token list.
+ // I.e, " +cc,-onset | +cc " parses to TWO options: (+cc,-onset), (+cc)
+
+ class LookParseResult
+ {
+ public:
+ struct Token
+ {
+ std::string name;
+ TransformDirection dir;
+
+ Token():
+ dir(TRANSFORM_DIR_FORWARD) {}
+
+ void parse(const std::string & str);
+ void serialize(std::ostream & os) const;
+ };
+
+ typedef std::vector<Token> Tokens;
+
+ static void serialize(std::ostream & os, const Tokens & tokens);
+
+ typedef std::vector<Tokens> Options;
+
+ const Options & parse(const std::string & looksstr);
+
+ const Options & getOptions() const;
+ bool empty() const;
+
+ void reverse();
+
+ private:
+ Options m_options;
+ };
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/LookTransform.cpp b/src/core/LookTransform.cpp
new file mode 100644
index 0000000..0092d16
--- /dev/null
+++ b/src/core/LookTransform.cpp
@@ -0,0 +1,405 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include <algorithm>
+#include <iterator>
+
+#include "LookParse.h"
+#include "NoOps.h"
+#include "OpBuilders.h"
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+
+OCIO_NAMESPACE_ENTER
+{
+ LookTransformRcPtr LookTransform::Create()
+ {
+ return LookTransformRcPtr(new LookTransform(), &deleter);
+ }
+
+ void LookTransform::deleter(LookTransform* t)
+ {
+ delete t;
+ }
+
+
+ class LookTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+ std::string src_;
+ std::string dst_;
+ std::string looks_;
+
+ Impl() :
+ dir_(TRANSFORM_DIR_FORWARD)
+ { }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+ src_ = rhs.src_;
+ dst_ = rhs.dst_;
+ looks_ = rhs.looks_;
+ return *this;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ LookTransform::LookTransform()
+ : m_impl(new LookTransform::Impl)
+ {
+ }
+
+ TransformRcPtr LookTransform::createEditableCopy() const
+ {
+ LookTransformRcPtr transform = LookTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ LookTransform::~LookTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ LookTransform& LookTransform::operator= (const LookTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection LookTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void LookTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+ const char * LookTransform::getSrc() const
+ {
+ return getImpl()->src_.c_str();
+ }
+
+ void LookTransform::setSrc(const char * src)
+ {
+ getImpl()->src_ = src;
+ }
+
+ const char * LookTransform::getDst() const
+ {
+ return getImpl()->dst_.c_str();
+ }
+
+ void LookTransform::setDst(const char * dst)
+ {
+ getImpl()->dst_ = dst;
+ }
+
+ void LookTransform::setLooks(const char * looks)
+ {
+ getImpl()->looks_ = looks;
+ }
+
+ const char * LookTransform::getLooks() const
+ {
+ return getImpl()->looks_.c_str();
+ }
+
+ std::ostream& operator<< (std::ostream& os, const LookTransform& t)
+ {
+ os << "<LookTransform ";
+ os << "src=" << t.getSrc() << ", ";
+ os << "dst=" << t.getDst() << ", ";
+ os << "looks=" << t.getLooks() << ", ";
+ os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
+ os << ">\n";
+ return os;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+
+
+
+ namespace
+ {
+
+ void RunLookTokens(OpRcPtrVec & ops,
+ ConstColorSpaceRcPtr & currentColorSpace,
+ bool skipColorSpaceConversions,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const LookParseResult::Tokens & lookTokens)
+ {
+ if(lookTokens.empty()) return;
+
+ for(unsigned int i=0; i<lookTokens.size(); ++i)
+ {
+ const std::string & lookName = lookTokens[i].name;
+
+ if(lookName.empty()) continue;
+
+ ConstLookRcPtr look = config.getLook(lookName.c_str());
+ if(!look)
+ {
+ std::ostringstream os;
+ os << "RunLookTokens error. ";
+ os << "The specified look, '" << lookName;
+ os << "', cannot be found. ";
+ if(config.getNumLooks() == 0)
+ {
+ os << " (No looks defined in config)";
+ }
+ else
+ {
+ os << " (looks: ";
+ for(int ii=0; ii<config.getNumLooks(); ++ii)
+ {
+ if(ii != 0) os << ", ";
+ os << config.getLookNameByIndex(ii);
+ }
+ os << ")";
+ }
+
+ throw Exception(os.str().c_str());
+ }
+
+ // Put the new ops into a temp array, to see if it's a no-op
+ // If it is a no-op, dont bother doing the colorspace conversion.
+ OpRcPtrVec tmpOps;
+
+ if(lookTokens[i].dir == TRANSFORM_DIR_FORWARD)
+ {
+ CreateLookNoOp(tmpOps, lookName);
+ if(look->getTransform())
+ {
+ BuildOps(tmpOps, config, context, look->getTransform(), TRANSFORM_DIR_FORWARD);
+ }
+ else if(look->getInverseTransform())
+ {
+ BuildOps(tmpOps, config, context, look->getInverseTransform(), TRANSFORM_DIR_INVERSE);
+ }
+ }
+ else if(lookTokens[i].dir == TRANSFORM_DIR_INVERSE)
+ {
+ CreateLookNoOp(tmpOps, std::string("-") + lookName);
+ if(look->getInverseTransform())
+ {
+ BuildOps(tmpOps, config, context, look->getInverseTransform(), TRANSFORM_DIR_FORWARD);
+ }
+ else if(look->getTransform())
+ {
+ BuildOps(tmpOps, config, context, look->getTransform(), TRANSFORM_DIR_INVERSE);
+ }
+ }
+ else
+ {
+ std::ostringstream os;
+ os << "BuildLookOps error. ";
+ os << "The specified look, '" << lookTokens[i].name;
+ os << "' has an ill-defined transform direction.";
+ throw Exception(os.str().c_str());
+ }
+
+ if(!IsOpVecNoOp(tmpOps))
+ {
+ if(!skipColorSpaceConversions)
+ {
+ ConstColorSpaceRcPtr processColorSpace = config.getColorSpace(look->getProcessSpace());
+ if(!processColorSpace)
+ {
+ std::ostringstream os;
+ os << "RunLookTokens error. ";
+ os << "The specified look, '" << lookTokens[i].name;
+ os << "', requires processing in the ColorSpace, '";
+ os << look->getProcessSpace() << "' which is not defined.";
+ throw Exception(os.str().c_str());
+ }
+
+ BuildColorSpaceOps(ops, config, context,
+ currentColorSpace,
+ processColorSpace);
+ currentColorSpace = processColorSpace;
+ }
+
+ std::copy(tmpOps.begin(), tmpOps.end(), std::back_inserter(ops));
+ }
+ }
+ }
+
+ } // anon namespace
+
+ ////////////////////////////////////////////////////////////////////////////
+
+
+ void BuildLookOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const LookTransform & lookTransform,
+ TransformDirection dir)
+ {
+ ConstColorSpaceRcPtr src, dst;
+ src = config.getColorSpace( lookTransform.getSrc() );
+ dst = config.getColorSpace( lookTransform.getDst() );
+
+ if(!src)
+ {
+ std::ostringstream os;
+ os << "BuildLookOps error.";
+ os << "The specified lookTransform specifies a src colorspace, '";
+ os << lookTransform.getSrc() << "', which is not defined.";
+ throw Exception(os.str().c_str());
+ }
+
+ if(!dst)
+ {
+ std::ostringstream os;
+ os << "BuildLookOps error.";
+ os << "The specified lookTransform specifies a dst colorspace, '";
+ os << lookTransform.getDst() << "', which is not defined.";
+ throw Exception(os.str().c_str());
+ }
+
+ LookParseResult looks;
+ looks.parse(lookTransform.getLooks());
+
+ // We must handle the inverse src/dst colorspace transformation explicitly.
+ if(dir == TRANSFORM_DIR_INVERSE)
+ {
+ std::swap(src, dst);
+ looks.reverse();
+ }
+ else if(dir == TRANSFORM_DIR_UNKNOWN)
+ {
+ std::ostringstream os;
+ os << "BuildLookOps error. A valid transform direction must be specified.";
+ throw Exception(os.str().c_str());
+ }
+
+ ConstColorSpaceRcPtr currentColorSpace = src;
+ BuildLookOps(ops,
+ currentColorSpace,
+ false,
+ config,
+ context,
+ looks);
+
+ BuildColorSpaceOps(ops, config, context,
+ currentColorSpace,
+ dst);
+ }
+
+ void BuildLookOps(OpRcPtrVec & ops,
+ ConstColorSpaceRcPtr & currentColorSpace,
+ bool skipColorSpaceConversions,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const LookParseResult & looks)
+ {
+ const LookParseResult::Options & options = looks.getOptions();
+
+ if(options.empty())
+ {
+ // Do nothing
+ }
+ else if(options.size() == 1)
+ {
+ // As an optimization, if we only have a single look option,
+ // just push back onto the final location
+ RunLookTokens(ops,
+ currentColorSpace,
+ skipColorSpaceConversions,
+ config,
+ context,
+ options[0]);
+ }
+ else
+ {
+ // If we have multiple look options, try each one in order,
+ // and if we can create the ops without a missing file exception,
+ // push back it's results and return
+
+ bool success = false;
+ std::ostringstream os;
+
+ OpRcPtrVec tmpOps;
+ ConstColorSpaceRcPtr cs;
+
+ for(unsigned int i=0; i<options.size(); ++i)
+ {
+ cs = currentColorSpace;
+ tmpOps.clear();
+
+ try
+ {
+ RunLookTokens(tmpOps,
+ cs,
+ skipColorSpaceConversions,
+ config,
+ context,
+ options[i]);
+ success = true;
+ break;
+ }
+ catch(ExceptionMissingFile & e)
+ {
+ if(i != 0) os << " ... ";
+
+ os << "(";
+ LookParseResult::serialize(os, options[i]);
+ os << ") " << e.what();
+ }
+ }
+
+ if(success)
+ {
+ currentColorSpace = cs;
+ std::copy(tmpOps.begin(), tmpOps.end(), std::back_inserter(ops));
+ }
+ else
+ {
+ throw ExceptionMissingFile(os.str().c_str());
+ }
+ }
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/Lut1DOp.cpp b/src/core/Lut1DOp.cpp
new file mode 100644
index 0000000..8363536
--- /dev/null
+++ b/src/core/Lut1DOp.cpp
@@ -0,0 +1,1038 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "HashUtils.h"
+#include "Lut1DOp.h"
+#include "MathUtils.h"
+#include "SSE.h"
+
+#include <algorithm>
+#include <cmath>
+#include <sstream>
+#include <iostream>
+
+OCIO_NAMESPACE_ENTER
+{
+ Lut1D::Lut1D() :
+ maxerror(std::numeric_limits<float>::min()),
+ errortype(ERROR_RELATIVE)
+ {
+ for(int i=0; i<3; ++i)
+ {
+ from_min[i] = 0.0f;
+ from_max[i] = 1.0f;
+ }
+ }
+
+ Lut1DRcPtr Lut1D::Create()
+ {
+ return Lut1DRcPtr(new Lut1D());
+ }
+
+
+ namespace
+ {
+ bool IsLut1DNoOp(const Lut1D & lut,
+ float maxerror,
+ ErrorType errortype)
+ {
+ // If tolerance not positive, skip the check.
+ if(!(maxerror > 0.0)) return false;
+
+ for(int channel = 0; channel<3; ++channel)
+ {
+ if(lut.luts[channel].size() == 0) continue;
+
+ float inorm = 1.0f / (static_cast<float>(lut.luts[channel].size()) - 1.0f);
+
+ float m = lut.from_max[channel] - lut.from_min[channel];
+ float b = lut.from_min[channel];
+
+ for(unsigned int i=0; i<lut.luts[channel].size(); ++i)
+ {
+ float x = static_cast<float>(i) * inorm;
+ float identval = m*x+b;
+ float lutval = lut.luts[channel][i];
+
+ if(errortype == ERROR_ABSOLUTE)
+ {
+ if(!equalWithAbsError(identval, lutval, maxerror))
+ {
+ return false;
+ }
+ }
+ else if(errortype == ERROR_RELATIVE)
+ {
+ if(!equalWithRelError(identval, lutval, maxerror))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ throw Exception("Unknown error type.");
+ }
+ }
+ }
+
+ return true;
+ }
+ }
+
+ std::string Lut1D::getCacheID() const
+ {
+ AutoMutex lock(m_mutex);
+
+ if(luts[0].empty() || luts[1].empty() || luts[2].empty())
+ throw Exception("Cannot compute cacheID of invalid Lut1D");
+
+ if(!m_cacheID.empty())
+ return m_cacheID;
+
+ finalize();
+ return m_cacheID;
+ }
+
+ bool Lut1D::isNoOp() const
+ {
+ AutoMutex lock(m_mutex);
+
+ if(luts[0].empty() || luts[1].empty() || luts[2].empty())
+ throw Exception("Cannot compute noOp of invalid Lut1D");
+
+ if(!m_cacheID.empty())
+ return m_isNoOp;
+
+ finalize();
+
+ return m_isNoOp;
+ }
+
+ void Lut1D::unfinalize()
+ {
+ AutoMutex lock(m_mutex);
+ m_cacheID = "";
+ m_isNoOp = false;
+ }
+
+ void Lut1D::finalize() const
+ {
+ m_isNoOp = IsLut1DNoOp(*this, maxerror, errortype);
+
+ if(m_isNoOp)
+ {
+ m_cacheID = "<NULL 1D>";
+ }
+ else
+ {
+ md5_state_t state;
+ md5_byte_t digest[16];
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)from_min, 3*sizeof(float));
+ md5_append(&state, (const md5_byte_t *)from_max, 3*sizeof(float));
+
+ for(int i=0; i<3; ++i)
+ {
+ md5_append( &state, (const md5_byte_t *)&(luts[i][0]),
+ (int) (luts[i].size()*sizeof(float)) );
+ }
+
+ md5_finish(&state, digest);
+
+ m_cacheID = GetPrintableHash(digest);
+ }
+ }
+
+
+ namespace
+ {
+ // Note: This function assumes that minVal is less than maxVal
+ inline int clamp(float k, float minVal, float maxVal)
+ {
+ return static_cast<int>(roundf(std::max(std::min(k, maxVal), minVal)));
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // Nearest Forward
+
+ inline float lookupNearest_1D(float index, float maxIndex, const float * simple_lut)
+ {
+ return simple_lut[clamp(index, 0.0f, maxIndex)];
+ }
+
+ void Lut1D_Nearest(float* rgbaBuffer, long numPixels, const Lut1D & lut)
+ {
+ float maxIndex[3];
+ float mInv[3];
+ float b[3];
+ float mInv_x_maxIndex[3];
+ const float* startPos[3];
+
+ for(int i=0; i<3; ++i)
+ {
+ maxIndex[i] = (float) (lut.luts[i].size() - 1);
+ mInv[i] = 1.0f / (lut.from_max[i] - lut.from_min[i]);
+ b[i] = lut.from_min[i];
+ mInv_x_maxIndex[i] = (float) (mInv[i] * maxIndex[i]);
+ startPos[i] = &(lut.luts[i][0]);
+ }
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ if(!isnan(rgbaBuffer[0]))
+ rgbaBuffer[0] = lookupNearest_1D(mInv_x_maxIndex[0] * (rgbaBuffer[0] - b[0]), maxIndex[0], startPos[0]);
+ if(!isnan(rgbaBuffer[1]))
+ rgbaBuffer[1] = lookupNearest_1D(mInv_x_maxIndex[1] * (rgbaBuffer[1] - b[1]), maxIndex[1], startPos[1]);
+ if(!isnan(rgbaBuffer[2]))
+ rgbaBuffer[2] = lookupNearest_1D(mInv_x_maxIndex[2] * (rgbaBuffer[2] - b[2]), maxIndex[2], startPos[2]);
+
+ rgbaBuffer += 4;
+ }
+ }
+#ifdef USE_SSE
+ void Lut1D_Nearest_SSE(float* rgbaBuffer, long numPixels, const Lut1D & lut)
+ {
+ // orig: 546 ms
+ // curr: 91 ms
+
+ // These are all sized 4, to allow simpler sse loading
+ float maxIndex[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ float mInv[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ float b[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ float mInv_x_maxIndex[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ const float* startPos[3];
+
+ for(int i=0; i<3; ++i)
+ {
+ maxIndex[i] = (float) (lut.luts[i].size() - 1);
+ mInv[i] = 1.0f / (lut.from_max[i] - lut.from_min[i]);
+ b[i] = lut.from_min[i];
+ mInv_x_maxIndex[i] = (float) (mInv[i] * maxIndex[i]);
+ startPos[i] = &(lut.luts[i][0]);
+ }
+
+ const __m128 _zero = _mm_setzero_ps();
+ const __m128 _mInv_x_maxIndex = _mm_loadu_ps(mInv_x_maxIndex);
+ const __m128 _b = _mm_loadu_ps(b);
+ const __m128 _maxIndex = _mm_loadu_ps(maxIndex);
+ const __m128 _half = _mm_set1_ps(0.5f);
+
+ float result[4];
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ // TODO: SSE Optimized nancheck
+
+ __m128 p = _mm_loadu_ps(rgbaBuffer);
+
+ // mInv_x_maxIndex * (p - b)
+ p = _mm_sub_ps(p, _b);
+ p = _mm_mul_ps(p, _mInv_x_maxIndex);
+
+ // clamp _zero <= b <= _maxIndex
+ p = _mm_max_ps(p, _zero);
+ p = _mm_min_ps(p, _maxIndex);
+
+ // add 0.5f for rounding
+ p = _mm_add_ps(p, _half);
+
+ _mm_storeu_ps(result, p);
+
+
+ // TODO: use native SSE to convert to an int?
+ // _mm_cvttss_si32
+ // Converts the lower single-precision, floating-point value of
+ // a to a 32-bit integer with truncation
+ //
+ // _mm_cvttps_pi32 converts 2 floats to 2 32-bit packed ints,
+ // with truncation
+
+ if(!isnan(result[0]))
+ rgbaBuffer[0] = startPos[0][(int)(result[0])];
+ if(!isnan(result[1]))
+ rgbaBuffer[1] = startPos[1][(int)(result[1])];
+ if(!isnan(result[2]))
+ rgbaBuffer[2] = startPos[2][(int)(result[2])];
+
+ rgbaBuffer += 4;
+ }
+ }
+#endif
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // Linear Forward
+
+ inline float lookupLinear_1D(float index, float maxIndex, const float * simple_lut)
+ {
+ int indexLow = clamp(std::floor(index), 0.0f, maxIndex);
+ int indexHigh = clamp(std::ceil(index), 0.0f, maxIndex);
+ float delta = index - (float)indexLow;
+ return (1.0f-delta) * simple_lut[indexLow] + delta * simple_lut[indexHigh];
+ }
+
+ void Lut1D_Linear(float* rgbaBuffer, long numPixels, const Lut1D & lut)
+ {
+ float maxIndex[3];
+ float mInv[3];
+ float b[3];
+ float mInv_x_maxIndex[3];
+ const float* startPos[3];
+
+ for(int i=0; i<3; ++i)
+ {
+ maxIndex[i] = (float) (lut.luts[i].size() - 1);
+ mInv[i] = 1.0f / (lut.from_max[i] - lut.from_min[i]);
+ b[i] = lut.from_min[i];
+ mInv_x_maxIndex[i] = (float) (mInv[i] * maxIndex[i]);
+ startPos[i] = &(lut.luts[i][0]);
+ }
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ if(!isnan(rgbaBuffer[0]))
+ rgbaBuffer[0] = lookupLinear_1D(mInv_x_maxIndex[0] * (rgbaBuffer[0] - b[0]), maxIndex[0], startPos[0]);
+ if(!isnan(rgbaBuffer[1]))
+ rgbaBuffer[1] = lookupLinear_1D(mInv_x_maxIndex[1] * (rgbaBuffer[1] - b[1]), maxIndex[1], startPos[1]);
+ if(!isnan(rgbaBuffer[2]))
+ rgbaBuffer[2] = lookupLinear_1D(mInv_x_maxIndex[2] * (rgbaBuffer[2] - b[2]), maxIndex[2], startPos[2]);
+
+ rgbaBuffer += 4;
+ }
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // Nearest Inverse
+
+ inline float reverseLookupNearest_1D(const float v, const float *start, const float *end)
+ {
+ const float *lowbound = std::lower_bound(start, end, v);
+ if (lowbound != start) --lowbound;
+
+ const float *highbound = lowbound;
+ if (highbound < end - 1) ++highbound;
+
+ // NOTE: Not dividing result by /(size-1) anymore
+ if (fabsf(v - *lowbound) < fabsf(v - *highbound))
+ {
+ return (float)(lowbound-start);
+ }
+ else
+ {
+ return (float)(highbound-start);
+ }
+ }
+
+ void Lut1D_NearestInverse(float* rgbaBuffer, long numPixels, const Lut1D & lut)
+ {
+ float m[3];
+ float b[3];
+ const float* startPos[3];
+ const float* endPos[3];
+
+ for(int i=0; i<3; ++i)
+ {
+ m[i] = (lut.from_max[i] - lut.from_min[i]);
+ b[i] = lut.from_min[i];
+
+ startPos[i] = &(lut.luts[i][0]);
+ endPos[i] = startPos[i] + lut.luts[i].size();
+
+ // Roll the size division into m as an optimization
+ m[i] /= (float) (lut.luts[i].size() - 1);
+ }
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ if(!isnan(rgbaBuffer[0]))
+ rgbaBuffer[0] = m[0] * reverseLookupNearest_1D(rgbaBuffer[0], startPos[0], endPos[0]) + b[0];
+ if(!isnan(rgbaBuffer[1]))
+ rgbaBuffer[1] = m[1] * reverseLookupNearest_1D(rgbaBuffer[1], startPos[1], endPos[1]) + b[1];
+ if(!isnan(rgbaBuffer[2]))
+ rgbaBuffer[2] = m[2] * reverseLookupNearest_1D(rgbaBuffer[2], startPos[2], endPos[2]) + b[2];
+
+ rgbaBuffer += 4;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+ // Linear Inverse
+
+ inline float reverseLookupLinear_1D(const float v, const float *start, const float *end, float invMaxIndex)
+ {
+ const float *lowbound = std::lower_bound(start, end, v);
+ if (lowbound != start) --lowbound;
+
+ const float *highbound = lowbound;
+ if (highbound < end - 1) ++highbound;
+
+ // lowbound is the lower bound, highbound is the upper bound.
+ float delta = 0.0;
+ if (*highbound > *lowbound)
+ {
+ delta = (v - *lowbound) / (*highbound - *lowbound);
+ }
+
+ return ((float)(lowbound - start) + delta) * invMaxIndex;
+ }
+
+ void Lut1D_LinearInverse(float* rgbaBuffer, long numPixels, const Lut1D & lut)
+ {
+ float m[3];
+ float b[3];
+ const float* startPos[3];
+ const float* endPos[3];
+ float invMaxIndex[3];
+
+ for(int i=0; i<3; ++i)
+ {
+ m[i] = (lut.from_max[i] - lut.from_min[i]);
+ b[i] = lut.from_min[i];
+
+ startPos[i] = &(lut.luts[i][0]);
+ endPos[i] = startPos[i] + lut.luts[i].size();
+
+ invMaxIndex[i] = 1.0f / (float) (lut.luts[i].size() - 1);
+ }
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ if(!isnan(rgbaBuffer[0]))
+ rgbaBuffer[0] = m[0] * reverseLookupLinear_1D(rgbaBuffer[0], startPos[0], endPos[0], invMaxIndex[0]) + b[0];
+ if(!isnan(rgbaBuffer[1]))
+ rgbaBuffer[1] = m[1] * reverseLookupLinear_1D(rgbaBuffer[1], startPos[1], endPos[1], invMaxIndex[0]) + b[1];
+ if(!isnan(rgbaBuffer[2]))
+ rgbaBuffer[2] = m[2] * reverseLookupLinear_1D(rgbaBuffer[2], startPos[2], endPos[2], invMaxIndex[0]) + b[2];
+
+ rgbaBuffer += 4;
+ }
+ }
+
+
+ }
+
+ namespace
+ {
+ class Lut1DOp : public Op
+ {
+ public:
+ Lut1DOp(const Lut1DRcPtr & lut,
+ Interpolation interpolation,
+ TransformDirection direction);
+ virtual ~Lut1DOp();
+
+ virtual OpRcPtr clone() const;
+
+ virtual std::string getInfo() const;
+ virtual std::string getCacheID() const;
+
+ virtual bool isNoOp() const;
+ virtual bool isSameType(const OpRcPtr & op) const;
+ virtual bool isInverse(const OpRcPtr & op) const;
+ virtual bool hasChannelCrosstalk() const;
+ virtual void finalize();
+ virtual void apply(float* rgbaBuffer, long numPixels) const;
+
+ virtual bool supportsGpuShader() const;
+ virtual void writeGpuShader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc) const;
+
+ private:
+ const Lut1DRcPtr m_lut;
+ Interpolation m_interpolation;
+ TransformDirection m_direction;
+
+ std::string m_cacheID;
+ };
+
+ typedef OCIO_SHARED_PTR<Lut1DOp> Lut1DOpRcPtr;
+
+
+ Lut1DOp::Lut1DOp(const Lut1DRcPtr & lut,
+ Interpolation interpolation,
+ TransformDirection direction):
+ Op(),
+ m_lut(lut),
+ m_interpolation(interpolation),
+ m_direction(direction)
+ {
+ }
+
+ OpRcPtr Lut1DOp::clone() const
+ {
+ OpRcPtr op = OpRcPtr(new Lut1DOp(m_lut, m_interpolation, m_direction));
+ return op;
+ }
+
+ Lut1DOp::~Lut1DOp()
+ { }
+
+ std::string Lut1DOp::getInfo() const
+ {
+ return "<Lut1DOp>";
+ }
+
+ std::string Lut1DOp::getCacheID() const
+ {
+ return m_cacheID;
+ }
+
+ // TODO: compute real value for isNoOp
+ bool Lut1DOp::isNoOp() const
+ {
+ return false;
+ }
+
+ bool Lut1DOp::isSameType(const OpRcPtr & op) const
+ {
+ Lut1DOpRcPtr typedRcPtr = DynamicPtrCast<Lut1DOp>(op);
+ if(!typedRcPtr) return false;
+ return true;
+ }
+
+ bool Lut1DOp::isInverse(const OpRcPtr & op) const
+ {
+ Lut1DOpRcPtr typedRcPtr = DynamicPtrCast<Lut1DOp>(op);
+ if(!typedRcPtr) return false;
+
+ if(GetInverseTransformDirection(m_direction) != typedRcPtr->m_direction)
+ return false;
+
+ return (m_lut->getCacheID() == typedRcPtr->m_lut->getCacheID());
+ }
+
+ bool Lut1DOp::hasChannelCrosstalk() const
+ {
+ return false;
+ }
+
+ void Lut1DOp::finalize()
+ {
+ if(m_direction == TRANSFORM_DIR_UNKNOWN)
+ {
+ throw Exception("Cannot apply lut1d op, unspecified transform direction.");
+ }
+
+ // Validate the requested interpolation type
+ switch(m_interpolation)
+ {
+ // These are the allowed values.
+ case INTERP_NEAREST:
+ case INTERP_LINEAR:
+ break;
+ case INTERP_BEST:
+ m_interpolation = INTERP_LINEAR;
+ break;
+ case INTERP_UNKNOWN:
+ throw Exception("Cannot apply Lut1DOp, unspecified interpolation.");
+ break;
+ case INTERP_TETRAHEDRAL:
+ throw Exception("Cannot apply Lut1DOp, tetrahedral interpolation is not allowed for 1d luts.");
+ break;
+ default:
+ throw Exception("Cannot apply Lut1DOp, invalid interpolation specified.");
+ }
+
+ if(m_lut->luts[0].empty() || m_lut->luts[1].empty() || m_lut->luts[2].empty())
+ {
+ throw Exception("Cannot apply lut1d op, no lut data provided.");
+ }
+
+ // Create the cacheID
+ std::ostringstream cacheIDStream;
+ cacheIDStream << "<Lut1DOp ";
+ cacheIDStream << m_lut->getCacheID() << " ";
+ cacheIDStream << InterpolationToString(m_interpolation) << " ";
+ cacheIDStream << TransformDirectionToString(m_direction) << " ";
+ cacheIDStream << ">";
+ m_cacheID = cacheIDStream.str();
+ }
+
+ void Lut1DOp::apply(float* rgbaBuffer, long numPixels) const
+ {
+ if(m_direction == TRANSFORM_DIR_FORWARD)
+ {
+ if(m_interpolation == INTERP_NEAREST)
+ {
+#ifdef USE_SSE
+ Lut1D_Nearest_SSE(rgbaBuffer, numPixels, *m_lut);
+#else
+ Lut1D_Nearest(rgbaBuffer, numPixels, *m_lut);
+#endif
+ }
+ else if(m_interpolation == INTERP_LINEAR)
+ {
+ Lut1D_Linear(rgbaBuffer, numPixels, *m_lut);
+ }
+ }
+ else if(m_direction == TRANSFORM_DIR_INVERSE)
+ {
+ if(m_interpolation == INTERP_NEAREST)
+ {
+ Lut1D_NearestInverse(rgbaBuffer, numPixels, *m_lut);
+ }
+ else if(m_interpolation == INTERP_LINEAR)
+ {
+ Lut1D_LinearInverse(rgbaBuffer, numPixels, *m_lut);
+ }
+ }
+ }
+
+ bool Lut1DOp::supportsGpuShader() const
+ {
+ return false;
+ }
+
+ void Lut1DOp::writeGpuShader(std::ostream & /*shader*/,
+ const std::string & /*pixelName*/,
+ const GpuShaderDesc & /*shaderDesc*/) const
+ {
+ throw Exception("Lut1DOp does not support analytical shader generation.");
+ }
+ }
+
+ void CreateLut1DOp(OpRcPtrVec & ops,
+ const Lut1DRcPtr & lut,
+ Interpolation interpolation,
+ TransformDirection direction)
+ {
+ if(lut->isNoOp()) return;
+
+ // TODO: Detect if lut1d can be exactly approximated as y = mx + b
+ // If so, return a mtx instead.
+
+ ops.push_back( OpRcPtr(new Lut1DOp(lut, interpolation, direction)) );
+ }
+
+
+ void GenerateIdentityLut1D(float* img, int numElements, int numChannels)
+ {
+ if(!img) return;
+ int numChannelsToFill = std::min(3, numChannels);
+
+ float scale = 1.0f / ((float) numElements - 1.0f);
+ for(int i=0; i<numElements; i++)
+ {
+ for(int c=0; c<numChannelsToFill; ++c)
+ {
+ img[numChannels*i+c] = scale * (float)(i);
+ }
+ }
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+#include <cstring>
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+OIIO_ADD_TEST(Lut1DOp, NoOp)
+{
+ // Make an identity lut
+ OCIO::Lut1DRcPtr lut = OCIO::Lut1D::Create();
+ lut->from_min[0] = 0.0f;
+ lut->from_min[1] = 0.0f;
+ lut->from_min[2] = 0.0f;
+
+ lut->from_max[0] = 1.0f;
+ lut->from_max[1] = 1.0f;
+ lut->from_max[2] = 1.0f;
+
+ int size = 256;
+ for(int i=0; i<size; ++i)
+ {
+ float x = (float)i / (float)(size-1);
+ for(int c=0; c<3; ++c)
+ {
+ lut->luts[c].push_back(x);
+ }
+ }
+
+ lut->maxerror = 1e-5f;
+ lut->errortype = OCIO::ERROR_RELATIVE;
+ OIIO_CHECK_EQUAL(lut->isNoOp(), true);
+
+ lut->unfinalize();
+ lut->maxerror = 1e-5f;
+ lut->errortype = OCIO::ERROR_ABSOLUTE;
+ OIIO_CHECK_EQUAL(lut->isNoOp(), true);
+
+ // Edit the lut
+ // These should NOT be identity
+ lut->unfinalize();
+ lut->luts[0][125] += 1e-3f;
+ lut->maxerror = 1e-5f;
+ lut->errortype = OCIO::ERROR_RELATIVE;
+ OIIO_CHECK_EQUAL(lut->isNoOp(), false);
+
+ lut->unfinalize();
+ lut->maxerror = 1e-5f;
+ lut->errortype = OCIO::ERROR_ABSOLUTE;
+ OIIO_CHECK_EQUAL(lut->isNoOp(), false);
+}
+
+
+OIIO_ADD_TEST(Lut1DOp, FiniteValue)
+{
+ // Make a lut that squares the input
+ OCIO::Lut1DRcPtr lut = OCIO::Lut1D::Create();
+ lut->from_min[0] = 0.0f;
+ lut->from_min[1] = 0.0f;
+ lut->from_min[2] = 0.0f;
+
+ lut->from_max[0] = 1.0f;
+ lut->from_max[1] = 1.0f;
+ lut->from_max[2] = 1.0f;
+
+ int size = 256;
+ for(int i=0; i<size; ++i)
+ {
+ float x = (float)i / (float)(size-1);
+ float x2 = x*x;
+
+ for(int c=0; c<3; ++c)
+ {
+ lut->luts[c].push_back(x2);
+ }
+ }
+
+ lut->maxerror = 1e-5f;
+ lut->errortype = OCIO::ERROR_RELATIVE;
+ OIIO_CHECK_EQUAL(lut->isNoOp(), false);
+
+ float inputBuffer_linearforward[4] = { 0.5f, 0.6f, 0.7f, 0.5f };
+ float outputBuffer_linearforward[4] = { 0.25f, 0.36f, 0.49f, 0.5f };
+ OCIO::Lut1D_Linear(inputBuffer_linearforward, 1, *lut);
+ for(int i=0; i <4; ++i)
+ {
+ OIIO_CHECK_CLOSE(inputBuffer_linearforward[i], outputBuffer_linearforward[i], 1e-5f);
+ }
+
+ float inputBuffer_nearestforward[4] = { 0.5f, 0.6f, 0.7f, 0.5f };
+ float outputBuffer_nearestforward[4] = { 0.25f, 0.36f, 0.49f, 0.5f };
+ OCIO::Lut1D_Nearest(inputBuffer_nearestforward, 1, *lut);
+ for(int i=0; i <4; ++i)
+ {
+ OIIO_CHECK_CLOSE(inputBuffer_nearestforward[i], outputBuffer_nearestforward[i], 1e-2f);
+ }
+
+ float inputBuffer_linearinverse[4] = { 0.5f, 0.6f, 0.7f, 0.5f };
+ float outputBuffer_linearinverse[4] = { 0.25f, 0.36f, 0.49f, 0.5f };
+ OCIO::Lut1D_LinearInverse(outputBuffer_linearinverse, 1, *lut);
+ for(int i=0; i <4; ++i)
+ {
+ OIIO_CHECK_CLOSE(inputBuffer_linearinverse[i], outputBuffer_linearinverse[i], 1e-5f);
+ }
+
+ float inputBuffer_nearestinverse[4] = { 0.5f, 0.6f, 0.7f, 0.5f };
+ float outputBuffer_nearestinverse[4] = { 0.25f, 0.36f, 0.49f, 0.5f };
+ OCIO::Lut1D_NearestInverse(outputBuffer_nearestinverse, 1, *lut);
+ for(int i=0; i <4; ++i)
+ {
+ OIIO_CHECK_CLOSE(inputBuffer_nearestinverse[i], outputBuffer_nearestinverse[i], 1e-2f);
+ }
+}
+
+
+OIIO_ADD_TEST(Lut1DOp, Inverse)
+{
+ // Make a lut that squares the input
+ OCIO::Lut1DRcPtr lut_a = OCIO::Lut1D::Create();
+ {
+ lut_a->from_min[0] = 0.0f;
+ lut_a->from_min[1] = 0.0f;
+ lut_a->from_min[2] = 0.0f;
+ lut_a->from_max[0] = 1.0f;
+ lut_a->from_max[1] = 1.0f;
+ lut_a->from_max[2] = 1.0f;
+ int size = 256;
+ for(int i=0; i<size; ++i)
+ {
+ float x = (float)i / (float)(size-1);
+ float x2 = x*x;
+
+ for(int c=0; c<3; ++c)
+ {
+ lut_a->luts[c].push_back(x2);
+ }
+ }
+ lut_a->maxerror = 1e-5f;
+ lut_a->errortype = OCIO::ERROR_RELATIVE;
+ }
+
+ // Make another lut
+ OCIO::Lut1DRcPtr lut_b = OCIO::Lut1D::Create();
+ {
+ lut_b->from_min[0] = 0.5f;
+ lut_b->from_min[1] = 0.6f;
+ lut_b->from_min[2] = 0.7f;
+ lut_b->from_max[0] = 1.0f;
+ lut_b->from_max[1] = 1.0f;
+ lut_b->from_max[2] = 1.0f;
+ int size = 256;
+ for(int i=0; i<size; ++i)
+ {
+ float x = (float)i / (float)(size-1);
+ float x2 = x*x;
+
+ for(int c=0; c<3; ++c)
+ {
+ lut_b->luts[c].push_back(x2);
+ }
+ }
+ lut_b->maxerror = 1e-5f;
+ lut_b->errortype = OCIO::ERROR_RELATIVE;
+ }
+
+ OCIO::OpRcPtrVec ops;
+ CreateLut1DOp(ops, lut_a, OCIO::INTERP_NEAREST, OCIO::TRANSFORM_DIR_FORWARD);
+ CreateLut1DOp(ops, lut_a, OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_INVERSE);
+ CreateLut1DOp(ops, lut_b, OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_FORWARD);
+ CreateLut1DOp(ops, lut_b, OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_INVERSE);
+
+ OIIO_CHECK_EQUAL(ops.size(), 4);
+
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[1]));
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[2]));
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[3]->clone()));
+
+ OIIO_CHECK_EQUAL( ops[0]->isInverse(ops[1]), true);
+ OIIO_CHECK_EQUAL( ops[0]->isInverse(ops[2]), false);
+ OIIO_CHECK_EQUAL( ops[0]->isInverse(ops[2]), false);
+ OIIO_CHECK_EQUAL( ops[0]->isInverse(ops[3]), false);
+ OIIO_CHECK_EQUAL( ops[2]->isInverse(ops[3]), true);
+}
+
+
+#ifdef USE_SSE
+OIIO_ADD_TEST(Lut1DOp, SSE)
+{
+ // Make a lut that squares the input
+ OCIO::Lut1DRcPtr lut = OCIO::Lut1D::Create();
+ lut->from_min[0] = 0.0f;
+ lut->from_min[1] = 0.0f;
+ lut->from_min[2] = 0.0f;
+
+ lut->from_max[0] = 1.0f;
+ lut->from_max[1] = 1.0f;
+ lut->from_max[2] = 1.0f;
+
+ int size = 256;
+ for(int i=0; i<size; ++i)
+ {
+ float x = (float)i / (float)(size-1);
+ float x2 = x*x;
+
+ for(int c=0; c<3; ++c)
+ {
+ lut->luts[c].push_back(x2);
+ }
+ }
+
+ lut->maxerror = 1e-5f;
+ lut->errortype = OCIO::ERROR_RELATIVE;
+ OIIO_CHECK_EQUAL(lut->isNoOp(), false);
+
+ int NUM_TEST_PIXELS = 1024;
+ std::vector<float> testValues(NUM_TEST_PIXELS*4);
+ std::vector<float> outputBuffer_cpu(NUM_TEST_PIXELS*4);
+ std::vector<float> outputBuffer_sse(NUM_TEST_PIXELS*4);
+
+ float val = -1.0f;
+ float delta = 0.00123456789f;
+
+ for(int i=0; i<NUM_TEST_PIXELS*4; ++i)
+ {
+ testValues[i] = val;
+ val += delta;
+ }
+
+ memcpy(&outputBuffer_cpu[0], &testValues[0], testValues.size()*sizeof(float));
+ memcpy(&outputBuffer_sse[0], &testValues[0], testValues.size()*sizeof(float));
+
+ OCIO::Lut1D_Nearest(&outputBuffer_cpu[0], NUM_TEST_PIXELS, *lut);
+ OCIO::Lut1D_Nearest_SSE(&outputBuffer_sse[0], NUM_TEST_PIXELS, *lut);
+
+ for(int i=0; i<NUM_TEST_PIXELS*4; ++i)
+ {
+ OIIO_CHECK_CLOSE(outputBuffer_cpu[i], outputBuffer_sse[i], 1e-7f);
+ //OIIO_CHECK_EQUAL(outputBuffer_cpu[i], outputBuffer_sse[i]);
+ }
+
+
+ // Test special values
+ /*
+ NUM_TEST_PIXELS = 2;
+ testValues.resize(NUM_TEST_PIXELS*4);
+ outputBuffer_cpu.resize(NUM_TEST_PIXELS*4);
+ outputBuffer_sse.resize(NUM_TEST_PIXELS*4);
+
+ testValues[0] = std::numeric_limits<float>::signaling_NaN();
+ testValues[1] = std::numeric_limits<float>::quiet_NaN();
+ testValues[2] = -std::numeric_limits<float>::signaling_NaN();
+ testValues[3] = -std::numeric_limits<float>::signaling_NaN();
+
+ testValues[4] = std::numeric_limits<float>::infinity();
+ testValues[5] = -std::numeric_limits<float>::infinity();
+ testValues[6] = 0.0f;
+
+
+ memcpy(&outputBuffer_cpu[0], &testValues[0], testValues.size()*sizeof(float));
+ memcpy(&outputBuffer_sse[0], &testValues[0], testValues.size()*sizeof(float));
+
+ OCIO::Lut1D_Nearest(&outputBuffer_cpu[0], NUM_TEST_PIXELS, lut);
+ OCIO::Lut1D_Nearest_SSE(&outputBuffer_sse[0], NUM_TEST_PIXELS, lut);
+
+ for(int i=0; i<NUM_TEST_PIXELS*4; ++i)
+ {
+ //OIIO_CHECK_CLOSE(outputBuffer_cpu[i], outputBuffer_sse[i], 1e-7f);
+ OIIO_CHECK_EQUAL(outputBuffer_cpu[i], outputBuffer_sse[i]);
+ }
+
+ */
+}
+#endif
+
+
+OIIO_ADD_TEST(Lut1DOp, NanInf)
+{
+ // Make a lut that squares the input
+ OCIO::Lut1DRcPtr lut = OCIO::Lut1D::Create();
+ lut->from_min[0] = 0.0f;
+ lut->from_min[1] = 0.0f;
+ lut->from_min[2] = 0.0f;
+
+ lut->from_max[0] = 1.0f;
+ lut->from_max[1] = 1.0f;
+ lut->from_max[2] = 1.0f;
+
+ int size = 256;
+ for(int i=0; i<size; ++i)
+ {
+ float x = (float)i / (float)(size-1);
+ float x2 = x*x;
+
+ for(int c=0; c<3; ++c)
+ {
+ lut->luts[c].push_back(x2);
+ }
+ }
+
+ lut->maxerror = 1e-5f;
+ lut->errortype = OCIO::ERROR_RELATIVE;
+ OIIO_CHECK_EQUAL(lut->isNoOp(), false);
+
+ const float reference[4] = { std::numeric_limits<float>::signaling_NaN(),
+ std::numeric_limits<float>::quiet_NaN(),
+ std::numeric_limits<float>::infinity(),
+ -std::numeric_limits<float>::infinity() };
+ /*
+ float output[4] = { std::numeric_limits<float>::signaling_NaN(),
+ std::numeric_limits<float>::quiet_NaN(),
+ 1.0f,
+ -std::numeric_limits<float>::infinity() };
+ */
+ float color[4];
+
+ memcpy(color, reference, 4*sizeof(float));
+ OCIO::Lut1D_Linear(color, 1, *lut);
+ /*
+ for(int i=0; i<4; ++i)
+ {
+ if(isnan(color[i]))
+ {
+ std::cerr << color[i] << " " << output[i] << std::endl;
+ OIIO_CHECK_EQUAL(isnan(color[i]), isnan(output[i]));
+ }
+ else
+ {
+ OIIO_CHECK_EQUAL(color[i], output[i]);
+ }
+ }
+ */
+ memcpy(color, reference, 4*sizeof(float));
+ OCIO::Lut1D_Nearest(color, 1, *lut);
+ /*
+ for(int i=0; i <4; ++i)
+ {
+ if(isnan(color[i]))
+ {
+ OIIO_CHECK_EQUAL(isnan(color[i]), isnan(output[i]));
+ }
+ else
+ {
+ OIIO_CHECK_EQUAL(color[i], output[i]);
+ }
+ }
+ */
+ memcpy(color, reference, 4*sizeof(float));
+ OCIO::Lut1D_LinearInverse(color, 1, *lut);
+ /*
+ for(int i=0; i <4; ++i)
+ {
+ if(isnan(color[i]))
+ {
+ OIIO_CHECK_EQUAL(isnan(color[i]), isnan(output[i]));
+ }
+ else
+ {
+ OIIO_CHECK_EQUAL(color[i], output[i]);
+ }
+ }
+ */
+ memcpy(color, reference, 4*sizeof(float));
+ OCIO::Lut1D_NearestInverse(color, 1, *lut);
+ /*
+ for(int i=0; i <4; ++i)
+ {
+ if(isnan(color[i]))
+ {
+ OIIO_CHECK_EQUAL(isnan(color[i]), isnan(output[i]));
+ }
+ else
+ {
+ OIIO_CHECK_EQUAL(color[i], output[i]);
+ }
+ }
+ */
+}
+
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/Lut1DOp.h b/src/core/Lut1DOp.h
new file mode 100644
index 0000000..d4d96c1
--- /dev/null
+++ b/src/core/Lut1DOp.h
@@ -0,0 +1,107 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_LUT1DOP_H
+#define INCLUDED_OCIO_LUT1DOP_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Mutex.h"
+#include "Op.h"
+
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+ // TODO: turn into a class instead of a struct?
+
+ enum ErrorType
+ {
+ ERROR_ABSOLUTE = 1,
+ ERROR_RELATIVE
+ };
+
+
+ struct Lut1D;
+ typedef OCIO_SHARED_PTR<Lut1D> Lut1DRcPtr;
+
+ struct Lut1D
+ {
+ static Lut1DRcPtr Create();
+
+ // This will compute the cacheid, and also
+ // determine if the lut is a no-op.
+ // If this lut is being read in from float ASCII text
+ // a value of 1e-5 is preferable.
+ // If this lut is being read in from integer ASCII
+ // representation, the value will depend on the LSB
+ // at the specified integer precision.
+ // Example: reading 10-bit ints? Use 2/1023.0
+ // If you dont want to do the noop computation,
+ // specify a 0.0 tolerance.
+ //
+ // TODO: Instead of having each user compute the error
+ // individually, maybe they should specify the original file bitdepth?
+ // (with appropriate precision tokens?)
+ float maxerror;
+ ErrorType errortype;
+
+ float from_min[3];
+ float from_max[3];
+
+ typedef std::vector<float> fv_t;
+ fv_t luts[3];
+
+ std::string getCacheID() const;
+ bool isNoOp() const;
+
+ void unfinalize();
+ private:
+ Lut1D();
+
+ mutable std::string m_cacheID;
+ mutable bool m_isNoOp;
+ mutable Mutex m_mutex;
+
+ void finalize() const;
+ };
+
+ typedef OCIO_SHARED_PTR<Lut1D> Lut1DRcPtr;
+
+ // This generates an identity 1d lut, from 0.0 to 1.0
+ void GenerateIdentityLut1D(float* img, int numElements, int numChannels);
+
+ void CreateLut1DOp(OpRcPtrVec & ops,
+ const Lut1DRcPtr & lut,
+ Interpolation interpolation,
+ TransformDirection direction);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/Lut3DOp.cpp b/src/core/Lut3DOp.cpp
new file mode 100644
index 0000000..d64d451
--- /dev/null
+++ b/src/core/Lut3DOp.cpp
@@ -0,0 +1,982 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "HashUtils.h"
+#include "Lut3DOp.h"
+#include "MathUtils.h"
+
+#include <cmath>
+#include <limits>
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+ Lut3D::Lut3D()
+ {
+ for(int i=0; i<3; ++i)
+ {
+ from_min[i] = 0.0f;
+ from_max[i] = 1.0f;
+ size[i] = 0;
+ }
+ }
+
+ Lut3DRcPtr Lut3D::Create()
+ {
+ return Lut3DRcPtr(new Lut3D());
+ }
+
+ std::string Lut3D::getCacheID() const
+ {
+ AutoMutex lock(m_cacheidMutex);
+
+ if(lut.empty())
+ throw Exception("Cannot compute cacheID of invalid Lut3D");
+
+ if(!m_cacheID.empty())
+ return m_cacheID;
+
+ md5_state_t state;
+ md5_byte_t digest[16];
+
+ md5_init(&state);
+
+ md5_append(&state, (const md5_byte_t *)from_min, 3*sizeof(float));
+ md5_append(&state, (const md5_byte_t *)from_max, 3*sizeof(float));
+ md5_append(&state, (const md5_byte_t *)size, 3*sizeof(int));
+ md5_append(&state, (const md5_byte_t *)&lut[0], (int) (lut.size()*sizeof(float)));
+
+ md5_finish(&state, digest);
+
+ m_cacheID = GetPrintableHash(digest);
+
+ return m_cacheID;
+ }
+
+
+ namespace
+ {
+ // Linear
+ inline float lerp(float a, float b, float z)
+ { return (b - a) * z + a; }
+
+ inline void lerp_rgb(float* out, float* a, float* b, float* z)
+ {
+ out[0] = (b[0] - a[0]) * z[0] + a[0];
+ out[1] = (b[1] - a[1]) * z[1] + a[1];
+ out[2] = (b[2] - a[2]) * z[2] + a[2];
+ }
+
+ // Bilinear
+ inline float lerp(float a, float b, float c, float d, float y, float z)
+ { return lerp(lerp(a, b, z), lerp(c, d, z), y); }
+
+ inline void lerp_rgb(float* out, float* a, float* b, float* c,
+ float* d, float* y, float* z)
+ {
+ float v1[3];
+ float v2[3];
+
+ lerp_rgb(v1, a, b, z);
+ lerp_rgb(v2, c, d, z);
+ lerp_rgb(out, v1, v2, y);
+ }
+
+ // Trilinear
+ inline float lerp(float a, float b, float c, float d,
+ float e, float f, float g, float h,
+ float x, float y, float z)
+ { return lerp(lerp(a,b,c,d,y,z), lerp(e,f,g,h,y,z), x); }
+
+ inline void lerp_rgb(float* out, float* a, float* b, float* c, float* d,
+ float* e, float* f, float* g, float* h,
+ float* x, float* y, float* z)
+ {
+ float v1[3];
+ float v2[3];
+
+ lerp_rgb(v1, a,b,c,d,y,z);
+ lerp_rgb(v2, e,f,g,h,y,z);
+ lerp_rgb(out, v1, v2, x);
+ }
+
+ inline float lookupNearest_3D(int rIndex, int gIndex, int bIndex,
+ int size_red, int size_green, int size_blue,
+ const float* simple_rgb_lut, int channelIndex)
+ {
+ return simple_rgb_lut[ GetLut3DIndex_B(rIndex, gIndex, bIndex,
+ size_red, size_green, size_blue) + channelIndex];
+ }
+
+ inline void lookupNearest_3D_rgb(float* rgb,
+ int rIndex, int gIndex, int bIndex,
+ int size_red, int size_green, int size_blue,
+ const float* simple_rgb_lut)
+ {
+ int offset = GetLut3DIndex_B(rIndex, gIndex, bIndex, size_red, size_green, size_blue);
+ rgb[0] = simple_rgb_lut[offset];
+ rgb[1] = simple_rgb_lut[offset+1];
+ rgb[2] = simple_rgb_lut[offset+2];
+ }
+
+ // Note: This function assumes that minVal is less than maxVal
+ inline int clamp(float k, float minVal, float maxVal)
+ {
+ return static_cast<int>(roundf(std::max(std::min(k, maxVal), minVal)));
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+ // Nearest Forward
+
+ void Lut3D_Nearest(float* rgbaBuffer, long numPixels, const Lut3D & lut)
+ {
+ float maxIndex[3];
+ float mInv[3];
+ float b[3];
+ float mInv_x_maxIndex[3];
+ int lutSize[3];
+ const float* startPos = &(lut.lut[0]);
+
+ for(int i=0; i<3; ++i)
+ {
+ maxIndex[i] = (float) (lut.size[i] - 1);
+ mInv[i] = 1.0f / (lut.from_max[i] - lut.from_min[i]);
+ b[i] = lut.from_min[i];
+ mInv_x_maxIndex[i] = (float) (mInv[i] * maxIndex[i]);
+
+ lutSize[i] = lut.size[i];
+ }
+
+ int localIndex[3];
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ if(isnan(rgbaBuffer[0]) || isnan(rgbaBuffer[1]) || isnan(rgbaBuffer[2]))
+ {
+ rgbaBuffer[0] = std::numeric_limits<float>::quiet_NaN();
+ rgbaBuffer[1] = std::numeric_limits<float>::quiet_NaN();
+ rgbaBuffer[2] = std::numeric_limits<float>::quiet_NaN();
+ }
+ else
+ {
+ localIndex[0] = clamp(mInv_x_maxIndex[0] * (rgbaBuffer[0] - b[0]), 0.0f, maxIndex[0]);
+ localIndex[1] = clamp(mInv_x_maxIndex[1] * (rgbaBuffer[1] - b[1]), 0.0f, maxIndex[1]);
+ localIndex[2] = clamp(mInv_x_maxIndex[2] * (rgbaBuffer[2] - b[2]), 0.0f, maxIndex[2]);
+
+ lookupNearest_3D_rgb(rgbaBuffer, localIndex[0], localIndex[1], localIndex[2],
+ lutSize[0], lutSize[1], lutSize[2], startPos);
+ }
+
+ rgbaBuffer += 4;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+ // Linear Forward
+
+ void Lut3D_Linear(float* rgbaBuffer, long numPixels, const Lut3D & lut)
+ {
+ float maxIndex[3];
+ float mInv[3];
+ float b[3];
+ float mInv_x_maxIndex[3];
+ int lutSize[3];
+ const float* startPos = &(lut.lut[0]);
+
+ for(int i=0; i<3; ++i)
+ {
+ maxIndex[i] = (float) (lut.size[i] - 1);
+ mInv[i] = 1.0f / (lut.from_max[i] - lut.from_min[i]);
+ b[i] = lut.from_min[i];
+ mInv_x_maxIndex[i] = (float) (mInv[i] * maxIndex[i]);
+
+ lutSize[i] = lut.size[i];
+ }
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+
+ if(isnan(rgbaBuffer[0]) || isnan(rgbaBuffer[1]) || isnan(rgbaBuffer[2]))
+ {
+ rgbaBuffer[0] = std::numeric_limits<float>::quiet_NaN();
+ rgbaBuffer[1] = std::numeric_limits<float>::quiet_NaN();
+ rgbaBuffer[2] = std::numeric_limits<float>::quiet_NaN();
+ }
+ else
+ {
+ float localIndex[3];
+ int indexLow[3];
+ int indexHigh[3];
+ float delta[3];
+ float a[3];
+ float b_[3];
+ float c[3];
+ float d[3];
+ float e[3];
+ float f[3];
+ float g[3];
+ float h[4];
+ float x[4];
+ float y[4];
+ float z[4];
+
+ localIndex[0] = std::max(std::min(mInv_x_maxIndex[0] * (rgbaBuffer[0] - b[0]), maxIndex[0]), 0.0f);
+ localIndex[1] = std::max(std::min(mInv_x_maxIndex[1] * (rgbaBuffer[1] - b[1]), maxIndex[1]), 0.0f);
+ localIndex[2] = std::max(std::min(mInv_x_maxIndex[2] * (rgbaBuffer[2] - b[2]), maxIndex[2]), 0.0f);
+
+ indexLow[0] = static_cast<int>(std::floor(localIndex[0]));
+ indexLow[1] = static_cast<int>(std::floor(localIndex[1]));
+ indexLow[2] = static_cast<int>(std::floor(localIndex[2]));
+
+ indexHigh[0] = static_cast<int>(std::ceil(localIndex[0]));
+ indexHigh[1] = static_cast<int>(std::ceil(localIndex[1]));
+ indexHigh[2] = static_cast<int>(std::ceil(localIndex[2]));
+
+ delta[0] = localIndex[0] - static_cast<float>(indexLow[0]);
+ delta[1] = localIndex[1] - static_cast<float>(indexLow[1]);
+ delta[2] = localIndex[2] - static_cast<float>(indexLow[2]);
+
+ // Lookup 8 corners of cube
+ lookupNearest_3D_rgb(a, indexLow[0], indexLow[1], indexLow[2], lutSize[0], lutSize[1], lutSize[2], startPos);
+ lookupNearest_3D_rgb(b_, indexLow[0], indexLow[1], indexHigh[2], lutSize[0], lutSize[1], lutSize[2], startPos);
+ lookupNearest_3D_rgb(c, indexLow[0], indexHigh[1], indexLow[2], lutSize[0], lutSize[1], lutSize[2], startPos);
+ lookupNearest_3D_rgb(d, indexLow[0], indexHigh[1], indexHigh[2], lutSize[0], lutSize[1], lutSize[2], startPos);
+ lookupNearest_3D_rgb(e, indexHigh[0], indexLow[1], indexLow[2], lutSize[0], lutSize[1], lutSize[2], startPos);
+ lookupNearest_3D_rgb(f, indexHigh[0], indexLow[1], indexHigh[2], lutSize[0], lutSize[1], lutSize[2], startPos);
+ lookupNearest_3D_rgb(g, indexHigh[0], indexHigh[1], indexLow[2], lutSize[0], lutSize[1], lutSize[2], startPos);
+ lookupNearest_3D_rgb(h, indexHigh[0], indexHigh[1], indexHigh[2], lutSize[0], lutSize[1], lutSize[2], startPos);
+
+ // Also store the 3d interpolation coordinates
+ x[0] = delta[0]; x[1] = delta[0]; x[2] = delta[0];
+ y[0] = delta[1]; y[1] = delta[1]; y[2] = delta[1];
+ z[0] = delta[2]; z[1] = delta[2]; z[2] = delta[2];
+
+ // Do a trilinear interpolation of the 8 corners
+ // 4726.8 scanlines/sec
+
+ lerp_rgb(rgbaBuffer, a, b_, c, d, e, f, g, h,
+ x, y, z);
+ }
+
+ rgbaBuffer += 4;
+ }
+ }
+ }
+
+
+ void Lut3D_Tetrahedral(float* rgbaBuffer, long numPixels, const Lut3D & lut)
+ {
+ // Tetrahedral interoplation, as described by:
+ // http://www.filmlight.ltd.uk/pdf/whitepapers/FL-TL-TN-0057-SoftwareLib.pdf
+ // http://blogs.mathworks.com/steve/2006/11/24/tetrahedral-interpolation-for-colorspace-conversion/
+ // http://www.hpl.hp.com/techreports/98/HPL-98-95.html
+
+ float maxIndex[3];
+ float mInv[3];
+ float b[3];
+ float mInv_x_maxIndex[3];
+ int lutSize[3];
+ const float* startPos = &(lut.lut[0]);
+
+ for(int i=0; i<3; ++i)
+ {
+ maxIndex[i] = (float) (lut.size[i] - 1);
+ mInv[i] = 1.0f / (lut.from_max[i] - lut.from_min[i]);
+ b[i] = lut.from_min[i];
+ mInv_x_maxIndex[i] = (float) (mInv[i] * maxIndex[i]);
+
+ lutSize[i] = lut.size[i];
+ }
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+
+ if(isnan(rgbaBuffer[0]) || isnan(rgbaBuffer[1]) || isnan(rgbaBuffer[2]))
+ {
+ rgbaBuffer[0] = std::numeric_limits<float>::quiet_NaN();
+ rgbaBuffer[1] = std::numeric_limits<float>::quiet_NaN();
+ rgbaBuffer[2] = std::numeric_limits<float>::quiet_NaN();
+ }
+ else
+ {
+ float localIndex[3];
+ int indexLow[3];
+ int indexHigh[3];
+ float delta[3];
+
+ // Same index/delta calculation as linear interpolation
+ localIndex[0] = std::max(std::min(mInv_x_maxIndex[0] * (rgbaBuffer[0] - b[0]), maxIndex[0]), 0.0f);
+ localIndex[1] = std::max(std::min(mInv_x_maxIndex[1] * (rgbaBuffer[1] - b[1]), maxIndex[1]), 0.0f);
+ localIndex[2] = std::max(std::min(mInv_x_maxIndex[2] * (rgbaBuffer[2] - b[2]), maxIndex[2]), 0.0f);
+
+ indexLow[0] = static_cast<int>(std::floor(localIndex[0]));
+ indexLow[1] = static_cast<int>(std::floor(localIndex[1]));
+ indexLow[2] = static_cast<int>(std::floor(localIndex[2]));
+
+ indexHigh[0] = static_cast<int>(std::ceil(localIndex[0]));
+ indexHigh[1] = static_cast<int>(std::ceil(localIndex[1]));
+ indexHigh[2] = static_cast<int>(std::ceil(localIndex[2]));
+
+ delta[0] = localIndex[0] - static_cast<float>(indexLow[0]);
+ delta[1] = localIndex[1] - static_cast<float>(indexLow[1]);
+ delta[2] = localIndex[2] - static_cast<float>(indexLow[2]);
+
+ // Rebind for consistency with Truelight paper
+ float fx = delta[0];
+ float fy = delta[1];
+ float fz = delta[2];
+
+ // Compute index into LUT for surrounding corners
+ const int n000 = GetLut3DIndex_B(indexLow[0], indexLow[1], indexLow[2],
+ lutSize[0], lutSize[1], lutSize[2]);
+ const int n100 = GetLut3DIndex_B(indexHigh[0], indexLow[1], indexLow[2],
+ lutSize[0], lutSize[1], lutSize[2]);
+ const int n010 = GetLut3DIndex_B(indexLow[0], indexHigh[1], indexLow[2],
+ lutSize[0], lutSize[1], lutSize[2]);
+ const int n001 = GetLut3DIndex_B(indexLow[0], indexLow[1], indexHigh[2],
+ lutSize[0], lutSize[1], lutSize[2]);
+ const int n110 = GetLut3DIndex_B(indexHigh[0], indexHigh[1], indexLow[2],
+ lutSize[0], lutSize[1], lutSize[2]);
+ const int n101 = GetLut3DIndex_B(indexHigh[0], indexLow[1], indexHigh[2],
+ lutSize[0], lutSize[1], lutSize[2]);
+ const int n011 = GetLut3DIndex_B(indexLow[0], indexHigh[1], indexHigh[2],
+ lutSize[0], lutSize[1], lutSize[2]);
+ const int n111 = GetLut3DIndex_B(indexHigh[0], indexHigh[1], indexHigh[2],
+ lutSize[0], lutSize[1], lutSize[2]);
+
+ if (fx > fy) {
+ if (fy > fz) {
+ rgbaBuffer[0] =
+ (1-fx) * startPos[n000] +
+ (fx-fy) * startPos[n100] +
+ (fy-fz) * startPos[n110] +
+ (fz) * startPos[n111];
+
+ rgbaBuffer[1] =
+ (1-fx) * startPos[n000+1] +
+ (fx-fy) * startPos[n100+1] +
+ (fy-fz) * startPos[n110+1] +
+ (fz) * startPos[n111+1];
+
+ rgbaBuffer[2] =
+ (1-fx) * startPos[n000+2] +
+ (fx-fy) * startPos[n100+2] +
+ (fy-fz) * startPos[n110+2] +
+ (fz) * startPos[n111+2];
+ }
+ else if (fx > fz)
+ {
+ rgbaBuffer[0] =
+ (1-fx) * startPos[n000] +
+ (fx-fz) * startPos[n100] +
+ (fz-fy) * startPos[n101] +
+ (fy) * startPos[n111];
+
+ rgbaBuffer[1] =
+ (1-fx) * startPos[n000+1] +
+ (fx-fz) * startPos[n100+1] +
+ (fz-fy) * startPos[n101+1] +
+ (fy) * startPos[n111+1];
+
+ rgbaBuffer[2] =
+ (1-fx) * startPos[n000+2] +
+ (fx-fz) * startPos[n100+2] +
+ (fz-fy) * startPos[n101+2] +
+ (fy) * startPos[n111+2];
+ }
+ else
+ {
+ rgbaBuffer[0] =
+ (1-fz) * startPos[n000] +
+ (fz-fx) * startPos[n001] +
+ (fx-fy) * startPos[n101] +
+ (fy) * startPos[n111];
+
+ rgbaBuffer[1] =
+ (1-fz) * startPos[n000+1] +
+ (fz-fx) * startPos[n001+1] +
+ (fx-fy) * startPos[n101+1] +
+ (fy) * startPos[n111+1];
+
+ rgbaBuffer[2] =
+ (1-fz) * startPos[n000+2] +
+ (fz-fx) * startPos[n001+2] +
+ (fx-fy) * startPos[n101+2] +
+ (fy) * startPos[n111+2];
+ }
+ }
+ else
+ {
+ if (fz > fy)
+ {
+ rgbaBuffer[0] =
+ (1-fz) * startPos[n000] +
+ (fz-fy) * startPos[n001] +
+ (fy-fx) * startPos[n011] +
+ (fx) * startPos[n111];
+
+ rgbaBuffer[1] =
+ (1-fz) * startPos[n000+1] +
+ (fz-fy) * startPos[n001+1] +
+ (fy-fx) * startPos[n011+1] +
+ (fx) * startPos[n111+1];
+
+ rgbaBuffer[2] =
+ (1-fz) * startPos[n000+2] +
+ (fz-fy) * startPos[n001+2] +
+ (fy-fx) * startPos[n011+2] +
+ (fx) * startPos[n111+2];
+ }
+ else if (fz > fx)
+ {
+ rgbaBuffer[0] =
+ (1-fy) * startPos[n000] +
+ (fy-fz) * startPos[n010] +
+ (fz-fx) * startPos[n011] +
+ (fx) * startPos[n111];
+
+ rgbaBuffer[1] =
+ (1-fy) * startPos[n000+1] +
+ (fy-fz) * startPos[n010+1] +
+ (fz-fx) * startPos[n011+1] +
+ (fx) * startPos[n111+1];
+
+ rgbaBuffer[2] =
+ (1-fy) * startPos[n000+2] +
+ (fy-fz) * startPos[n010+2] +
+ (fz-fx) * startPos[n011+2] +
+ (fx) * startPos[n111+2];
+ }
+ else
+ {
+ rgbaBuffer[0] =
+ (1-fy) * startPos[n000] +
+ (fy-fx) * startPos[n010] +
+ (fx-fz) * startPos[n110] +
+ (fz) * startPos[n111];
+
+ rgbaBuffer[1] =
+ (1-fy) * startPos[n000+1] +
+ (fy-fx) * startPos[n010+1] +
+ (fx-fz) * startPos[n110+1] +
+ (fz) * startPos[n111+1];
+
+ rgbaBuffer[2] =
+ (1-fy) * startPos[n000+2] +
+ (fy-fx) * startPos[n010+2] +
+ (fx-fz) * startPos[n110+2] +
+ (fz) * startPos[n111+2];
+ }
+ }
+ } // !isnan
+
+ rgbaBuffer += 4;
+ }
+ }
+
+
+ void GenerateIdentityLut3D(float* img, int edgeLen, int numChannels, Lut3DOrder lut3DOrder)
+ {
+ if(!img) return;
+ if(numChannels < 3)
+ {
+ throw Exception("Cannot generate idenitity 3d lut with less than 3 channels.");
+ }
+
+ float c = 1.0f / ((float)edgeLen - 1.0f);
+
+ if(lut3DOrder == LUT3DORDER_FAST_RED)
+ {
+ for(int i=0; i<edgeLen*edgeLen*edgeLen; i++)
+ {
+ img[numChannels*i+0] = (float)(i%edgeLen) * c;
+ img[numChannels*i+1] = (float)((i/edgeLen)%edgeLen) * c;
+ img[numChannels*i+2] = (float)((i/edgeLen/edgeLen)%edgeLen) * c;
+ }
+ }
+ else if(lut3DOrder == LUT3DORDER_FAST_BLUE)
+ {
+ for(int i=0; i<edgeLen*edgeLen*edgeLen; i++)
+ {
+ img[numChannels*i+0] = (float)((i/edgeLen/edgeLen)%edgeLen) * c;
+ img[numChannels*i+1] = (float)((i/edgeLen)%edgeLen) * c;
+ img[numChannels*i+2] = (float)(i%edgeLen) * c;
+ }
+ }
+ else
+ {
+ throw Exception("Unknown Lut3DOrder.");
+ }
+ }
+
+
+ int Get3DLutEdgeLenFromNumPixels(int numPixels)
+ {
+ int dim = static_cast<int>(roundf(powf((float) numPixels, 1.0f/3.0f)));
+
+ if(dim*dim*dim != numPixels)
+ {
+ std::ostringstream os;
+ os << "Cannot infer 3D Lut size. ";
+ os << numPixels << " element(s) does not correspond to a ";
+ os << "unform cube edge length. (nearest edge length is ";
+ os << dim << ").";
+ throw Exception(os.str().c_str());
+ }
+
+ return dim;
+ }
+
+ namespace
+ {
+ class Lut3DOp : public Op
+ {
+ public:
+ Lut3DOp(Lut3DRcPtr lut,
+ Interpolation interpolation,
+ TransformDirection direction);
+ virtual ~Lut3DOp();
+
+ virtual OpRcPtr clone() const;
+
+ virtual std::string getInfo() const;
+ virtual std::string getCacheID() const;
+
+ virtual bool isNoOp() const;
+ virtual bool isSameType(const OpRcPtr & op) const;
+ virtual bool isInverse(const OpRcPtr & op) const;
+ virtual bool hasChannelCrosstalk() const;
+ virtual void finalize();
+ virtual void apply(float* rgbaBuffer, long numPixels) const;
+
+ virtual bool supportsGpuShader() const;
+ virtual void writeGpuShader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc) const;
+
+ private:
+ Lut3DRcPtr m_lut;
+ Interpolation m_interpolation;
+ TransformDirection m_direction;
+
+ // Set in finalize
+ std::string m_cacheID;
+ };
+
+ typedef OCIO_SHARED_PTR<Lut3DOp> Lut3DOpRcPtr;
+
+
+ Lut3DOp::Lut3DOp(Lut3DRcPtr lut,
+ Interpolation interpolation,
+ TransformDirection direction):
+ Op(),
+ m_lut(lut),
+ m_interpolation(interpolation),
+ m_direction(direction)
+ {
+ }
+
+ OpRcPtr Lut3DOp::clone() const
+ {
+ OpRcPtr op = OpRcPtr(new Lut3DOp(m_lut, m_interpolation, m_direction));
+ return op;
+ }
+
+ Lut3DOp::~Lut3DOp()
+ { }
+
+ std::string Lut3DOp::getInfo() const
+ {
+ return "<Lut3DOp>";
+ }
+
+ std::string Lut3DOp::getCacheID() const
+ {
+ return m_cacheID;
+ }
+
+ // TODO: compute real value for isNoOp
+ bool Lut3DOp::isNoOp() const
+ {
+ return false;
+ }
+
+ bool Lut3DOp::isSameType(const OpRcPtr & op) const
+ {
+ Lut3DOpRcPtr typedRcPtr = DynamicPtrCast<Lut3DOp>(op);
+ if(!typedRcPtr) return false;
+ return true;
+ }
+
+ bool Lut3DOp::isInverse(const OpRcPtr & op) const
+ {
+ Lut3DOpRcPtr typedRcPtr = DynamicPtrCast<Lut3DOp>(op);
+ if(!typedRcPtr) return false;
+
+ if(GetInverseTransformDirection(m_direction) != typedRcPtr->m_direction)
+ return false;
+
+ return (m_lut->getCacheID() == typedRcPtr->m_lut->getCacheID());
+ }
+
+ // TODO: compute real value for hasChannelCrosstalk
+ bool Lut3DOp::hasChannelCrosstalk() const
+ {
+ return true;
+ }
+
+ void Lut3DOp::finalize()
+ {
+ if(m_direction != TRANSFORM_DIR_FORWARD)
+ {
+ std::ostringstream os;
+ os << "3D Luts can only be applied in the forward direction. ";
+ os << "(" << TransformDirectionToString(m_direction) << ")";
+ os << " specified.";
+ throw Exception(os.str().c_str());
+ }
+
+ // Validate the requested interpolation type
+ switch(m_interpolation)
+ {
+ // These are the allowed values.
+ case INTERP_NEAREST:
+ case INTERP_LINEAR:
+ case INTERP_TETRAHEDRAL:
+ break;
+ case INTERP_BEST:
+ m_interpolation = INTERP_LINEAR;
+ break;
+ case INTERP_UNKNOWN:
+ throw Exception("Cannot apply Lut3DOp, unspecified interpolation.");
+ break;
+ default:
+ throw Exception("Cannot apply Lut3DOp, invalid interpolation specified.");
+ }
+
+ for(int i=0; i<3; ++i)
+ {
+ if(m_lut->size[i] == 0)
+ {
+ throw Exception("Cannot apply Lut3DOp, lut object is empty.");
+ }
+ // TODO if from_min[i] == from_max[i]
+ }
+
+ if(m_lut->size[0]*m_lut->size[1]*m_lut->size[2] * 3 != (int)m_lut->lut.size())
+ {
+ throw Exception("Cannot apply Lut3DOp, specified size does not match data.");
+ }
+
+ // Create the cacheID
+ std::ostringstream cacheIDStream;
+ cacheIDStream << "<Lut3DOp ";
+ cacheIDStream << m_lut->getCacheID() << " ";
+ cacheIDStream << InterpolationToString(m_interpolation) << " ";
+ cacheIDStream << TransformDirectionToString(m_direction) << " ";
+ cacheIDStream << ">";
+ m_cacheID = cacheIDStream.str();
+ }
+
+ void Lut3DOp::apply(float* rgbaBuffer, long numPixels) const
+ {
+ if(m_interpolation == INTERP_NEAREST)
+ {
+ Lut3D_Nearest(rgbaBuffer, numPixels, *m_lut);
+ }
+ else if(m_interpolation == INTERP_LINEAR)
+ {
+ Lut3D_Linear(rgbaBuffer, numPixels, *m_lut);
+ }
+ else if(m_interpolation == INTERP_TETRAHEDRAL)
+ {
+ Lut3D_Tetrahedral(rgbaBuffer, numPixels, *m_lut);
+ }
+ }
+
+ bool Lut3DOp::supportsGpuShader() const
+ {
+ return false;
+ }
+
+ void Lut3DOp::writeGpuShader(std::ostream & /*shader*/,
+ const std::string & /*pixelName*/,
+ const GpuShaderDesc & /*shaderDesc*/) const
+ {
+ throw Exception("Lut3DOp does not support analytical shader generation.");
+ }
+ }
+
+ void CreateLut3DOp(OpRcPtrVec & ops,
+ Lut3DRcPtr lut,
+ Interpolation interpolation,
+ TransformDirection direction)
+ {
+ ops.push_back( Lut3DOpRcPtr(new Lut3DOp(lut, interpolation, direction)) );
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+#include <cstring>
+#include <cstdlib>
+#include <sys/time.h>
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+OIIO_ADD_TEST(Lut3DOp, NanInfValueCheck)
+{
+ OCIO::Lut3DRcPtr lut = OCIO::Lut3D::Create();
+
+ lut->from_min[0] = 0.0f;
+ lut->from_min[1] = 0.0f;
+ lut->from_min[2] = 0.0f;
+
+ lut->from_max[0] = 1.0f;
+ lut->from_max[1] = 1.0f;
+ lut->from_max[2] = 1.0f;
+
+ lut->size[0] = 3;
+ lut->size[1] = 3;
+ lut->size[2] = 3;
+
+ lut->lut.resize(lut->size[0]*lut->size[1]*lut->size[2]*3);
+
+ GenerateIdentityLut3D(&lut->lut[0], lut->size[0], 3, OCIO::LUT3DORDER_FAST_RED);
+ for(unsigned int i=0; i<lut->lut.size(); ++i)
+ {
+ lut->lut[i] = powf(lut->lut[i], 2.0f);
+ }
+
+ const float reference[4] = { std::numeric_limits<float>::signaling_NaN(),
+ std::numeric_limits<float>::quiet_NaN(),
+ std::numeric_limits<float>::infinity(),
+ -std::numeric_limits<float>::infinity() };
+ float color[4];
+
+ memcpy(color, reference, 4*sizeof(float));
+ OCIO::Lut3D_Nearest(color, 1, *lut);
+
+ memcpy(color, reference, 4*sizeof(float));
+ OCIO::Lut3D_Linear(color, 1, *lut);
+}
+
+
+OIIO_ADD_TEST(Lut3DOp, ValueCheck)
+{
+ OCIO::Lut3DRcPtr lut = OCIO::Lut3D::Create();
+
+ lut->from_min[0] = 0.0f;
+ lut->from_min[1] = 0.0f;
+ lut->from_min[2] = 0.0f;
+
+ lut->from_max[0] = 1.0f;
+ lut->from_max[1] = 1.0f;
+ lut->from_max[2] = 1.0f;
+
+ lut->size[0] = 32;
+ lut->size[1] = 32;
+ lut->size[2] = 32;
+
+ lut->lut.resize(lut->size[0]*lut->size[1]*lut->size[2]*3);
+ GenerateIdentityLut3D(&lut->lut[0], lut->size[0], 3, OCIO::LUT3DORDER_FAST_RED);
+ for(unsigned int i=0; i<lut->lut.size(); ++i)
+ {
+ lut->lut[i] = powf(lut->lut[i], 2.0f);
+ }
+
+ const float reference[] = { 0.0f, 0.2f, 0.3f, 1.0f,
+ 0.1234f, 0.4567f, 0.9876f, 1.0f,
+ 11.0f, -0.5f, 0.5010f, 1.0f
+ };
+ const float nearest[] = { 0.0f, 0.03746097535f, 0.0842871964f, 1.0f,
+ 0.01664932258f, 0.2039542049f, 1.0f, 1.0f,
+ 1.0f, 0.0f, 0.2663891613f, 1.0f
+ };
+ const float linear[] = { 0.0f, 0.04016649351f, 0.09021852165f, 1.0f,
+ 0.01537752338f, 0.2087130845f, 0.9756000042f, 1.0f,
+ 1.0f, 0.0f, 0.2512601018f, 1.0f
+ };
+ float color[12];
+
+ // Check nearest
+ memcpy(color, reference, 12*sizeof(float));
+ OCIO::Lut3D_Nearest(color, 3, *lut);
+ for(unsigned int i=0; i<12; ++i)
+ {
+ OIIO_CHECK_CLOSE(color[i], nearest[i], 1e-8);
+ }
+
+ // Check linear
+ memcpy(color, reference, 12*sizeof(float));
+ OCIO::Lut3D_Linear(color, 3, *lut);
+ for(unsigned int i=0; i<12; ++i)
+ {
+ OIIO_CHECK_CLOSE(color[i], linear[i], 1e-8);
+ }
+
+ // Check tetrahedral
+ memcpy(color, reference, 12*sizeof(float));
+ OCIO::Lut3D_Tetrahedral(color, 3, *lut);
+ for(unsigned int i=0; i<12; ++i)
+ {
+ OIIO_CHECK_CLOSE(color[i], linear[i], 1e-7); // Note, max delta lowered from 1e-8
+ }
+}
+
+
+
+OIIO_ADD_TEST(Lut3DOp, InverseComparisonCheck)
+{
+ OCIO::Lut3DRcPtr lut_a = OCIO::Lut3D::Create();
+ lut_a->from_min[0] = 0.0f;
+ lut_a->from_min[1] = 0.0f;
+ lut_a->from_min[2] = 0.0f;
+ lut_a->from_max[0] = 1.0f;
+ lut_a->from_max[1] = 1.0f;
+ lut_a->from_max[2] = 1.0f;
+ lut_a->size[0] = 32;
+ lut_a->size[1] = 32;
+ lut_a->size[2] = 32;
+ lut_a->lut.resize(lut_a->size[0]*lut_a->size[1]*lut_a->size[2]*3);
+ GenerateIdentityLut3D(&lut_a->lut[0], lut_a->size[0], 3, OCIO::LUT3DORDER_FAST_RED);
+
+ OCIO::Lut3DRcPtr lut_b = OCIO::Lut3D::Create();
+ lut_b->from_min[0] = 0.5f;
+ lut_b->from_min[1] = 0.5f;
+ lut_b->from_min[2] = 0.5f;
+ lut_b->from_max[0] = 1.0f;
+ lut_b->from_max[1] = 1.0f;
+ lut_b->from_max[2] = 1.0f;
+ lut_b->size[0] = 32;
+ lut_b->size[1] = 32;
+ lut_b->size[2] = 32;
+ lut_b->lut.resize(lut_b->size[0]*lut_b->size[1]*lut_b->size[2]*3);
+ GenerateIdentityLut3D(&lut_b->lut[0], lut_b->size[0], 3, OCIO::LUT3DORDER_FAST_RED);
+
+ OCIO::OpRcPtrVec ops;
+ CreateLut3DOp(ops, lut_a, OCIO::INTERP_NEAREST, OCIO::TRANSFORM_DIR_FORWARD);
+ CreateLut3DOp(ops, lut_a, OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_INVERSE);
+ CreateLut3DOp(ops, lut_b, OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_FORWARD);
+ CreateLut3DOp(ops, lut_b, OCIO::INTERP_LINEAR, OCIO::TRANSFORM_DIR_INVERSE);
+
+ OIIO_CHECK_EQUAL(ops.size(), 4);
+
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[1]));
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[2]));
+ OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[3]->clone()));
+
+ OIIO_CHECK_EQUAL( ops[0]->isInverse(ops[1]), true);
+ OIIO_CHECK_EQUAL( ops[0]->isInverse(ops[2]), false);
+ OIIO_CHECK_EQUAL( ops[0]->isInverse(ops[2]), false);
+ OIIO_CHECK_EQUAL( ops[0]->isInverse(ops[3]), false);
+ OIIO_CHECK_EQUAL( ops[2]->isInverse(ops[3]), true);
+}
+
+
+OIIO_ADD_TEST(Lut3DOp, PerformanceCheck)
+{
+ /*
+ OCIO::Lut3D lut;
+
+ lut.from_min[0] = 0.0f;
+ lut.from_min[1] = 0.0f;
+ lut.from_min[2] = 0.0f;
+
+ lut.from_max[0] = 1.0f;
+ lut.from_max[1] = 1.0f;
+ lut.from_max[2] = 1.0f;
+
+ lut.size[0] = 32;
+ lut.size[1] = 32;
+ lut.size[2] = 32;
+
+ lut.lut.resize(lut.size[0]*lut.size[1]*lut.size[2]*3);
+ GenerateIdentityLut3D(&lut.lut[0], lut.size[0], 3, OCIO::LUT3DORDER_FAST_RED);
+
+ std::vector<float> img;
+ int xres = 2048;
+ int yres = 1;
+ int channels = 4;
+ img.resize(xres*yres*channels);
+
+ srand48(0);
+
+ // create random values from -0.05 to 1.05
+ // (To simulate clipping performance)
+
+ for(unsigned int i=0; i<img.size(); ++i)
+ {
+ float uniform = (float)drand48();
+ img[i] = uniform*1.1f - 0.05f;
+ }
+
+ timeval t;
+ gettimeofday(&t, 0);
+ double starttime = (double) t.tv_sec + (double) t.tv_usec / 1000000.0;
+
+ int numloops = 1024;
+ for(int i=0; i<numloops; ++i)
+ {
+ //OCIO::Lut3D_Nearest(&img[0], xres*yres, lut);
+ OCIO::Lut3D_Linear(&img[0], xres*yres, lut);
+ }
+
+ gettimeofday(&t, 0);
+ double endtime = (double) t.tv_sec + (double) t.tv_usec / 1000000.0;
+ double totaltime_a = (endtime-starttime)/numloops;
+
+ printf("Linear: %0.1f ms - %0.1f fps\n", totaltime_a*1000.0, 1.0/totaltime_a);
+
+
+ // Tetrahedral
+ gettimeofday(&t, 0);
+ starttime = (double) t.tv_sec + (double) t.tv_usec / 1000000.0;
+
+ for(int i=0; i<numloops; ++i)
+ {
+ OCIO::Lut3D_Tetrahedral(&img[0], xres*yres, lut);
+ }
+
+ gettimeofday(&t, 0);
+ endtime = (double) t.tv_sec + (double) t.tv_usec / 1000000.0;
+ double totaltime_b = (endtime-starttime)/numloops;
+
+ printf("Tetra: %0.1f ms - %0.1f fps\n", totaltime_b*1000.0, 1.0/totaltime_b);
+
+ double speed_diff = totaltime_a/totaltime_b;
+ printf("Tetra is %.04f speed of Linear\n", speed_diff);
+ */
+}
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/Lut3DOp.h b/src/core/Lut3DOp.h
new file mode 100644
index 0000000..bba532b
--- /dev/null
+++ b/src/core/Lut3DOp.h
@@ -0,0 +1,114 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_LUT3DOP_H
+#define INCLUDED_OCIO_LUT3DOP_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Mutex.h"
+#include "Op.h"
+
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+ // TODO: turn into a class instead of a struct?
+
+ struct Lut3D;
+ typedef OCIO_SHARED_PTR<Lut3D> Lut3DRcPtr;
+
+ struct Lut3D
+ {
+ static Lut3DRcPtr Create();
+
+ float from_min[3];
+ float from_max[3];
+ int size[3];
+
+ typedef std::vector<float> fv_t;
+ fv_t lut;
+
+ std::string getCacheID() const;
+
+ private:
+ Lut3D();
+ mutable std::string m_cacheID;
+ mutable Mutex m_cacheidMutex;
+ };
+
+ // RGB channel ordering.
+ // Pixels ordered in such a way that the blue coordinate changes fastest,
+ // then the green coordinate, and finally, the red coordinate changes slowest
+
+ inline int GetLut3DIndex_B(int indexR, int indexG, int indexB,
+ int sizeR, int sizeG, int /*sizeB*/)
+ {
+ return 3 * (indexR + sizeR * (indexG + sizeG * indexB));
+ }
+
+
+ // RGB channel ordering.
+ // Pixels ordered in such a way that the red coordinate changes fastest,
+ // then the green coordinate, and finally, the blue coordinate changes slowest
+
+ inline int GetLut3DIndex_R(int indexR, int indexG, int indexB,
+ int /*sizeR*/, int sizeG, int sizeB)
+ {
+ return 3 * (indexB + sizeB * (indexG + sizeG * indexR));
+ }
+
+ // What is the preferred order for the lut3d?
+ // I.e., are the first two entries change along
+ // the blue direction, or the red direction?
+ // OpenGL expects 'red'
+
+ enum Lut3DOrder
+ {
+ LUT3DORDER_FAST_RED = 0,
+ LUT3DORDER_FAST_BLUE
+ };
+
+ void GenerateIdentityLut3D(float* img, int edgeLen, int numChannels,
+ Lut3DOrder lut3DOrder);
+
+ // Essentially the cube root, but will throw an exception if the
+ // cuberoot is not exact.
+ int Get3DLutEdgeLenFromNumPixels(int numPixels);
+
+
+
+ void CreateLut3DOp(OpRcPtrVec & ops,
+ Lut3DRcPtr lut,
+ Interpolation interpolation,
+ TransformDirection direction);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/MathUtils.cpp b/src/core/MathUtils.cpp
new file mode 100644
index 0000000..19691d7
--- /dev/null
+++ b/src/core/MathUtils.cpp
@@ -0,0 +1,603 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include <cstring>
+
+#include "MathUtils.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ const float FLTMIN = std::numeric_limits<float>::min();
+ }
+
+ bool IsScalarEqualToZero(float v)
+ {
+ return equalWithAbsError(v, 0.0f, FLTMIN);
+ }
+
+ bool IsScalarEqualToOne(float v)
+ {
+ return equalWithAbsError(v, 1.0f, FLTMIN);
+ }
+
+ float GetSafeScalarInverse(float v, float defaultValue)
+ {
+ if(IsScalarEqualToZero(v)) return defaultValue;
+ return 1.0f / v;
+ }
+
+ bool IsVecEqualToZero(const float* v, int size)
+ {
+ for(int i=0; i<size; ++i)
+ {
+ if(!IsScalarEqualToZero(v[i])) return false;
+ }
+ return true;
+ }
+
+ bool IsVecEqualToOne(const float* v, int size)
+ {
+ for(int i=0; i<size; ++i)
+ {
+ if(!IsScalarEqualToOne(v[i])) return false;
+ }
+ return true;
+ }
+
+ bool VecContainsZero(const float* v, int size)
+ {
+ for(int i=0; i<size; ++i)
+ {
+ if(IsScalarEqualToZero(v[i])) return true;
+ }
+ return false;
+ }
+
+ bool VecContainsOne(const float* v, int size)
+ {
+ for(int i=0; i<size; ++i)
+ {
+ if(IsScalarEqualToOne(v[i])) return true;
+ }
+ return false;
+ }
+
+ bool VecsEqualWithRelError(const float* v1, int size1,
+ const float* v2, int size2,
+ float e)
+ {
+ if(size1 != size2) return false;
+ for(int i=0; i<size1; ++i)
+ {
+ if(!equalWithRelError(v1[i], v2[i], e)) return false;
+ }
+
+ return true;
+ }
+
+ double ClampToNormHalf(double val)
+ {
+ if(val < -GetHalfMax())
+ {
+ return -GetHalfMax();
+ }
+
+ if(val > -GetHalfNormMin() && val<GetHalfNormMin())
+ {
+ return 0.0;
+ }
+
+ if(val > GetHalfMax())
+ {
+ return GetHalfMax();
+ }
+
+ return val;
+ }
+
+ bool IsM44Identity(const float* m44)
+ {
+ int index=0;
+
+ for(unsigned int j=0; j<4; ++j)
+ {
+ for(unsigned int i=0; i<4; ++i)
+ {
+ index = 4*j+i;
+
+ if(i==j)
+ {
+ if(!IsScalarEqualToOne(m44[index])) return false;
+ }
+ else
+ {
+ if(!IsScalarEqualToZero(m44[index])) return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool IsM44Diagonal(const float* m44)
+ {
+ for(int i=0; i<16; ++i)
+ {
+ if((i%5)==0) continue; // If we're on the diagonal, skip it
+ if(!IsScalarEqualToZero(m44[i])) return false;
+ }
+
+ return true;
+ }
+
+ void GetM44Diagonal(float* out4, const float* m44)
+ {
+ for(int i=0; i<4; ++i)
+ {
+ out4[i] = m44[i*5];
+ }
+ }
+
+ // We use an intermediate double representation to make sure
+ // there is minimal float precision error on the determinant's computation
+ // (We have seen IsScalarEqualToZero sensitivities here on 32-bit
+ // virtual machines)
+
+ bool GetM44Inverse(float* inverse_out, const float* m_)
+ {
+ double m[16];
+ for(unsigned int i=0; i<16; ++i) m[i] = (double)m_[i];
+
+ double d10_21 = m[4]*m[9] - m[5]*m[8];
+ double d10_22 = m[4]*m[10] - m[6]*m[8];
+ double d10_23 = m[4]*m[11] - m[7]*m[8];
+ double d11_22 = m[5]*m[10] - m[6]*m[9];
+ double d11_23 = m[5]*m[11] - m[7]*m[9];
+ double d12_23 = m[6]*m[11] - m[7]*m[10];
+
+ double a00 = m[13]*d12_23 - m[14]*d11_23 + m[15]*d11_22;
+ double a10 = m[14]*d10_23 - m[15]*d10_22 - m[12]*d12_23;
+ double a20 = m[12]*d11_23 - m[13]*d10_23 + m[15]*d10_21;
+ double a30 = m[13]*d10_22 - m[14]*d10_21 - m[12]*d11_22;
+
+ double det = a00*m[0] + a10*m[1] + a20*m[2] + a30*m[3];
+
+ if(IsScalarEqualToZero((float)det)) return false;
+
+ det = 1.0/det;
+
+ double d00_31 = m[0]*m[13] - m[1]*m[12];
+ double d00_32 = m[0]*m[14] - m[2]*m[12];
+ double d00_33 = m[0]*m[15] - m[3]*m[12];
+ double d01_32 = m[1]*m[14] - m[2]*m[13];
+ double d01_33 = m[1]*m[15] - m[3]*m[13];
+ double d02_33 = m[2]*m[15] - m[3]*m[14];
+
+ double a01 = m[9]*d02_33 - m[10]*d01_33 + m[11]*d01_32;
+ double a11 = m[10]*d00_33 - m[11]*d00_32 - m[8]*d02_33;
+ double a21 = m[8]*d01_33 - m[9]*d00_33 + m[11]*d00_31;
+ double a31 = m[9]*d00_32 - m[10]*d00_31 - m[8]*d01_32;
+
+ double a02 = m[6]*d01_33 - m[7]*d01_32 - m[5]*d02_33;
+ double a12 = m[4]*d02_33 - m[6]*d00_33 + m[7]*d00_32;
+ double a22 = m[5]*d00_33 - m[7]*d00_31 - m[4]*d01_33;
+ double a32 = m[4]*d01_32 - m[5]*d00_32 + m[6]*d00_31;
+
+ double a03 = m[2]*d11_23 - m[3]*d11_22 - m[1]*d12_23;
+ double a13 = m[0]*d12_23 - m[2]*d10_23 + m[3]*d10_22;
+ double a23 = m[1]*d10_23 - m[3]*d10_21 - m[0]*d11_23;
+ double a33 = m[0]*d11_22 - m[1]*d10_22 + m[2]*d10_21;
+
+ inverse_out[0] = (float) (a00*det);
+ inverse_out[1] = (float) (a01*det);
+ inverse_out[2] = (float) (a02*det);
+ inverse_out[3] = (float) (a03*det);
+ inverse_out[4] = (float) (a10*det);
+ inverse_out[5] = (float) (a11*det);
+ inverse_out[6] = (float) (a12*det);
+ inverse_out[7] = (float) (a13*det);
+ inverse_out[8] = (float) (a20*det);
+ inverse_out[9] = (float) (a21*det);
+ inverse_out[10] = (float) (a22*det);
+ inverse_out[11] = (float) (a23*det);
+ inverse_out[12] = (float) (a30*det);
+ inverse_out[13] = (float) (a31*det);
+ inverse_out[14] = (float) (a32*det);
+ inverse_out[15] = (float) (a33*det);
+
+ return true;
+ }
+
+ void GetM44M44Product(float* mout, const float* m1_, const float* m2_)
+ {
+ float m1[16];
+ float m2[16];
+ memcpy(m1, m1_, 16*sizeof(float));
+ memcpy(m2, m2_, 16*sizeof(float));
+
+ mout[ 0] = m1[ 0]*m2[0] + m1[ 1]*m2[4] + m1[ 2]*m2[ 8] + m1[ 3]*m2[12];
+ mout[ 1] = m1[ 0]*m2[1] + m1[ 1]*m2[5] + m1[ 2]*m2[ 9] + m1[ 3]*m2[13];
+ mout[ 2] = m1[ 0]*m2[2] + m1[ 1]*m2[6] + m1[ 2]*m2[10] + m1[ 3]*m2[14];
+ mout[ 3] = m1[ 0]*m2[3] + m1[ 1]*m2[7] + m1[ 2]*m2[11] + m1[ 3]*m2[15];
+ mout[ 4] = m1[ 4]*m2[0] + m1[ 5]*m2[4] + m1[ 6]*m2[ 8] + m1[ 7]*m2[12];
+ mout[ 5] = m1[ 4]*m2[1] + m1[ 5]*m2[5] + m1[ 6]*m2[ 9] + m1[ 7]*m2[13];
+ mout[ 6] = m1[ 4]*m2[2] + m1[ 5]*m2[6] + m1[ 6]*m2[10] + m1[ 7]*m2[14];
+ mout[ 7] = m1[ 4]*m2[3] + m1[ 5]*m2[7] + m1[ 6]*m2[11] + m1[ 7]*m2[15];
+ mout[ 8] = m1[ 8]*m2[0] + m1[ 9]*m2[4] + m1[10]*m2[ 8] + m1[11]*m2[12];
+ mout[ 9] = m1[ 8]*m2[1] + m1[ 9]*m2[5] + m1[10]*m2[ 9] + m1[11]*m2[13];
+ mout[10] = m1[ 8]*m2[2] + m1[ 9]*m2[6] + m1[10]*m2[10] + m1[11]*m2[14];
+ mout[11] = m1[ 8]*m2[3] + m1[ 9]*m2[7] + m1[10]*m2[11] + m1[11]*m2[15];
+ mout[12] = m1[12]*m2[0] + m1[13]*m2[4] + m1[14]*m2[ 8] + m1[15]*m2[12];
+ mout[13] = m1[12]*m2[1] + m1[13]*m2[5] + m1[14]*m2[ 9] + m1[15]*m2[13];
+ mout[14] = m1[12]*m2[2] + m1[13]*m2[6] + m1[14]*m2[10] + m1[15]*m2[14];
+ mout[15] = m1[12]*m2[3] + m1[13]*m2[7] + m1[14]*m2[11] + m1[15]*m2[15];
+ }
+
+ namespace
+ {
+
+ void GetM44V4Product(float* vout, const float* m, const float* v_)
+ {
+ float v[4];
+ memcpy(v, v_, 4*sizeof(float));
+
+ vout[0] = m[ 0]*v[0] + m[ 1]*v[1] + m[ 2]*v[2] + m[ 3]*v[3];
+ vout[1] = m[ 4]*v[0] + m[ 5]*v[1] + m[ 6]*v[2] + m[ 7]*v[3];
+ vout[2] = m[ 8]*v[0] + m[ 9]*v[1] + m[10]*v[2] + m[11]*v[3];
+ vout[3] = m[12]*v[0] + m[13]*v[1] + m[14]*v[2] + m[15]*v[3];
+ }
+
+ void GetV4Sum(float* vout, const float* v1, const float* v2)
+ {
+ for(int i=0; i<4; ++i)
+ {
+ vout[i] = v1[i] + v2[i];
+ }
+ }
+
+ } // anon namespace
+
+ // All m(s) are 4x4. All v(s) are size 4 vectors.
+ // Return mout, vout, where mout*x+vout == m2*(m1*x+v1)+v2
+ // mout = m2*m1
+ // vout = m2*v1 + v2
+ void GetMxbCombine(float* mout, float* vout,
+ const float* m1_, const float* v1_,
+ const float* m2_, const float* v2_)
+ {
+ float m1[16];
+ float v1[4];
+ float m2[16];
+ float v2[4];
+ memcpy(m1, m1_, 16*sizeof(float));
+ memcpy(v1, v1_, 4*sizeof(float));
+ memcpy(m2, m2_, 16*sizeof(float));
+ memcpy(v2, v2_, 4*sizeof(float));
+
+ GetM44M44Product(mout, m2, m1);
+ GetM44V4Product(vout, m2, v1);
+ GetV4Sum(vout, vout, v2);
+ }
+
+ namespace
+ {
+
+ void GetMxbResult(float* vout, float* m, float* x, float* v)
+ {
+ GetM44V4Product(vout, m, x);
+ GetV4Sum(vout, vout, v);
+ }
+
+ } // anon namespace
+
+ bool GetMxbInverse(float* mout, float* vout,
+ const float* m_, const float* v_)
+ {
+ float m[16];
+ float v[4];
+ memcpy(m, m_, 16*sizeof(float));
+ memcpy(v, v_, 4*sizeof(float));
+
+ if(!GetM44Inverse(mout, m)) return false;
+
+ for(int i=0; i<4; ++i)
+ {
+ v[i] = -v[i];
+ }
+ GetM44V4Product(vout, mout, v);
+
+ return true;
+ }
+
+}
+
+OCIO_NAMESPACE_EXIT
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+OCIO_NAMESPACE_USING
+
+#include "UnitTest.h"
+
+OIIO_ADD_TEST(MathUtils, M44_is_diagonal)
+{
+ {
+ float m44[] = { 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+ bool isdiag = IsM44Diagonal(m44);
+ OIIO_CHECK_EQUAL(isdiag, true);
+
+ m44[1] += 1e-8f;
+ isdiag = IsM44Diagonal(m44);
+ OIIO_CHECK_EQUAL(isdiag, false);
+ }
+}
+
+
+OIIO_ADD_TEST(MathUtils, IsScalarEqualToZero)
+{
+ OIIO_CHECK_EQUAL(IsScalarEqualToZero(0.0f), true);
+ OIIO_CHECK_EQUAL(IsScalarEqualToZero(-0.0f), true);
+
+ OIIO_CHECK_EQUAL(IsScalarEqualToZero(-1.072883670794056e-09f), false);
+ OIIO_CHECK_EQUAL(IsScalarEqualToZero(1.072883670794056e-09f), false);
+
+ OIIO_CHECK_EQUAL(IsScalarEqualToZero(-1.072883670794056e-03f), false);
+ OIIO_CHECK_EQUAL(IsScalarEqualToZero(1.072883670794056e-03f), false);
+
+ OIIO_CHECK_EQUAL(IsScalarEqualToZero(-1.072883670794056e-01f), false);
+ OIIO_CHECK_EQUAL(IsScalarEqualToZero(1.072883670794056e-01f), false);
+}
+
+OIIO_ADD_TEST(MathUtils, GetM44Inverse)
+{
+ // This is a degenerate matrix, and shouldnt be invertible.
+ float m[] = { 0.3f, 0.3f, 0.3f, 0.0f,
+ 0.3f, 0.3f, 0.3f, 0.0f,
+ 0.3f, 0.3f, 0.3f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+
+ float mout[16];
+ bool invertsuccess = GetM44Inverse(mout, m);
+ OIIO_CHECK_EQUAL(invertsuccess, false);
+}
+
+
+OIIO_ADD_TEST(MathUtils, M44_M44_product)
+{
+ {
+ float mout[16];
+ float m1[] = { 1.0f, 2.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 1.0f, 0.0f,
+ 1.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 1.0f, 3.0f, 1.0f };
+ float m2[] = { 1.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 2.0f, 0.0f, 0.0f, 1.0f };
+ GetM44M44Product(mout, m1, m2);
+
+ float mcorrect[] = { 1.0f, 3.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 1.0f, 0.0f,
+ 1.0f, 1.0f, 1.0f, 0.0f,
+ 2.0f, 1.0f, 3.0f, 1.0f };
+
+ for(int i=0; i<16; ++i)
+ {
+ OIIO_CHECK_EQUAL(mout[i], mcorrect[i]);
+ }
+ }
+}
+
+OIIO_ADD_TEST(MathUtils, M44_V4_product)
+{
+ {
+ float vout[4];
+ float m[] = { 1.0f, 2.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 1.0f, 0.0f,
+ 1.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 1.0f, 3.0f, 1.0f };
+ float v[] = { 1.0f, 2.0f, 3.0f, 4.0f };
+ GetM44V4Product(vout, m, v);
+
+ float vcorrect[] = { 5.0f, 5.0f, 4.0f, 15.0f };
+
+ for(int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_EQUAL(vout[i], vcorrect[i]);
+ }
+ }
+}
+
+OIIO_ADD_TEST(MathUtils, V4_add)
+{
+ {
+ float vout[4];
+ float v1[] = { 1.0f, 2.0f, 3.0f, 4.0f };
+ float v2[] = { 3.0f, 1.0f, 4.0f, 1.0f };
+ GetV4Sum(vout, v1, v2);
+
+ float vcorrect[] = { 4.0f, 3.0f, 7.0f, 5.0f };
+
+ for(int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_EQUAL(vout[i], vcorrect[i]);
+ }
+ }
+}
+
+OIIO_ADD_TEST(MathUtils, mxb_eval)
+{
+ {
+ float vout[4];
+ float m[] = { 1.0f, 2.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 1.0f, 0.0f,
+ 1.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 1.0f, 3.0f, 1.0f };
+ float x[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ float v[] = { 1.0f, 2.0f, 3.0f, 4.0f };
+ GetMxbResult(vout, m, x, v);
+
+ float vcorrect[] = { 4.0f, 4.0f, 5.0f, 9.0f };
+
+ for(int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_EQUAL(vout[i], vcorrect[i]);
+ }
+ }
+}
+
+OIIO_ADD_TEST(MathUtils, Combine_two_mxb)
+{
+ float m1[] = { 1.0f, 0.0f, 2.0f, 0.0f,
+ 2.0f, 1.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 2.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 1.0f };
+ float v1[] = { 1.0f, 2.0f, 3.0f, 4.0f };
+ float m2[] = { 2.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 3.0f, 0.0f,
+ 1.0f,1.0f, 1.0f, 1.0f };
+ float v2[] = { 0.0f, 2.0f, 1.0f, 0.0f };
+ float tolerance = 1e-9f;
+
+ {
+ float x[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ float vout[4];
+
+ // Combine two mx+b operations, and apply to test point
+ float mout[16];
+ float vcombined[4];
+ GetMxbCombine(mout, vout, m1, v1, m2, v2);
+ GetMxbResult(vcombined, mout, x, vout);
+
+ // Sequentially apply the two mx+b operations.
+ GetMxbResult(vout, m1, x, v1);
+ GetMxbResult(vout, m2, vout, v2);
+
+ // Compare outputs
+ for(int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(vcombined[i], vout[i], tolerance);
+ }
+ }
+
+ {
+ float x[] = { 6.0f, 0.5f, -2.0f, -0.1f };
+ float vout[4];
+
+ float mout[16];
+ float vcombined[4];
+ GetMxbCombine(mout, vout, m1, v1, m2, v2);
+ GetMxbResult(vcombined, mout, x, vout);
+
+ GetMxbResult(vout, m1, x, v1);
+ GetMxbResult(vout, m2, vout, v2);
+
+ for(int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(vcombined[i], vout[i], tolerance);
+ }
+ }
+
+ {
+ float x[] = { 26.0f, -0.5f, 0.005f, 12.1f };
+ float vout[4];
+
+ float mout[16];
+ float vcombined[4];
+ GetMxbCombine(mout, vout, m1, v1, m2, v2);
+ GetMxbResult(vcombined, mout, x, vout);
+
+ GetMxbResult(vout, m1, x, v1);
+ GetMxbResult(vout, m2, vout, v2);
+
+ // We pick a not so small tolerance, as we're dealing with
+ // large numbers, and the error for CHECK_CLOSE is absolute.
+ for(int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(vcombined[i], vout[i], 1e-3);
+ }
+ }
+}
+
+OIIO_ADD_TEST(MathUtils, mxb_invert)
+{
+ {
+ float m[] = { 1.0f, 2.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 1.0f, 0.0f,
+ 1.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 1.0f, 3.0f, 1.0f };
+ float x[] = { 1.0f, 0.5f, -1.0f, 60.0f };
+ float v[] = { 1.0f, 2.0f, 3.0f, 4.0f };
+
+ float vresult[4];
+ float mout[16];
+ float vout[4];
+
+ GetMxbResult(vresult, m, x, v);
+ bool invertsuccess = GetMxbInverse(mout, vout, m, v);
+ OIIO_CHECK_EQUAL(invertsuccess, true);
+
+ GetMxbResult(vresult, mout, vresult, vout);
+
+ float tolerance = 1e-9f;
+ for(int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(vresult[i], x[i], tolerance);
+ }
+ }
+
+ {
+ float m[] = { 0.3f, 0.3f, 0.3f, 0.0f,
+ 0.3f, 0.3f, 0.3f, 0.0f,
+ 0.3f, 0.3f, 0.3f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+ float v[] = { 0.0f, 0.0f, 0.0f, 0.0f };
+
+ float mout[16];
+ float vout[4];
+
+ bool invertsuccess = GetMxbInverse(mout, vout, m, v);
+ OIIO_CHECK_EQUAL(invertsuccess, false);
+ }
+}
+
+#endif
+
diff --git a/src/core/MathUtils.h b/src/core/MathUtils.h
new file mode 100644
index 0000000..ffe66f3
--- /dev/null
+++ b/src/core/MathUtils.h
@@ -0,0 +1,186 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_MATHUTILS_H
+#define INCLUDED_OCIO_MATHUTILS_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include <cmath>
+#include <vector>
+
+#include "Op.h"
+#include "Platform.h"
+
+#ifdef WINDOWS
+#include <float.h>
+#endif
+
+OCIO_NAMESPACE_ENTER
+{
+ // From Imath
+ //--------------------------------------------------------------------------
+ // Compare two numbers and test if they are "approximately equal":
+ //
+ // equalWithAbsError (x1, x2, e)
+ //
+ // Returns true if x1 is the same as x2 with an absolute error of
+ // no more than e,
+ //
+ // abs (x1 - x2) <= e
+ //
+ // equalWithRelError (x1, x2, e)
+ //
+ // Returns true if x1 is the same as x2 with an relative error of
+ // no more than e,
+ //
+ // abs (x1 - x2) <= e * x1
+ //
+ //--------------------------------------------------------------------------
+
+ inline bool equalWithAbsError (float x1, float x2, float e)
+ {
+ return ((x1 > x2)? x1 - x2: x2 - x1) <= e;
+ }
+
+ inline bool equalWithRelError (float x1, float x2, float e)
+ {
+ return ((x1 > x2)? x1 - x2: x2 - x1) <= e * ((x1 > 0)? x1: -x1);
+ }
+
+ inline float lerpf(float a, float b, float z)
+ {
+ return (b - a) * z + a;
+ }
+
+#ifdef WINDOWS
+ inline double
+ round (float val) {
+ return floor (val + 0.5);
+ }
+
+ inline float
+ roundf (float val) {
+ return static_cast<float>(round (val));
+ }
+
+ inline int
+ isnan (float val) {
+ // Windows uses a non-standard version of 'isnan'
+ return _isnan (val);
+ }
+#else
+
+#ifdef ANDROID
+// support std::isnan - needs to be tested as it might not be part of the NDK
+#define _GLIBCXX_USE_C99_MATH 1
+#endif
+
+ // This lets all platforms just use isnan, within the OCIO namespace,
+ // across all platforms. (Windows defines the function above).
+ using std::isnan;
+#endif
+
+ // Checks within fltmin tolerance
+ bool IsScalarEqualToZero(float v);
+ bool IsScalarEqualToOne(float v);
+
+ // Are all the vector components the specified value?
+ bool IsVecEqualToZero(const float* v, int size);
+ bool IsVecEqualToOne(const float* v, int size);
+
+ // Is at least one of the specified components equal to 0?
+ bool VecContainsZero(const float* v, int size);
+ bool VecContainsOne(const float* v, int size);
+
+ // Are two vectors equal? (Same size, same values?)
+ bool VecsEqualWithRelError(const float* v1, int size1,
+ const float* v2, int size2,
+ float e);
+
+ inline double GetHalfMax()
+ {
+ return 65504.0; // Largest positive half
+ }
+
+ inline double GetHalfMin()
+ {
+ return 5.96046448e-08; // Smallest positive half;
+ }
+
+ inline double GetHalfNormMin()
+ {
+ return 6.10351562e-05; // Smallest positive normalized half
+ }
+
+ //! Clamp the specified value to the valid range of normalized half.
+ // (can be either positive or negative though
+
+ double ClampToNormHalf(double val);
+
+ float GetSafeScalarInverse(float v, float defaultValue = 1.0);
+
+
+ // All matrix / vector operations use the following sizing...
+ //
+ // m : 4x4 matrix
+ // v : 4 column vector
+
+ // Return the 4x4 inverse, and whether the inverse has succeeded.
+ // Supports in-place operations
+ bool GetM44Inverse(float* mout, const float* m);
+
+ // Is an identity matrix? (with fltmin tolerance)
+ bool IsM44Identity(const float* m);
+
+ // Is this a purely diagonal matrix?
+ bool IsM44Diagonal(const float* m);
+
+ // Extract the diagonal
+ void GetM44Diagonal(float* vout, const float* m);
+
+ // Get the product, out = m1*m2
+ // Supports in-place operations
+ void GetM44Product(float* mout, const float* m1, const float* m2);
+
+ // Combine two transforms in the mx+b form, into a single transform
+ // mout*x+vout == m2*(m1*x+v1)+v2
+ // Supports in-place operations
+ void GetMxbCombine(float* mout, float* vout,
+ const float* m1, const float* v1,
+ const float* m2, const float* v2);
+
+ // Supports in-place operations
+ bool GetMxbInverse(float* mout, float* vout,
+ const float* m, const float* v);
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/MatrixOps.cpp b/src/core/MatrixOps.cpp
new file mode 100644
index 0000000..a0d4ef3
--- /dev/null
+++ b/src/core/MatrixOps.cpp
@@ -0,0 +1,788 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "GpuShaderUtils.h"
+#include "HashUtils.h"
+#include "MatrixOps.h"
+#include "MathUtils.h"
+
+#include <cstring>
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ void ApplyScale(float* rgbaBuffer, long numPixels,
+ const float* scale4)
+ {
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ rgbaBuffer[0] *= scale4[0];
+ rgbaBuffer[1] *= scale4[1];
+ rgbaBuffer[2] *= scale4[2];
+ rgbaBuffer[3] *= scale4[3];
+
+ rgbaBuffer += 4;
+ }
+ }
+
+ void ApplyOffset(float* rgbaBuffer, long numPixels,
+ const float* offset4)
+ {
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ rgbaBuffer[0] += offset4[0];
+ rgbaBuffer[1] += offset4[1];
+ rgbaBuffer[2] += offset4[2];
+ rgbaBuffer[3] += offset4[3];
+
+ rgbaBuffer += 4;
+ }
+ }
+
+ void ApplyMatrix(float* rgbaBuffer, long numPixels,
+ const float* mat44)
+ {
+ float r,g,b,a;
+
+ for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
+ {
+ r = rgbaBuffer[0];
+ g = rgbaBuffer[1];
+ b = rgbaBuffer[2];
+ a = rgbaBuffer[3];
+
+ rgbaBuffer[0] = r*mat44[0] + g*mat44[1] + b*mat44[2] + a*mat44[3];
+ rgbaBuffer[1] = r*mat44[4] + g*mat44[5] + b*mat44[6] + a*mat44[7];
+ rgbaBuffer[2] = r*mat44[8] + g*mat44[9] + b*mat44[10] + a*mat44[11];
+ rgbaBuffer[3] = r*mat44[12] + g*mat44[13] + b*mat44[14] + a*mat44[15];
+
+ rgbaBuffer += 4;
+ }
+ }
+ }
+
+
+
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+
+
+ namespace
+ {
+ class MatrixOffsetOp : public Op
+ {
+ public:
+ MatrixOffsetOp(const float * m44,
+ const float * offset4,
+ TransformDirection direction);
+ virtual ~MatrixOffsetOp();
+
+ virtual OpRcPtr clone() const;
+
+ virtual std::string getInfo() const;
+ virtual std::string getCacheID() const;
+
+ virtual bool isNoOp() const;
+ virtual bool isSameType(const OpRcPtr & op) const;
+ virtual bool isInverse(const OpRcPtr & op) const;
+ virtual bool canCombineWith(const OpRcPtr & op) const;
+ virtual void combineWith(OpRcPtrVec & ops, const OpRcPtr & secondOp) const;
+
+ virtual bool hasChannelCrosstalk() const;
+ virtual void finalize();
+ virtual void apply(float* rgbaBuffer, long numPixels) const;
+
+ virtual bool supportsGpuShader() const;
+ virtual void writeGpuShader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc) const;
+
+ private:
+ bool m_isNoOp;
+ float m_m44[16];
+ float m_offset4[4];
+ TransformDirection m_direction;
+
+ // Set in finalize
+ bool m_m44IsIdentity;
+ bool m_m44IsDiagonal;
+ bool m_offset4IsIdentity;
+ float m_m44_inv[16];
+ std::string m_cacheID;
+ };
+
+
+ typedef OCIO_SHARED_PTR<MatrixOffsetOp> MatrixOffsetOpRcPtr;
+
+
+ MatrixOffsetOp::MatrixOffsetOp(const float * m44,
+ const float * offset4,
+ TransformDirection direction):
+ Op(),
+ m_isNoOp(false),
+ m_direction(direction),
+ m_m44IsIdentity(false),
+ m_offset4IsIdentity(false)
+ {
+ if(m_direction == TRANSFORM_DIR_UNKNOWN)
+ {
+ throw Exception("Cannot apply MatrixOffsetOp op, unspecified transform direction.");
+ }
+
+ memcpy(m_m44, m44, 16*sizeof(float));
+ memcpy(m_offset4, offset4, 4*sizeof(float));
+
+ memset(m_m44_inv, 0, 16*sizeof(float));
+
+ // This Op will be a NoOp if and old if both the offset and matrix
+ // are identity. This hold true no matter what the direction is,
+ // so we can compute this ahead of time.
+ m_isNoOp = (IsVecEqualToZero(m_offset4, 4) && IsM44Identity(m_m44));
+ }
+
+ OpRcPtr MatrixOffsetOp::clone() const
+ {
+ OpRcPtr op = OpRcPtr(new MatrixOffsetOp(m_m44, m_offset4, m_direction));
+ return op;
+ }
+
+ MatrixOffsetOp::~MatrixOffsetOp()
+ { }
+
+ std::string MatrixOffsetOp::getInfo() const
+ {
+ return "<MatrixOffsetOp>";
+ }
+
+ std::string MatrixOffsetOp::getCacheID() const
+ {
+ return m_cacheID;
+ }
+
+ bool MatrixOffsetOp::isNoOp() const
+ {
+ return m_isNoOp;
+ }
+
+ bool MatrixOffsetOp::isSameType(const OpRcPtr & op) const
+ {
+ MatrixOffsetOpRcPtr typedRcPtr = DynamicPtrCast<MatrixOffsetOp>(op);
+ if(!typedRcPtr) return false;
+ return true;
+ }
+
+ bool MatrixOffsetOp::isInverse(const OpRcPtr & op) const
+ {
+ MatrixOffsetOpRcPtr typedRcPtr = DynamicPtrCast<MatrixOffsetOp>(op);
+ if(!typedRcPtr) return false;
+
+ if(GetInverseTransformDirection(m_direction) != typedRcPtr->m_direction)
+ return false;
+
+ float error = std::numeric_limits<float>::min();
+ if(!VecsEqualWithRelError(m_m44, 16, typedRcPtr->m_m44, 16, error))
+ return false;
+ if(!VecsEqualWithRelError(m_offset4, 4,typedRcPtr->m_offset4, 4, error))
+ return false;
+
+ return true;
+ }
+
+ bool MatrixOffsetOp::canCombineWith(const OpRcPtr & op) const
+ {
+ return isSameType(op);
+ }
+
+ void MatrixOffsetOp::combineWith(OpRcPtrVec & ops, const OpRcPtr & secondOp) const
+ {
+ MatrixOffsetOpRcPtr typedRcPtr = DynamicPtrCast<MatrixOffsetOp>(secondOp);
+ if(!typedRcPtr)
+ {
+ std::ostringstream os;
+ os << "MatrixOffsetOp can only be combined with other ";
+ os << "MatrixOffsetOps. secondOp:" << secondOp->getInfo();
+ throw Exception(os.str().c_str());
+ }
+
+ float mout[16];
+ float vout[4];
+
+ if(m_direction == TRANSFORM_DIR_FORWARD &&
+ typedRcPtr->m_direction == TRANSFORM_DIR_FORWARD)
+ {
+ GetMxbCombine(mout, vout,
+ m_m44, m_offset4,
+ typedRcPtr->m_m44, typedRcPtr->m_offset4);
+ }
+ else if(m_direction == TRANSFORM_DIR_FORWARD &&
+ typedRcPtr->m_direction == TRANSFORM_DIR_INVERSE)
+ {
+ float minv2[16];
+ float vinv2[4];
+
+ if(!GetMxbInverse(minv2, vinv2, typedRcPtr->m_m44, typedRcPtr->m_offset4))
+ {
+ std::ostringstream os;
+ os << "Cannot invert second MatrixOffsetOp op. ";
+ os << "Matrix inverse does not exist for (";
+ for(int i=0; i<16; ++i)
+ {
+ os << typedRcPtr->m_m44[i] << " ";
+ }
+ os << ").";
+ throw Exception(os.str().c_str());
+ }
+
+ GetMxbCombine(mout, vout,
+ m_m44, m_offset4,
+ minv2, vinv2);
+ }
+ else if(m_direction == TRANSFORM_DIR_INVERSE &&
+ typedRcPtr->m_direction == TRANSFORM_DIR_FORWARD)
+ {
+ float minv1[16];
+ float vinv1[4];
+
+ if(!GetMxbInverse(minv1, vinv1, m_m44, m_offset4))
+ {
+ std::ostringstream os;
+ os << "Cannot invert primary MatrixOffsetOp op. ";
+ os << "Matrix inverse does not exist for (";
+ for(int i=0; i<16; ++i)
+ {
+ os << m_m44[i] << " ";
+ }
+ os << ").";
+ throw Exception(os.str().c_str());
+ }
+
+ GetMxbCombine(mout, vout,
+ minv1, vinv1,
+ typedRcPtr->m_m44, typedRcPtr->m_offset4);
+
+ }
+ else if(m_direction == TRANSFORM_DIR_INVERSE &&
+ typedRcPtr->m_direction == TRANSFORM_DIR_INVERSE)
+ {
+ float minv1[16];
+ float vinv1[4];
+ float minv2[16];
+ float vinv2[4];
+
+ if(!GetMxbInverse(minv1, vinv1, m_m44, m_offset4))
+ {
+ std::ostringstream os;
+ os << "Cannot invert primary MatrixOffsetOp op. ";
+ os << "Matrix inverse does not exist for (";
+ for(int i=0; i<16; ++i)
+ {
+ os << m_m44[i] << " ";
+ }
+ os << ").";
+ throw Exception(os.str().c_str());
+ }
+
+ if(!GetMxbInverse(minv2, vinv2, typedRcPtr->m_m44, typedRcPtr->m_offset4))
+ {
+ std::ostringstream os;
+ os << "Cannot invert second MatrixOffsetOp op. ";
+ os << "Matrix inverse does not exist for (";
+ for(int i=0; i<16; ++i)
+ {
+ os << typedRcPtr->m_m44[i] << " ";
+ }
+ os << ").";
+ throw Exception(os.str().c_str());
+ }
+
+ GetMxbCombine(mout, vout,
+ minv1, vinv1,
+ minv2, vinv2);
+ }
+ else
+ {
+ std::ostringstream os;
+ os << "MatrixOffsetOp cannot combine ops with unspecified ";
+ os << "directions. First op: " << m_direction << " ";
+ os << "secondOp:" << typedRcPtr->m_direction;
+ throw Exception(os.str().c_str());
+ }
+
+ CreateMatrixOffsetOp(ops,
+ mout, vout,
+ TRANSFORM_DIR_FORWARD);
+ }
+
+ bool MatrixOffsetOp::hasChannelCrosstalk() const
+ {
+ return (!m_m44IsDiagonal);
+ }
+
+ void MatrixOffsetOp::finalize()
+ {
+ m_offset4IsIdentity = IsVecEqualToZero(m_offset4, 4);
+ m_m44IsIdentity = IsM44Identity(m_m44);
+ m_m44IsDiagonal = IsM44Diagonal(m_m44);
+
+ if(m_direction == TRANSFORM_DIR_INVERSE)
+ {
+ if(!GetM44Inverse(m_m44_inv, m_m44))
+ {
+ std::ostringstream os;
+ os << "Cannot apply MatrixOffsetOp op. ";
+ os << "Matrix inverse does not exist for m44 (";
+ for(int i=0; i<16; ++i) os << m_m44[i] << " ";
+ os << ").";
+ throw Exception(os.str().c_str());
+ }
+ }
+
+ // Create the cacheID
+ md5_state_t state;
+ md5_byte_t digest[16];
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)m_m44, 16*sizeof(float));
+ md5_append(&state, (const md5_byte_t *)m_offset4, 4*sizeof(float));
+ md5_finish(&state, digest);
+
+ std::ostringstream cacheIDStream;
+ cacheIDStream << "<MatrixOffsetOp ";
+ cacheIDStream << GetPrintableHash(digest) << " ";
+ cacheIDStream << TransformDirectionToString(m_direction) << " ";
+ cacheIDStream << ">";
+
+ m_cacheID = cacheIDStream.str();
+ }
+
+ void MatrixOffsetOp::apply(float* rgbaBuffer, long numPixels) const
+ {
+ if(m_direction == TRANSFORM_DIR_FORWARD)
+ {
+ if(!m_m44IsIdentity)
+ {
+ if(m_m44IsDiagonal)
+ {
+ float scale[4];
+ GetM44Diagonal(scale, m_m44);
+ ApplyScale(rgbaBuffer, numPixels, scale);
+ }
+ else
+ {
+ ApplyMatrix(rgbaBuffer, numPixels, m_m44);
+ }
+ }
+
+ if(!m_offset4IsIdentity)
+ {
+ ApplyOffset(rgbaBuffer, numPixels, m_offset4);
+ }
+ }
+ else if(m_direction == TRANSFORM_DIR_INVERSE)
+ {
+ if(!m_offset4IsIdentity)
+ {
+ float offset_inv[] = { -m_offset4[0],
+ -m_offset4[1],
+ -m_offset4[2],
+ -m_offset4[3] };
+
+ ApplyOffset(rgbaBuffer, numPixels, offset_inv);
+ }
+
+ if(!m_m44IsIdentity)
+ {
+ if(m_m44IsDiagonal)
+ {
+ float scale[4];
+ GetM44Diagonal(scale, m_m44_inv);
+ ApplyScale(rgbaBuffer, numPixels, scale);
+ }
+ else
+ {
+ ApplyMatrix(rgbaBuffer, numPixels, m_m44_inv);
+ }
+ }
+ }
+ } // Op::process
+
+ bool MatrixOffsetOp::supportsGpuShader() const
+ {
+ return true;
+ }
+
+ void MatrixOffsetOp::writeGpuShader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc) const
+ {
+ GpuLanguage lang = shaderDesc.getLanguage();
+
+ // TODO: This should not act upon alpha,
+ // since we dont apply it on the CPU?
+
+ if(m_direction == TRANSFORM_DIR_FORWARD)
+ {
+ if(!m_m44IsIdentity)
+ {
+ if(m_m44IsDiagonal)
+ {
+ shader << pixelName << " = ";
+ float scale[4];
+ GetM44Diagonal(scale, m_m44);
+ Write_half4(shader, scale, lang);
+ shader << " * " << pixelName << ";\n";
+ }
+ else
+ {
+ shader << pixelName << " = ";
+ Write_mtx_x_vec(shader,
+ GpuTextHalf4x4(m_m44, lang), pixelName,
+ lang);
+ shader << ";\n";
+ }
+ }
+
+ if(!m_offset4IsIdentity)
+ {
+ shader << pixelName << " = ";
+ Write_half4(shader, m_offset4, lang);
+ shader << " + " << pixelName << ";\n";
+ }
+ }
+ else if(m_direction == TRANSFORM_DIR_INVERSE)
+ {
+ if(!m_offset4IsIdentity)
+ {
+ float offset_inv[] = { -m_offset4[0],
+ -m_offset4[1],
+ -m_offset4[2],
+ -m_offset4[3] };
+
+ shader << pixelName << " = ";
+ Write_half4(shader, offset_inv, lang);
+ shader << " + " << pixelName << ";\n";
+ }
+
+ if(!m_m44IsIdentity)
+ {
+ if(m_m44IsDiagonal)
+ {
+ shader << pixelName << " = ";
+ float scale[4];
+ GetM44Diagonal(scale, m_m44_inv);
+ Write_half4(shader, scale, lang);
+ shader << " * " << pixelName << ";\n";
+ }
+ else
+ {
+ shader << pixelName << " = ";
+ Write_mtx_x_vec(shader,
+ GpuTextHalf4x4(m_m44_inv, lang), pixelName,
+ lang);
+ shader << ";\n";
+ }
+ }
+ }
+ }
+
+ } // Anon namespace
+
+
+
+
+
+
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+
+
+
+ void CreateScaleOp(OpRcPtrVec & ops,
+ const float * scale4,
+ TransformDirection direction)
+ {
+ float offset4[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ CreateScaleOffsetOp(ops, scale4, offset4, direction);
+ }
+
+ void CreateMatrixOp(OpRcPtrVec & ops,
+ const float * m44,
+ TransformDirection direction)
+ {
+ float offset4[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ CreateMatrixOffsetOp(ops, m44, offset4, direction);
+ }
+
+ void CreateOffsetOp(OpRcPtrVec & ops,
+ const float * offset4,
+ TransformDirection direction)
+ {
+ float scale4[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ CreateScaleOffsetOp(ops, scale4, offset4, direction);
+ }
+
+ void CreateScaleOffsetOp(OpRcPtrVec & ops,
+ const float * scale4, const float * offset4,
+ TransformDirection direction)
+ {
+ float m44[16];
+ memset(m44, 0, 16*sizeof(float));
+
+ m44[0] = scale4[0];
+ m44[5] = scale4[1];
+ m44[10] = scale4[2];
+ m44[15] = scale4[3];
+
+ CreateMatrixOffsetOp(ops,
+ m44, offset4,
+ direction);
+ }
+
+ void CreateSaturationOp(OpRcPtrVec & ops,
+ float sat,
+ const float * lumaCoef3,
+ TransformDirection direction)
+ {
+ float matrix[16];
+ float offset[4];
+ MatrixTransform::Sat(matrix, offset,
+ sat, lumaCoef3);
+
+ CreateMatrixOffsetOp(ops, matrix, offset, direction);
+ }
+
+ void CreateMatrixOffsetOp(OpRcPtrVec & ops,
+ const float * m44, const float * offset4,
+ TransformDirection direction)
+ {
+ bool mtxIsIdentity = IsM44Identity(m44);
+ bool offsetIsIdentity = IsVecEqualToZero(offset4, 4);
+ if(mtxIsIdentity && offsetIsIdentity) return;
+
+ ops.push_back( MatrixOffsetOpRcPtr(new MatrixOffsetOp(m44,
+ offset4, direction)) );
+ }
+
+ void CreateFitOp(OpRcPtrVec & ops,
+ const float * oldmin4, const float * oldmax4,
+ const float * newmin4, const float * newmax4,
+ TransformDirection direction)
+ {
+ float matrix[16];
+ float offset[4];
+ MatrixTransform::Fit(matrix, offset,
+ oldmin4, oldmax4,
+ newmin4, newmax4);
+
+ CreateMatrixOffsetOp(ops, matrix, offset, direction);
+ }
+
+}
+OCIO_NAMESPACE_EXIT
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+OCIO_NAMESPACE_USING
+
+OIIO_ADD_TEST(MatrixOps, Combining)
+{
+ float m1[16] = { 1.1f, 0.2f, 0.3f, 0.4f,
+ 0.5f, 1.6f, 0.7f, 0.8f,
+ 0.2f, 0.1f, 1.1f, 0.2f,
+ 0.3f, 0.4f, 0.5f, 1.6f };
+
+ float v1[4] = { -0.5f, -0.25f, 0.25f, 0.0f };
+
+ float m2[16] = { 1.1f, -0.1f, -0.1f, 0.0f,
+ 0.1f, 0.9f, -0.2f, 0.0f,
+ 0.05f, 0.0f, 1.1f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+ float v2[4] = { -0.2f, -0.1f, -0.1f, -0.2f };
+
+ const float source[] = { 0.1f, 0.2f, 0.3f, 0.4f,
+ -0.1f, -0.2f, 50.0f, 123.4f,
+ 1.0f, 1.0f, 1.0f, 1.0f };
+ float error = 1e-4f;
+
+ {
+ OpRcPtrVec ops;
+ CreateMatrixOffsetOp(ops, m1, v1, TRANSFORM_DIR_FORWARD);
+ CreateMatrixOffsetOp(ops, m2, v2, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(ops.size(), 2);
+ ops[0]->finalize();
+ ops[1]->finalize();
+
+ OpRcPtrVec combined;
+ ops[0]->combineWith(combined, ops[1]);
+ OIIO_CHECK_EQUAL(combined.size(), 1);
+ combined[0]->finalize();
+
+ for(int test=0; test<3; ++test)
+ {
+ float tmp[4];
+ memcpy(tmp, &source[4*test], 4*sizeof(float));
+ ops[0]->apply(tmp, 1);
+ ops[1]->apply(tmp, 1);
+
+ float tmp2[4];
+ memcpy(tmp2, &source[4*test], 4*sizeof(float));
+ combined[0]->apply(tmp2, 1);
+
+ for(unsigned int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(tmp2[i], tmp[i], error);
+ }
+ }
+ }
+
+
+ {
+ OpRcPtrVec ops;
+ CreateMatrixOffsetOp(ops, m1, v1, TRANSFORM_DIR_FORWARD);
+ CreateMatrixOffsetOp(ops, m2, v2, TRANSFORM_DIR_INVERSE);
+ OIIO_CHECK_EQUAL(ops.size(), 2);
+ ops[0]->finalize();
+ ops[1]->finalize();
+
+ OpRcPtrVec combined;
+ ops[0]->combineWith(combined, ops[1]);
+ OIIO_CHECK_EQUAL(combined.size(), 1);
+ combined[0]->finalize();
+
+
+ for(int test=0; test<3; ++test)
+ {
+ float tmp[4];
+ memcpy(tmp, &source[4*test], 4*sizeof(float));
+ ops[0]->apply(tmp, 1);
+ ops[1]->apply(tmp, 1);
+
+ float tmp2[4];
+ memcpy(tmp2, &source[4*test], 4*sizeof(float));
+ combined[0]->apply(tmp2, 1);
+
+ for(unsigned int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(tmp2[i], tmp[i], error);
+ }
+ }
+ }
+
+ {
+ OpRcPtrVec ops;
+ CreateMatrixOffsetOp(ops, m1, v1, TRANSFORM_DIR_INVERSE);
+ CreateMatrixOffsetOp(ops, m2, v2, TRANSFORM_DIR_FORWARD);
+ OIIO_CHECK_EQUAL(ops.size(), 2);
+ ops[0]->finalize();
+ ops[1]->finalize();
+
+ OpRcPtrVec combined;
+ ops[0]->combineWith(combined, ops[1]);
+ OIIO_CHECK_EQUAL(combined.size(), 1);
+ combined[0]->finalize();
+
+ for(int test=0; test<3; ++test)
+ {
+ float tmp[4];
+ memcpy(tmp, &source[4*test], 4*sizeof(float));
+ ops[0]->apply(tmp, 1);
+ ops[1]->apply(tmp, 1);
+
+ float tmp2[4];
+ memcpy(tmp2, &source[4*test], 4*sizeof(float));
+ combined[0]->apply(tmp2, 1);
+
+ for(unsigned int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(tmp2[i], tmp[i], error);
+ }
+ }
+ }
+
+ {
+ OpRcPtrVec ops;
+ CreateMatrixOffsetOp(ops, m1, v1, TRANSFORM_DIR_INVERSE);
+ CreateMatrixOffsetOp(ops, m2, v2, TRANSFORM_DIR_INVERSE);
+ OIIO_CHECK_EQUAL(ops.size(), 2);
+ ops[0]->finalize();
+ ops[1]->finalize();
+
+ OpRcPtrVec combined;
+ ops[0]->combineWith(combined, ops[1]);
+ OIIO_CHECK_EQUAL(combined.size(), 1);
+ combined[0]->finalize();
+
+ for(int test=0; test<3; ++test)
+ {
+ float tmp[4];
+ memcpy(tmp, &source[4*test], 4*sizeof(float));
+ ops[0]->apply(tmp, 1);
+ ops[1]->apply(tmp, 1);
+
+ float tmp2[4];
+ memcpy(tmp2, &source[4*test], 4*sizeof(float));
+ combined[0]->apply(tmp2, 1);
+
+ for(unsigned int i=0; i<4; ++i)
+ {
+ OIIO_CHECK_CLOSE(tmp2[i], tmp[i], error);
+ }
+ }
+ }
+}
+
+#endif
diff --git a/src/core/MatrixOps.h b/src/core/MatrixOps.h
new file mode 100644
index 0000000..e462706
--- /dev/null
+++ b/src/core/MatrixOps.h
@@ -0,0 +1,75 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_MATRIXOFFSETOP_H
+#define INCLUDED_OCIO_MATRIXOFFSETOP_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Op.h"
+
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+ // Use whichever is most convenient; they are equally efficient
+
+ void CreateScaleOp(OpRcPtrVec & ops,
+ const float * scale4,
+ TransformDirection direction);
+
+ void CreateMatrixOp(OpRcPtrVec & ops,
+ const float * m44,
+ TransformDirection direction);
+
+ void CreateOffsetOp(OpRcPtrVec & ops,
+ const float * offset4,
+ TransformDirection direction);
+
+ void CreateMatrixOffsetOp(OpRcPtrVec & ops,
+ const float * m44, const float * offset4,
+ TransformDirection direction);
+
+ void CreateScaleOffsetOp(OpRcPtrVec & ops,
+ const float * scale4, const float * offset4,
+ TransformDirection direction);
+
+ void CreateFitOp(OpRcPtrVec & ops,
+ const float * oldmin4, const float * oldmax4,
+ const float * newmin4, const float * newmax4,
+ TransformDirection direction);
+
+ void CreateSaturationOp(OpRcPtrVec & ops,
+ float sat,
+ const float * lumaCoef3,
+ TransformDirection direction);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/MatrixTransform.cpp b/src/core/MatrixTransform.cpp
new file mode 100644
index 0000000..cb88327
--- /dev/null
+++ b/src/core/MatrixTransform.cpp
@@ -0,0 +1,386 @@
+/*
+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 <cstring>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "OpBuilders.h"
+#include "MatrixOps.h"
+#include "MathUtils.h"
+
+
+OCIO_NAMESPACE_ENTER
+{
+ MatrixTransformRcPtr MatrixTransform::Create()
+ {
+ return MatrixTransformRcPtr(new MatrixTransform(), &deleter);
+ }
+
+ void MatrixTransform::deleter(MatrixTransform* t)
+ {
+ delete t;
+ }
+
+ class MatrixTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+ float matrix_[16];
+ float offset_[4];
+
+ Impl() :
+ dir_(TRANSFORM_DIR_FORWARD)
+ {
+ Identity(matrix_, offset_);
+ }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+ memcpy(matrix_, rhs.matrix_, 16*sizeof(float));
+ memcpy(offset_, rhs.offset_, 4*sizeof(float));
+ return *this;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ MatrixTransform::MatrixTransform()
+ : m_impl(new MatrixTransform::Impl)
+ {
+ }
+
+ TransformRcPtr MatrixTransform::createEditableCopy() const
+ {
+ MatrixTransformRcPtr transform = MatrixTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ MatrixTransform::~MatrixTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ MatrixTransform& MatrixTransform::operator= (const MatrixTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection MatrixTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void MatrixTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+ bool MatrixTransform::equals(const MatrixTransform & other) const
+ {
+ const float abserror = 1e-9f;
+
+ for(int i=0; i<16; ++i)
+ {
+ if(!equalWithAbsError(getImpl()->matrix_[i],
+ other.getImpl()->matrix_[i], abserror))
+ {
+ return false;
+ }
+ }
+
+ for(int i=0; i<4; ++i)
+ {
+ if(!equalWithAbsError(getImpl()->offset_[i],
+ other.getImpl()->offset_[i], abserror))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void MatrixTransform::getValue(float * m44, float * offset4) const
+ {
+ if(m44) memcpy(m44, getImpl()->matrix_, 16*sizeof(float));
+ if(offset4) memcpy(offset4, getImpl()->offset_, 4*sizeof(float));
+ }
+
+ void MatrixTransform::setValue(const float * m44, const float * offset4)
+ {
+ if(m44) memcpy(getImpl()->matrix_, m44, 16*sizeof(float));
+ if(offset4) memcpy(getImpl()->offset_, offset4, 4*sizeof(float));
+ }
+
+ void MatrixTransform::setMatrix(const float * m44)
+ {
+ if(m44) memcpy(getImpl()->matrix_, m44, 16*sizeof(float));
+ }
+
+ void MatrixTransform::getMatrix(float * m44) const
+ {
+ if(m44) memcpy(m44, getImpl()->matrix_, 16*sizeof(float));
+ }
+
+ void MatrixTransform::setOffset(const float * offset4)
+ {
+ if(offset4) memcpy(getImpl()->offset_, offset4, 4*sizeof(float));
+ }
+
+ void MatrixTransform::getOffset(float * offset4) const
+ {
+ if(offset4) memcpy(offset4, getImpl()->offset_, 4*sizeof(float));
+ }
+
+ /*
+ Fit is canonically formulated as:
+ out = newmin + ((value-oldmin)/(oldmax-oldmin)*(newmax-newmin))
+ I.e., subtract the old offset, descale into the [0,1] range,
+ scale into the new range, and add the new offset
+
+ We algebraiclly manipulate the terms into y = mx + b form as:
+ m = (newmax-newmin)/(oldmax-oldmin)
+ b = (newmin*oldmax - newmax*oldmin) / (oldmax-oldmin)
+ */
+
+ void MatrixTransform::Fit(float * m44, float * offset4,
+ const float * oldmin4, const float * oldmax4,
+ const float * newmin4, const float * newmax4)
+ {
+ if(!oldmin4 || !oldmax4) return;
+ if(!newmin4 || !newmax4) return;
+
+ if(m44) memset(m44, 0, 16*sizeof(float));
+ if(offset4) memset(offset4, 0, 4*sizeof(float));
+
+ for(int i=0; i<4; ++i)
+ {
+ float denom = oldmax4[i] - oldmin4[i];
+ if(IsScalarEqualToZero(denom))
+ {
+ std::ostringstream os;
+ os << "Cannot create Fit operator. ";
+ os << "Max value equals min value '";
+ os << oldmax4[i] << "' in channel index ";
+ os << i << ".";
+ throw Exception(os.str().c_str());
+ }
+
+ if(m44) m44[5*i] = (newmax4[i]-newmin4[i]) / denom;
+ if(offset4) offset4[i] = (newmin4[i]*oldmax4[i] - newmax4[i]*oldmin4[i]) / denom;
+ }
+ }
+
+
+ void MatrixTransform::Identity(float * m44, float * offset4)
+ {
+ if(m44)
+ {
+ memset(m44, 0, 16*sizeof(float));
+ m44[0] = 1.0f;
+ m44[5] = 1.0f;
+ m44[10] = 1.0f;
+ m44[15] = 1.0f;
+ }
+
+ if(offset4)
+ {
+ offset4[0] = 0.0f;
+ offset4[1] = 0.0f;
+ offset4[2] = 0.0f;
+ offset4[3] = 0.0f;
+ }
+ }
+
+ void MatrixTransform::Sat(float * m44, float * offset4,
+ float sat, const float * lumaCoef3)
+ {
+ if(!lumaCoef3) return;
+
+ if(m44)
+ {
+ m44[0] = (1 - sat) * lumaCoef3[0] + sat;
+ m44[1] = (1 - sat) * lumaCoef3[1];
+ m44[2] = (1 - sat) * lumaCoef3[2];
+ m44[3] = 0.0f;
+
+ m44[4] = (1 - sat) * lumaCoef3[0];
+ m44[5] = (1 - sat) * lumaCoef3[1] + sat;
+ m44[6] = (1 - sat) * lumaCoef3[2];
+ m44[7] = 0.0f;
+
+ m44[8] = (1 - sat) * lumaCoef3[0];
+ m44[9] = (1 - sat) * lumaCoef3[1];
+ m44[10] = (1 - sat) * lumaCoef3[2] + sat;
+ m44[11] = 0.0f;
+
+ m44[12] = 0.0f;
+ m44[13] = 0.0f;
+ m44[14] = 0.0f;
+ m44[15] = 1.0f;
+ }
+
+ if(offset4)
+ {
+ offset4[0] = 0.0f;
+ offset4[1] = 0.0f;
+ offset4[2] = 0.0f;
+ offset4[3] = 0.0f;
+ }
+ }
+
+ void MatrixTransform::Scale(float * m44, float * offset4,
+ const float * scale4)
+ {
+ if(!scale4) return;
+
+ if(m44)
+ {
+ memset(m44, 0, 16*sizeof(float));
+ m44[0] = scale4[0];
+ m44[5] = scale4[1];
+ m44[10] = scale4[2];
+ m44[15] = scale4[3];
+ }
+
+ if(offset4)
+ {
+ offset4[0] = 0.0f;
+ offset4[1] = 0.0f;
+ offset4[2] = 0.0f;
+ offset4[3] = 0.0f;
+ }
+ }
+
+ void MatrixTransform::View(float * m44, float * offset4,
+ int * channelHot4,
+ const float * lumaCoef3)
+ {
+ if(!channelHot4 || !lumaCoef3) return;
+
+ if(offset4)
+ {
+ offset4[0] = 0.0f;
+ offset4[1] = 0.0f;
+ offset4[2] = 0.0f;
+ offset4[3] = 0.0f;
+ }
+
+ if(m44)
+ {
+ memset(m44, 0, 16*sizeof(float));
+
+ // All channels are hot, return identity
+ if(channelHot4[0] && channelHot4[1] &&
+ channelHot4[2] && channelHot4[3])
+ {
+ Identity(m44, 0x0);
+ }
+ // If not all the channels are hot, but alpha is,
+ // just show it.
+ else if(channelHot4[3])
+ {
+ for(int i=0; i<4; ++i)
+ {
+ m44[4*i+3] = 1.0f;
+ }
+ }
+ // Blend rgb as specified, place it in all 3 output
+ // channels (to make a grayscale final image)
+ else
+ {
+ float values[3] = { 0.0f, 0.0f, 0.0f };
+
+ for(int i = 0; i < 3; ++i)
+ {
+ values[i] += lumaCoef3[i] * (channelHot4[i] ? 1.0f : 0.0f);
+ }
+
+ float sum = values[0] + values[1] + values[2];
+ if(!IsScalarEqualToZero(sum))
+ {
+ values[0] /= sum;
+ values[1] /= sum;
+ values[2] /= sum;
+ }
+
+ // Copy rgb into rgb rows
+ for(int row=0; row<3; ++row)
+ {
+ for(int i=0; i<3; i++)
+ {
+ m44[4*row+i] = values[i];
+ }
+ }
+
+ // Preserve alpha
+ m44[15] = 1.0f;
+ }
+ }
+ }
+
+ std::ostream& operator<< (std::ostream& os, const MatrixTransform& t)
+ {
+ os << "<MatrixTransform ";
+ os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
+ os << ">\n";
+ return os;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ void BuildMatrixOps(OpRcPtrVec & ops,
+ const Config& /*config*/,
+ const MatrixTransform & transform,
+ TransformDirection dir)
+ {
+ TransformDirection combinedDir = CombineTransformDirections(dir,
+ transform.getDirection());
+
+ float matrix[16];
+ float offset[4];
+ transform.getValue(matrix, offset);
+
+ CreateMatrixOffsetOp(ops,
+ matrix, offset,
+ combinedDir);
+ }
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/Mutex.h b/src/core/Mutex.h
new file mode 100644
index 0000000..421ad29
--- /dev/null
+++ b/src/core/Mutex.h
@@ -0,0 +1,115 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_MUTEX_H
+#define INCLUDED_OCIO_MUTEX_H
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. 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.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER 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 BASED 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 DAMAGES.
+*/
+
+#include "Platform.h"
+
+// #define DEBUG_THREADING
+
+/** For internal use only */
+
+OCIO_NAMESPACE_ENTER
+{
+
+#ifndef NDEBUG
+ template <class T>
+ class DebugLock : public T {
+ public:
+ DebugLock() : _locked(0) {}
+ void lock() { T::lock(); _locked = 1; }
+ void unlock() { assert(_locked); _locked = 0; T::unlock(); }
+ bool locked() { return _locked != 0; }
+ private:
+ int _locked;
+ };
+#endif
+
+ /** Automatically acquire and release lock within enclosing scope. */
+ template <class T>
+ class AutoLock {
+ public:
+ AutoLock(T& m) : _m(m) { _m.lock(); }
+ ~AutoLock() { _m.unlock(); }
+ private:
+ T& _m;
+ };
+
+#ifndef NDEBUG
+ // add debug wrappers to mutex and spinlock
+ typedef DebugLock<_Mutex> Mutex;
+ typedef DebugLock<_SpinLock> SpinLock;
+#else
+ typedef _Mutex Mutex;
+ typedef _SpinLock SpinLock;
+#endif
+
+ typedef AutoLock<Mutex> AutoMutex;
+ typedef AutoLock<SpinLock> AutoSpin;
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/NoOps.cpp b/src/core/NoOps.cpp
new file mode 100644
index 0000000..101324e
--- /dev/null
+++ b/src/core/NoOps.cpp
@@ -0,0 +1,641 @@
+/*
+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 <sstream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "AllocationOp.h"
+#include "NoOps.h"
+#include "OpBuilders.h"
+#include "Op.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ class AllocationNoOp : public Op
+ {
+ public:
+ AllocationNoOp(const AllocationData & allocationData):
+ m_allocationData(allocationData) {}
+ virtual ~AllocationNoOp() {}
+
+ virtual OpRcPtr clone() const;
+
+ virtual std::string getInfo() const { return "<AllocationNoOp>"; }
+ virtual std::string getCacheID() const { return ""; }
+
+ virtual bool isNoOp() const { return true; }
+ virtual bool isSameType(const OpRcPtr & op) const;
+ virtual bool isInverse(const OpRcPtr & op) const;
+ virtual bool hasChannelCrosstalk() const { return false; }
+ virtual void finalize() { }
+ virtual void apply(float* /*rgbaBuffer*/, long /*numPixels*/) const { }
+
+ virtual bool supportsGpuShader() const { return true; }
+ virtual void writeGpuShader(std::ostream & /*shader*/,
+ const std::string & /*pixelName*/,
+ const GpuShaderDesc & /*shaderDesc*/) const
+ { }
+
+ void getGpuAllocation(AllocationData & allocation) const;
+
+ private:
+ AllocationData m_allocationData;
+ };
+
+ typedef OCIO_SHARED_PTR<AllocationNoOp> AllocationNoOpRcPtr;
+
+ OpRcPtr AllocationNoOp::clone() const
+ {
+ OpRcPtr op = OpRcPtr(new AllocationNoOp(m_allocationData));
+ return op;
+ }
+
+ bool AllocationNoOp::isSameType(const OpRcPtr & op) const
+ {
+ AllocationNoOpRcPtr typedRcPtr = DynamicPtrCast<AllocationNoOp>(op);
+ if(!typedRcPtr) return false;
+ return true;
+ }
+
+ bool AllocationNoOp::isInverse(const OpRcPtr & op) const
+ {
+ if(!isSameType(op)) return false;
+ return true;
+ }
+
+ void AllocationNoOp::getGpuAllocation(AllocationData & allocation) const
+ {
+ allocation = m_allocationData;
+ }
+
+ // Return whether the op defines an Allocation
+ bool DefinesGpuAllocation(const OpRcPtr & op)
+ {
+ AllocationNoOpRcPtr allocationNoOpRcPtr =
+ DynamicPtrCast<AllocationNoOp>(op);
+
+ if(allocationNoOpRcPtr) return true;
+ return false;
+ }
+ }
+
+ void CreateGpuAllocationNoOp(OpRcPtrVec & ops,
+ const AllocationData & allocationData)
+ {
+ ops.push_back( AllocationNoOpRcPtr(new AllocationNoOp(allocationData)) );
+ }
+
+
+ namespace
+ {
+ // Find the minimal index range in the opVec that does not support
+ // shader text generation. The endIndex *is* inclusive.
+ //
+ // I.e., if the entire opVec does not support GPUShaders, the
+ // result will be startIndex = 0, endIndex = opVec.size() - 1
+ //
+ // If the entire opVec supports GPU generation, both the
+ // startIndex and endIndex will equal -1
+
+ void GetGpuUnsupportedIndexRange(int * startIndex, int * endIndex,
+ const OpRcPtrVec & opVec)
+ {
+ int start = -1;
+ int end = -1;
+
+ for(unsigned int i=0; i<opVec.size(); ++i)
+ {
+ // We've found a gpu unsupported op.
+ // If it's the first, save it as our start.
+ // Otherwise, update the end.
+
+ if(!opVec[i]->supportsGpuShader())
+ {
+ if(start<0)
+ {
+ start = i;
+ end = i;
+ }
+ else end = i;
+ }
+ }
+
+ // Now that we've found a startIndex, walk back until we find
+ // one that defines a GpuAllocation. (we can only upload to
+ // the gpu at a location are tagged with an allocation)
+
+ while(start>0)
+ {
+ if(DefinesGpuAllocation(opVec[start])) break;
+ --start;
+ }
+
+ if(startIndex) *startIndex = start;
+ if(endIndex) *endIndex = end;
+ }
+
+
+ bool GetGpuAllocation(AllocationData & allocation,
+ const OpRcPtr & op)
+ {
+ AllocationNoOpRcPtr allocationNoOpRcPtr =
+ DynamicPtrCast<AllocationNoOp>(op);
+
+ if(!allocationNoOpRcPtr)
+ {
+ return false;
+ }
+
+ allocationNoOpRcPtr->getGpuAllocation(allocation);
+ return true;
+ }
+ }
+
+
+ void PartitionGPUOps(OpRcPtrVec & gpuPreOps,
+ OpRcPtrVec & gpuLatticeOps,
+ OpRcPtrVec & gpuPostOps,
+ const OpRcPtrVec & ops)
+ {
+ //
+ // Partition the original, raw opvec into 3 segments for GPU Processing
+ //
+ // gpuLatticeOps need not support analytical gpu shader generation
+ // the pre and post ops must support analytical generation.
+ // Additional ops will be inserted to take into account allocations
+ // transformations.
+
+
+ // This is used to bound our analytical shader text generation
+ // start index and end index are inclusive.
+
+ int gpuLut3DOpStartIndex = 0;
+ int gpuLut3DOpEndIndex = 0;
+ GetGpuUnsupportedIndexRange(&gpuLut3DOpStartIndex,
+ &gpuLut3DOpEndIndex,
+ ops);
+
+ // Write the entire shader using only shader text (3d lut is unused)
+ if(gpuLut3DOpStartIndex == -1 && gpuLut3DOpEndIndex == -1)
+ {
+ for(unsigned int i=0; i<ops.size(); ++i)
+ {
+ gpuPreOps.push_back( ops[i]->clone() );
+ }
+ }
+ // Analytical -> 3dlut -> analytical
+ else
+ {
+ // Handle analytical shader block before start index.
+ for(int i=0; i<gpuLut3DOpStartIndex; ++i)
+ {
+ gpuPreOps.push_back( ops[i]->clone() );
+ }
+
+ // Get the GPU Allocation at the cross-over point
+ // Create 2 symmetrically canceling allocation ops,
+ // where the shader text moves to a nicely allocated LDR
+ // (low dynamic range color space), and the lattice processing
+ // does the inverse (making the overall operation a no-op
+ // color-wise
+
+ AllocationData allocation;
+ if(gpuLut3DOpStartIndex<0 || gpuLut3DOpStartIndex>=(int)ops.size())
+ {
+ std::ostringstream error;
+ error << "Invalid GpuUnsupportedIndexRange: ";
+ error << "gpuLut3DOpStartIndex: " << gpuLut3DOpStartIndex << " ";
+ error << "gpuLut3DOpEndIndex: " << gpuLut3DOpEndIndex << " ";
+ error << "cpuOps.size: " << ops.size();
+ throw Exception(error.str().c_str());
+ }
+
+ // If the specified location defines an allocation, use it.
+ // It's possible that this index wont define an allocation.
+ // (For example in the case of getProcessor(FileTransform)
+ if(GetGpuAllocation(allocation, ops[gpuLut3DOpStartIndex]))
+ {
+ CreateAllocationOps(gpuPreOps, allocation,
+ TRANSFORM_DIR_FORWARD);
+ CreateAllocationOps(gpuLatticeOps, allocation,
+ TRANSFORM_DIR_INVERSE);
+ }
+
+ // Handle cpu lattice processing
+ for(int i=gpuLut3DOpStartIndex; i<=gpuLut3DOpEndIndex; ++i)
+ {
+ gpuLatticeOps.push_back( ops[i]->clone() );
+ }
+
+ // And then handle the gpu post processing
+ for(int i=gpuLut3DOpEndIndex+1; i<(int)ops.size(); ++i)
+ {
+ gpuPostOps.push_back( ops[i]->clone() );
+ }
+ }
+ }
+
+ void AssertPartitionIntegrity(OpRcPtrVec & gpuPreOps,
+ OpRcPtrVec & gpuLatticeOps,
+ OpRcPtrVec & gpuPostOps)
+ {
+ // All gpu pre ops must support analytical gpu shader generation
+ for(unsigned int i=0; i<gpuPreOps.size(); ++i)
+ {
+ if(!gpuPreOps[i]->supportsGpuShader())
+ {
+ throw Exception("Patition failed check. gpuPreOps");
+ }
+ }
+
+ // If there are any lattice ops, at lease one must NOT support GPU
+ // shaders (otherwise this block isnt necessary!)
+ if(gpuLatticeOps.size()>0)
+ {
+ bool requireslattice = false;
+ for(unsigned int i=0; i<gpuLatticeOps.size(); ++i)
+ {
+ if(!gpuLatticeOps[i]->supportsGpuShader()) requireslattice = true;
+ }
+
+ if(!requireslattice)
+ {
+ throw Exception("Patition failed check. gpuLatticeOps");
+ }
+ }
+
+ // All gpu post ops must support analytical gpu shader generation
+ for(unsigned int i=0; i<gpuPostOps.size(); ++i)
+ {
+ if(!gpuPostOps[i]->supportsGpuShader())
+ {
+ throw Exception("Patition failed check. gpuPostOps");
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ namespace
+ {
+ class FileNoOp : public Op
+ {
+ public:
+ FileNoOp(const std::string & fileReference):
+ m_fileReference(fileReference) {}
+ virtual ~FileNoOp() {}
+
+ virtual OpRcPtr clone() const;
+
+ virtual std::string getInfo() const { return "<FileNoOp>"; }
+ virtual std::string getCacheID() const { return ""; }
+
+ virtual bool isNoOp() const { return true; }
+ virtual bool isSameType(const OpRcPtr & op) const;
+ virtual bool isInverse(const OpRcPtr & op) const;
+ virtual bool hasChannelCrosstalk() const { return false; }
+ virtual void dumpMetadata(ProcessorMetadataRcPtr & metadata) const;
+
+ virtual void finalize() {}
+ virtual void apply(float* /*rgbaBuffer*/, long /*numPixels*/) const {}
+
+ virtual bool supportsGpuShader() const { return true; }
+ virtual void writeGpuShader(std::ostream & /*shader*/,
+ const std::string & /*pixelName*/,
+ const GpuShaderDesc & /*shaderDesc*/) const
+ { }
+
+ private:
+ std::string m_fileReference;
+ };
+
+ typedef OCIO_SHARED_PTR<FileNoOp> FileNoOpRcPtr;
+
+ OpRcPtr FileNoOp::clone() const
+ {
+ OpRcPtr op = OpRcPtr(new FileNoOp(m_fileReference));
+ return op;
+ }
+
+ bool FileNoOp::isSameType(const OpRcPtr & op) const
+ {
+ FileNoOpRcPtr typedRcPtr = DynamicPtrCast<FileNoOp>(op);
+ if(!typedRcPtr) return false;
+ return true;
+ }
+
+ bool FileNoOp::isInverse(const OpRcPtr & op) const
+ {
+ return isSameType(op);
+ }
+
+ void FileNoOp::dumpMetadata(ProcessorMetadataRcPtr & metadata) const
+ {
+ metadata->addFile(m_fileReference.c_str());
+ }
+ }
+
+ void CreateFileNoOp(OpRcPtrVec & ops,
+ const std::string & fileReference)
+ {
+ ops.push_back( FileNoOpRcPtr(new FileNoOp(fileReference)) );
+ }
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ namespace
+ {
+ class LookNoOp : public Op
+ {
+ public:
+ LookNoOp(const std::string & look):
+ m_look(look) {}
+ virtual ~LookNoOp() {}
+
+ virtual OpRcPtr clone() const;
+
+ virtual std::string getInfo() const { return "<LookNoOp>"; }
+ virtual std::string getCacheID() const { return ""; }
+
+ virtual bool isNoOp() const { return true; }
+ virtual bool isSameType(const OpRcPtr & op) const;
+ virtual bool isInverse(const OpRcPtr & op) const;
+ virtual bool hasChannelCrosstalk() const { return false; }
+ virtual void dumpMetadata(ProcessorMetadataRcPtr & metadata) const;
+
+ virtual void finalize() {}
+ virtual void apply(float* /*rgbaBuffer*/, long /*numPixels*/) const {}
+
+ virtual bool supportsGpuShader() const { return true; }
+ virtual void writeGpuShader(std::ostream & /*shader*/,
+ const std::string & /*pixelName*/,
+ const GpuShaderDesc & /*shaderDesc*/) const
+ { }
+
+ private:
+ std::string m_look;
+ };
+
+ typedef OCIO_SHARED_PTR<LookNoOp> LookNoOpRcPtr;
+
+ OpRcPtr LookNoOp::clone() const
+ {
+ OpRcPtr op = OpRcPtr(new FileNoOp(m_look));
+ return op;
+ }
+
+ bool LookNoOp::isSameType(const OpRcPtr & op) const
+ {
+ FileNoOpRcPtr typedRcPtr = DynamicPtrCast<FileNoOp>(op);
+ if(!typedRcPtr) return false;
+ return true;
+ }
+
+ bool LookNoOp::isInverse(const OpRcPtr & op) const
+ {
+ return isSameType(op);
+ }
+
+ void LookNoOp::dumpMetadata(ProcessorMetadataRcPtr & metadata) const
+ {
+ metadata->addLook(m_look.c_str());
+ }
+ }
+
+ void CreateLookNoOp(OpRcPtrVec & ops,
+ const std::string & look)
+ {
+ ops.push_back( LookNoOpRcPtr(new LookNoOp(look)) );
+ }
+
+}
+OCIO_NAMESPACE_EXIT
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+OCIO_NAMESPACE_USING
+
+#include "UnitTest.h"
+#include "Lut1DOp.h"
+#include "MatrixOps.h"
+
+void CreateGenericAllocationOp(OpRcPtrVec & ops)
+{
+ AllocationData srcAllocation;
+ srcAllocation.allocation = ALLOCATION_LG2;
+ srcAllocation.vars.push_back(-8.0f);
+ srcAllocation.vars.push_back(8.0f);
+ CreateGpuAllocationNoOp(ops, srcAllocation);
+}
+
+void CreateGenericScaleOp(OpRcPtrVec & ops)
+{
+ float scale4[4] = { 1.04f, 1.05f, 1.06f, 1.0f };
+ CreateScaleOp(ops, scale4, TRANSFORM_DIR_FORWARD);
+}
+
+void CreateGenericLutOp(OpRcPtrVec & ops)
+{
+ // Make a lut that squares the input
+ Lut1DRcPtr lut = Lut1D::Create();
+ {
+ lut->from_min[0] = 0.0f;
+ lut->from_min[1] = 0.0f;
+ lut->from_min[2] = 0.0f;
+ lut->from_max[0] = 1.0f;
+ lut->from_max[1] = 1.0f;
+ lut->from_max[2] = 1.0f;
+ int size = 256;
+ for(int i=0; i<size; ++i)
+ {
+ float x = (float)i / (float)(size-1);
+ float x2 = x*x;
+
+ for(int c=0; c<3; ++c)
+ {
+ lut->luts[c].push_back(x2);
+ }
+ }
+ }
+
+ CreateLut1DOp(ops, lut, INTERP_LINEAR, TRANSFORM_DIR_FORWARD);
+}
+
+OIIO_ADD_TEST(NoOps, PartitionGPUOps)
+{
+ {
+ OpRcPtrVec ops;
+
+ OpRcPtrVec gpuPreOps, gpuLatticeOps, gpuPostOps;
+ PartitionGPUOps(gpuPreOps, gpuLatticeOps, gpuPostOps, ops);
+
+ OIIO_CHECK_EQUAL(gpuPreOps.size(), 0);
+ OIIO_CHECK_EQUAL(gpuLatticeOps.size(), 0);
+ OIIO_CHECK_EQUAL(gpuPostOps.size(), 0);
+
+ OIIO_CHECK_NO_THOW( AssertPartitionIntegrity(gpuPreOps,
+ gpuLatticeOps,
+ gpuPostOps) );
+ }
+
+ {
+ OpRcPtrVec ops;
+ CreateGenericAllocationOp(ops);
+
+ OpRcPtrVec gpuPreOps, gpuLatticeOps, gpuPostOps;
+ PartitionGPUOps(gpuPreOps, gpuLatticeOps, gpuPostOps, ops);
+
+ OIIO_CHECK_EQUAL(gpuPreOps.size(), 1);
+ OIIO_CHECK_EQUAL(gpuLatticeOps.size(), 0);
+ OIIO_CHECK_EQUAL(gpuPostOps.size(), 0);
+
+ OIIO_CHECK_NO_THOW( AssertPartitionIntegrity(gpuPreOps,
+ gpuLatticeOps,
+ gpuPostOps) );
+ }
+
+ {
+ OpRcPtrVec ops;
+
+ CreateGenericAllocationOp(ops);
+ CreateGenericScaleOp(ops);
+
+ OpRcPtrVec gpuPreOps, gpuLatticeOps, gpuPostOps;
+ PartitionGPUOps(gpuPreOps, gpuLatticeOps, gpuPostOps, ops);
+
+ OIIO_CHECK_EQUAL(gpuPreOps.size(), 2);
+ OIIO_CHECK_EQUAL(gpuLatticeOps.size(), 0);
+ OIIO_CHECK_EQUAL(gpuPostOps.size(), 0);
+
+ OIIO_CHECK_NO_THOW( AssertPartitionIntegrity(gpuPreOps,
+ gpuLatticeOps,
+ gpuPostOps) );
+ }
+
+ {
+ OpRcPtrVec ops;
+
+ CreateGenericAllocationOp(ops);
+ CreateGenericLutOp(ops);
+ CreateGenericScaleOp(ops);
+
+ OpRcPtrVec gpuPreOps, gpuLatticeOps, gpuPostOps;
+ PartitionGPUOps(gpuPreOps, gpuLatticeOps, gpuPostOps, ops);
+
+ OIIO_CHECK_EQUAL(gpuPreOps.size(), 2);
+ OIIO_CHECK_EQUAL(gpuLatticeOps.size(), 4);
+ OIIO_CHECK_EQUAL(gpuPostOps.size(), 1);
+
+ OIIO_CHECK_NO_THOW( AssertPartitionIntegrity(gpuPreOps,
+ gpuLatticeOps,
+ gpuPostOps) );
+ }
+
+ {
+ OpRcPtrVec ops;
+
+ CreateGenericLutOp(ops);
+
+ OpRcPtrVec gpuPreOps, gpuLatticeOps, gpuPostOps;
+ PartitionGPUOps(gpuPreOps, gpuLatticeOps, gpuPostOps, ops);
+
+ OIIO_CHECK_EQUAL(gpuPreOps.size(), 0);
+ OIIO_CHECK_EQUAL(gpuLatticeOps.size(), 1);
+ OIIO_CHECK_EQUAL(gpuPostOps.size(), 0);
+
+ OIIO_CHECK_NO_THOW( AssertPartitionIntegrity(gpuPreOps,
+ gpuLatticeOps,
+ gpuPostOps) );
+ }
+
+ {
+ OpRcPtrVec ops;
+
+ CreateGenericLutOp(ops);
+ CreateGenericScaleOp(ops);
+ CreateGenericAllocationOp(ops);
+ CreateGenericLutOp(ops);
+ CreateGenericScaleOp(ops);
+ CreateGenericAllocationOp(ops);
+
+ OpRcPtrVec gpuPreOps, gpuLatticeOps, gpuPostOps;
+ PartitionGPUOps(gpuPreOps, gpuLatticeOps, gpuPostOps, ops);
+
+ OIIO_CHECK_EQUAL(gpuPreOps.size(), 0);
+ OIIO_CHECK_EQUAL(gpuLatticeOps.size(), 4);
+ OIIO_CHECK_EQUAL(gpuPostOps.size(), 2);
+
+ OIIO_CHECK_NO_THOW( AssertPartitionIntegrity(gpuPreOps,
+ gpuLatticeOps,
+ gpuPostOps) );
+ }
+
+ {
+ OpRcPtrVec ops;
+
+ CreateGenericAllocationOp(ops);
+ CreateGenericScaleOp(ops);
+ CreateGenericLutOp(ops);
+ CreateGenericScaleOp(ops);
+ CreateGenericAllocationOp(ops);
+ CreateGenericLutOp(ops);
+ CreateGenericScaleOp(ops);
+ CreateGenericAllocationOp(ops);
+
+ OpRcPtrVec gpuPreOps, gpuLatticeOps, gpuPostOps;
+ PartitionGPUOps(gpuPreOps, gpuLatticeOps, gpuPostOps, ops);
+
+ OIIO_CHECK_EQUAL(gpuPreOps.size(), 2);
+ OIIO_CHECK_EQUAL(gpuLatticeOps.size(), 8);
+ OIIO_CHECK_EQUAL(gpuPostOps.size(), 2);
+
+ OIIO_CHECK_NO_THOW( AssertPartitionIntegrity(gpuPreOps,
+ gpuLatticeOps,
+ gpuPostOps) );
+ /*
+ std::cerr << "gpuPreOps" << std::endl;
+ std::cerr << SerializeOpVec(gpuPreOps, 4) << std::endl;
+ std::cerr << "gpuLatticeOps" << std::endl;
+ std::cerr << SerializeOpVec(gpuLatticeOps, 4) << std::endl;
+ std::cerr << "gpuPostOps" << std::endl;
+ std::cerr << SerializeOpVec(gpuPostOps, 4) << std::endl;
+ */
+ }
+} // PartitionGPUOps
+
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/NoOps.h b/src/core/NoOps.h
new file mode 100644
index 0000000..dcc93e8
--- /dev/null
+++ b/src/core/NoOps.h
@@ -0,0 +1,67 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_GPUALLOCATIONNOOP_H
+#define INCLUDED_OCIO_GPUALLOCATIONNOOP_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Op.h"
+
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+ void CreateGpuAllocationNoOp(OpRcPtrVec & ops,
+ const AllocationData & allocationData);
+
+
+ // Partition an opvec into 3 segments for GPU Processing
+ //
+ // gpuLatticeOps need not support analytical gpu shader generation
+ // the pre and post ops must support analytical generation.
+ //
+ // Additional ops will optinally be inserted to take into account
+ // allocation transformations
+
+ void PartitionGPUOps(OpRcPtrVec & gpuPreOps,
+ OpRcPtrVec & gpuLatticeOps,
+ OpRcPtrVec & gpuPostOps,
+ const OpRcPtrVec & ops);
+
+ void CreateFileNoOp(OpRcPtrVec & ops,
+ const std::string & fname);
+
+ void CreateLookNoOp(OpRcPtrVec & ops,
+ const std::string & lookName);
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/OCIOYaml.cpp b/src/core/OCIOYaml.cpp
new file mode 100644
index 0000000..7089318
--- /dev/null
+++ b/src/core/OCIOYaml.cpp
@@ -0,0 +1,1218 @@
+/*
+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 <cstring>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Logging.h"
+#include "MathUtils.h"
+#include "OCIOYaml.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ ///////////////////////////////////////////////////////////////////////////
+ // Core
+
+ void LogUnknownKeyWarning(const std::string & name, const YAML::Node& tag)
+ {
+ std::string key;
+ tag >> key;
+
+ std::ostringstream os;
+ os << "Unknown key in " << name << ": ";
+ os << "'" << key << "'. (line ";
+ os << (tag.GetMark().line+1) << ", column "; // (yaml line numbers start at 0)
+ os << tag.GetMark().column << ")";
+ LogWarning(os.str());
+ }
+
+ void operator >> (const YAML::Node& node, ColorSpaceRcPtr& cs)
+ {
+ if(node.Tag() != "ColorSpace")
+ return; // not a !<ColorSpace> tag
+
+ std::string key, stringval;
+ bool boolval;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "name")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ cs->setName(stringval.c_str());
+ }
+ else if(key == "description")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ cs->setDescription(stringval.c_str());
+ }
+ else if(key == "family")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ cs->setFamily(stringval.c_str());
+ }
+ else if(key == "equalitygroup")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ cs->setEqualityGroup(stringval.c_str());
+ }
+ else if(key == "bitdepth")
+ {
+ BitDepth ret;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<BitDepth>(ret))
+ cs->setBitDepth(ret);
+ }
+ else if(key == "isdata")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<bool>(boolval))
+ cs->setIsData(boolval);
+ }
+ else if(key == "allocation")
+ {
+ Allocation val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<Allocation>(val))
+ cs->setAllocation(val);
+ }
+ else if(key == "allocationvars")
+ {
+ std::vector<float> val;
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> val;
+ if(!val.empty())
+ {
+ cs->setAllocationVars(static_cast<int>(val.size()), &val[0]);
+ }
+ }
+ }
+ else if(key == "to_reference")
+ {
+ TransformRcPtr val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformRcPtr>(val))
+ cs->setTransform(val, COLORSPACE_DIR_TO_REFERENCE);
+ }
+ else if(key == "from_reference")
+ {
+ TransformRcPtr val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformRcPtr>(val))
+ cs->setTransform(val, COLORSPACE_DIR_FROM_REFERENCE);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ColorSpaceRcPtr cs)
+ {
+ out << YAML::VerbatimTag("ColorSpace");
+ out << YAML::BeginMap;
+
+ out << YAML::Key << "name" << YAML::Value << cs->getName();
+ out << YAML::Key << "family" << YAML::Value << cs->getFamily();
+ out << YAML::Key << "equalitygroup" << YAML::Value << cs->getEqualityGroup();
+ out << YAML::Key << "bitdepth" << YAML::Value << cs->getBitDepth();
+ if(strlen(cs->getDescription()) > 0)
+ {
+ out << YAML::Key << "description";
+ out << YAML::Value << YAML::Literal << cs->getDescription();
+ }
+ out << YAML::Key << "isdata" << YAML::Value << cs->isData();
+
+ out << YAML::Key << "allocation" << YAML::Value << cs->getAllocation();
+ if(cs->getAllocationNumVars() > 0)
+ {
+ std::vector<float> allocationvars(cs->getAllocationNumVars());
+ cs->getAllocationVars(&allocationvars[0]);
+ out << YAML::Key << "allocationvars";
+ out << YAML::Flow << YAML::Value << allocationvars;
+ }
+
+ ConstTransformRcPtr toref = \
+ cs->getTransform(COLORSPACE_DIR_TO_REFERENCE);
+ if(toref)
+ out << YAML::Key << "to_reference" << YAML::Value << toref;
+
+ ConstTransformRcPtr fromref = \
+ cs->getTransform(COLORSPACE_DIR_FROM_REFERENCE);
+ if(fromref)
+ out << YAML::Key << "from_reference" << YAML::Value << fromref;
+
+ out << YAML::EndMap;
+ out << YAML::Newline;
+
+ return out;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ // Look. (not the transform, the top-level class)
+
+ void operator >> (const YAML::Node& node, LookRcPtr& look)
+ {
+ if(node.Tag() != "Look")
+ return;
+
+ std::string key, stringval;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "name")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ look->setName(stringval.c_str());
+ }
+ else if(key == "process_space")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ look->setProcessSpace(stringval.c_str());
+ }
+ else if(key == "transform")
+ {
+ TransformRcPtr val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformRcPtr>(val))
+ look->setTransform(val);
+ }
+ else if(key == "inverse_transform")
+ {
+ TransformRcPtr val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformRcPtr>(val))
+ look->setInverseTransform(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, LookRcPtr look)
+ {
+ out << YAML::VerbatimTag("Look");
+ out << YAML::BeginMap;
+ out << YAML::Key << "name" << YAML::Value << look->getName();
+ out << YAML::Key << "process_space" << YAML::Value << look->getProcessSpace();
+
+ if(look->getTransform())
+ {
+ out << YAML::Key << "transform";
+ out << YAML::Value << look->getTransform();
+ }
+
+ if(look->getInverseTransform())
+ {
+ out << YAML::Key << "inverse_transform";
+ out << YAML::Value << look->getInverseTransform();
+ }
+
+ out << YAML::EndMap;
+ out << YAML::Newline;
+
+ return out;
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ namespace
+ {
+ void EmitBaseTransformKeyValues(YAML::Emitter & out,
+ const ConstTransformRcPtr & t)
+ {
+ if(t->getDirection() != TRANSFORM_DIR_FORWARD)
+ {
+ out << YAML::Key << "direction";
+ out << YAML::Value << YAML::Flow << t->getDirection();
+ }
+ }
+ }
+
+ void operator >> (const YAML::Node& node, TransformRcPtr& t)
+ {
+ if(node.Type() != YAML::NodeType::Map)
+ {
+ std::ostringstream os;
+ os << "Unsupported Transform type encountered: (" << node.Type() << ") in OCIO profile. ";
+ os << "Only Mapping types supported. (line ";
+ os << (node.GetMark().line+1) << ", column "; // (yaml line numbers start at 0)
+ os << node.GetMark().column << ")";
+ throw Exception(os.str().c_str());
+ }
+
+ std::string type = node.Tag();
+
+ if(type == "AllocationTransform") {
+ AllocationTransformRcPtr temp;
+ node.Read<AllocationTransformRcPtr>(temp);
+ t = temp;
+ }
+ else if(type == "CDLTransform") {
+ CDLTransformRcPtr temp;
+ node.Read<CDLTransformRcPtr>(temp);
+ t = temp;
+ }
+ else if(type == "ColorSpaceTransform") {
+ ColorSpaceTransformRcPtr temp;
+ node.Read<ColorSpaceTransformRcPtr>(temp);
+ t = temp;
+ }
+ // TODO: DisplayTransform
+ else if(type == "ExponentTransform") {
+ ExponentTransformRcPtr temp;
+ node.Read<ExponentTransformRcPtr>(temp);
+ t = temp;
+ }
+ else if(type == "FileTransform") {
+ FileTransformRcPtr temp;
+ node.Read<FileTransformRcPtr>(temp);
+ t = temp;
+ }
+ else if(type == "GroupTransform") {
+ GroupTransformRcPtr temp;
+ node.Read<GroupTransformRcPtr>(temp);
+ t = temp;
+ }
+ else if(type == "LogTransform") {
+ LogTransformRcPtr temp;
+ node.Read<LogTransformRcPtr>(temp);
+ t = temp;
+ }
+ else if(type == "LookTransform") {
+ LookTransformRcPtr temp;
+ node.Read<LookTransformRcPtr>(temp);
+ t = temp;
+ }
+ else if(type == "MatrixTransform") {
+ MatrixTransformRcPtr temp;
+ node.Read<MatrixTransformRcPtr>(temp);
+ t = temp;
+ }
+ else if(type == "TruelightTransform") {
+ TruelightTransformRcPtr temp;
+ node.Read<TruelightTransformRcPtr>(temp);
+ t = temp;
+ }
+ else
+ {
+ // TODO: add a new empty (better name?) aka passthru Transform()
+ // which does nothing. This is so upsupported !<tag> types don't
+ // throw an exception. Alternativly this could be caught in the
+ // GroupTransformRcPtr >> operator with some type of
+ // supported_tag() method
+
+ // TODO: consider the forwards-compatibility implication of
+ // throwing an exception. Should this be a warning, instead?
+
+ // t = EmptyTransformRcPtr(new EmptyTransform(), &deleter);
+ std::ostringstream os;
+ os << "Unsupported transform type !<" << type << "> in OCIO profile. ";
+ os << " (line ";
+ os << (node.GetMark().line+1) << ", column "; // (yaml line numbers start at 0)
+ os << node.GetMark().column << ")";
+ throw Exception(os.str().c_str());
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstTransformRcPtr t)
+ {
+ if(ConstAllocationTransformRcPtr Allocation_tran = \
+ DynamicPtrCast<const AllocationTransform>(t))
+ out << Allocation_tran;
+ else if(ConstCDLTransformRcPtr CDL_tran = \
+ DynamicPtrCast<const CDLTransform>(t))
+ out << CDL_tran;
+ else if(ConstColorSpaceTransformRcPtr ColorSpace_tran = \
+ DynamicPtrCast<const ColorSpaceTransform>(t))
+ out << ColorSpace_tran;
+ else if(ConstExponentTransformRcPtr Exponent_tran = \
+ DynamicPtrCast<const ExponentTransform>(t))
+ out << Exponent_tran;
+ else if(ConstFileTransformRcPtr File_tran = \
+ DynamicPtrCast<const FileTransform>(t))
+ out << File_tran;
+ else if(ConstGroupTransformRcPtr Group_tran = \
+ DynamicPtrCast<const GroupTransform>(t))
+ out << Group_tran;
+ else if(ConstLogTransformRcPtr Log_tran = \
+ DynamicPtrCast<const LogTransform>(t))
+ out << Log_tran;
+ else if(ConstLookTransformRcPtr Look_tran = \
+ DynamicPtrCast<const LookTransform>(t))
+ out << Look_tran;
+ else if(ConstMatrixTransformRcPtr Matrix_tran = \
+ DynamicPtrCast<const MatrixTransform>(t))
+ out << Matrix_tran;
+ else if(ConstTruelightTransformRcPtr Truelight_tran = \
+ DynamicPtrCast<const TruelightTransform>(t))
+ out << Truelight_tran;
+ else
+ throw Exception("Unsupported Transform() type for serialization.");
+
+ return out;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Transforms
+
+ void operator >> (const YAML::Node& node, GroupTransformRcPtr& t)
+ {
+ t = GroupTransform::Create();
+
+ std::string key;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "children")
+ {
+ const YAML::Node & children = iter.second();
+ for(unsigned i = 0; i <children.size(); ++i)
+ {
+ TransformRcPtr childTransform;
+ children[i].Read<TransformRcPtr>(childTransform);
+
+ // TODO: consider the forwards-compatibility implication of
+ // throwing an exception. Should this be a warning, instead?
+ if(!childTransform)
+ {
+ throw Exception("Child transform could not be parsed.");
+ }
+
+ t->push_back(childTransform);
+ }
+ }
+ else if(key == "direction")
+ {
+ TransformDirection val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformDirection>(val))
+ t->setDirection(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstGroupTransformRcPtr t)
+ {
+ out << YAML::VerbatimTag("GroupTransform");
+ out << YAML::BeginMap;
+ EmitBaseTransformKeyValues(out, t);
+
+ out << YAML::Key << "children";
+ out << YAML::Value;
+
+ out << YAML::BeginSeq;
+ for(int i = 0; i < t->size(); ++i)
+ {
+ out << t->getTransform(i);
+ }
+ out << YAML::EndSeq;
+
+ out << YAML::EndMap;
+
+ return out;
+ }
+
+
+
+ void operator >> (const YAML::Node& node, FileTransformRcPtr& t)
+ {
+ t = FileTransform::Create();
+
+ std::string key, stringval;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "src")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setSrc(stringval.c_str());
+ }
+ else if(key == "cccid")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setCCCId(stringval.c_str());
+ }
+ else if(key == "interpolation")
+ {
+ Interpolation val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<Interpolation>(val))
+ t->setInterpolation(val);
+ }
+ else if(key == "direction")
+ {
+ TransformDirection val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformDirection>(val))
+ t->setDirection(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstFileTransformRcPtr t)
+ {
+ out << YAML::VerbatimTag("FileTransform");
+ out << YAML::Flow << YAML::BeginMap;
+ out << YAML::Key << "src" << YAML::Value << t->getSrc();
+ const char * cccid = t->getCCCId();
+ if(cccid && *cccid)
+ {
+ out << YAML::Key << "cccid" << YAML::Value << t->getCCCId();
+ }
+ out << YAML::Key << "interpolation";
+ out << YAML::Value << t->getInterpolation();
+
+ EmitBaseTransformKeyValues(out, t);
+ out << YAML::EndMap;
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, ColorSpaceTransformRcPtr& t)
+ {
+ t = ColorSpaceTransform::Create();
+
+ std::string key, stringval;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "src")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setSrc(stringval.c_str());
+ }
+ else if(key == "dst")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setDst(stringval.c_str());
+ }
+ else if(key == "direction")
+ {
+ TransformDirection val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformDirection>(val))
+ t->setDirection(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstColorSpaceTransformRcPtr t)
+ {
+ out << YAML::VerbatimTag("ColorSpaceTransform");
+ out << YAML::Flow << YAML::BeginMap;
+ out << YAML::Key << "src" << YAML::Value << t->getSrc();
+ out << YAML::Key << "dst" << YAML::Value << t->getDst();
+ EmitBaseTransformKeyValues(out, t);
+ out << YAML::EndMap;
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, LookTransformRcPtr& t)
+ {
+ t = LookTransform::Create();
+
+ std::string key, stringval;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "src")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setSrc(stringval.c_str());
+ }
+ else if(key == "dst")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setDst(stringval.c_str());
+ }
+ else if(key == "looks")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setLooks(stringval.c_str());
+ }
+ else if(key == "direction")
+ {
+ TransformDirection val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformDirection>(val))
+ t->setDirection(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstLookTransformRcPtr t)
+ {
+ out << YAML::VerbatimTag("LookTransform");
+ out << YAML::Flow << YAML::BeginMap;
+ out << YAML::Key << "src" << YAML::Value << t->getSrc();
+ out << YAML::Key << "dst" << YAML::Value << t->getDst();
+ out << YAML::Key << "looks" << YAML::Value << t->getLooks();
+ EmitBaseTransformKeyValues(out, t);
+ out << YAML::EndMap;
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, ExponentTransformRcPtr& t)
+ {
+ t = ExponentTransform::Create();
+
+ std::string key;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "value")
+ {
+ std::vector<float> val;
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> val;
+ if(val.size() != 4)
+ {
+ std::ostringstream os;
+ os << "ExponentTransform parse error, value field must be 4 ";
+ os << "floats. Found '" << val.size() << "'.";
+ throw Exception(os.str().c_str());
+ }
+ t->setValue(&val[0]);
+ }
+ }
+ else if(key == "direction")
+ {
+ TransformDirection val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformDirection>(val))
+ t->setDirection(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstExponentTransformRcPtr t)
+ {
+ out << YAML::VerbatimTag("ExponentTransform");
+ out << YAML::Flow << YAML::BeginMap;
+
+ std::vector<float> value(4, 0.0);
+ t->getValue(&value[0]);
+ out << YAML::Key << "value";
+ out << YAML::Value << YAML::Flow << value;
+ EmitBaseTransformKeyValues(out, t);
+ out << YAML::EndMap;
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, LogTransformRcPtr& t)
+ {
+ t = LogTransform::Create();
+
+ std::string key;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "base")
+ {
+ float val = 0.0f;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<float>(val))
+ t->setBase(val);
+ }
+ else if(key == "direction")
+ {
+ TransformDirection val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformDirection>(val))
+ t->setDirection(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstLogTransformRcPtr t)
+ {
+ out << YAML::VerbatimTag("LogTransform");
+ out << YAML::Flow << YAML::BeginMap;
+ out << YAML::Key << "base" << YAML::Value << t->getBase();
+ EmitBaseTransformKeyValues(out, t);
+ out << YAML::EndMap;
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, MatrixTransformRcPtr& t)
+ {
+ t = MatrixTransform::Create();
+
+ std::string key;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "matrix")
+ {
+ std::vector<float> val;
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> val;
+ if(val.size() != 16)
+ {
+ std::ostringstream os;
+ os << "MatrixTransform parse error, matrix field must be 16 ";
+ os << "floats. Found '" << val.size() << "'.";
+ throw Exception(os.str().c_str());
+ }
+ t->setMatrix(&val[0]);
+ }
+ }
+ else if(key == "offset")
+ {
+ std::vector<float> val;
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> val;
+ if(val.size() != 4)
+ {
+ std::ostringstream os;
+ os << "MatrixTransform parse error, offset field must be 4 ";
+ os << "floats. Found '" << val.size() << "'.";
+ throw Exception(os.str().c_str());
+ }
+ t->setOffset(&val[0]);
+ }
+ }
+ else if(key == "direction")
+ {
+ TransformDirection val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformDirection>(val))
+ t->setDirection(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstMatrixTransformRcPtr t)
+ {
+ out << YAML::VerbatimTag("MatrixTransform");
+ out << YAML::Flow << YAML::BeginMap;
+
+ std::vector<float> matrix(16, 0.0);
+ t->getMatrix(&matrix[0]);
+ if(!IsM44Identity(&matrix[0]))
+ {
+ out << YAML::Key << "matrix";
+ out << YAML::Value << YAML::Flow << matrix;
+ }
+
+ std::vector<float> offset(4, 0.0);
+ t->getOffset(&offset[0]);
+ if(!IsVecEqualToZero(&offset[0],4))
+ {
+ out << YAML::Key << "offset";
+ out << YAML::Value << YAML::Flow << offset;
+ }
+
+ EmitBaseTransformKeyValues(out, t);
+ out << YAML::EndMap;
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, CDLTransformRcPtr& t)
+ {
+ t = CDLTransform::Create();
+
+ std::string key;
+ std::vector<float> floatvecval;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "slope")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> floatvecval;
+ if(floatvecval.size() != 3)
+ {
+ std::ostringstream os;
+ os << "CDLTransform parse error, 'slope' field must be 3 ";
+ os << "floats. Found '" << floatvecval.size() << "'.";
+ throw Exception(os.str().c_str());
+ }
+ t->setSlope(&floatvecval[0]);
+ }
+ }
+ else if(key == "offset")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> floatvecval;
+ if(floatvecval.size() != 3)
+ {
+ std::ostringstream os;
+ os << "CDLTransform parse error, 'offset' field must be 3 ";
+ os << "floats. Found '" << floatvecval.size() << "'.";
+ throw Exception(os.str().c_str());
+ }
+ t->setOffset(&floatvecval[0]);
+ }
+ }
+ else if(key == "power")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> floatvecval;
+ if(floatvecval.size() != 3)
+ {
+ std::ostringstream os;
+ os << "CDLTransform parse error, 'power' field must be 3 ";
+ os << "floats. Found '" << floatvecval.size() << "'.";
+ throw Exception(os.str().c_str());
+ }
+ t->setPower(&floatvecval[0]);
+ }
+ }
+ else if(key == "saturation" || key == "sat")
+ {
+ float val = 0.0f;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<float>(val))
+ t->setSat(val);
+ }
+ else if(key == "direction")
+ {
+ TransformDirection val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformDirection>(val))
+ t->setDirection(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstCDLTransformRcPtr t)
+ {
+ out << YAML::VerbatimTag("CDLTransform");
+ out << YAML::Flow << YAML::BeginMap;
+
+ std::vector<float> slope(3);
+ t->getSlope(&slope[0]);
+ if(!IsVecEqualToOne(&slope[0], 3))
+ {
+ out << YAML::Key << "slope";
+ out << YAML::Value << YAML::Flow << slope;
+ }
+
+ std::vector<float> offset(3);
+ t->getOffset(&offset[0]);
+ if(!IsVecEqualToZero(&offset[0], 3))
+ {
+ out << YAML::Key << "offset";
+ out << YAML::Value << YAML::Flow << offset;
+ }
+
+ std::vector<float> power(3);
+ t->getPower(&power[0]);
+ if(!IsVecEqualToOne(&power[0], 3))
+ {
+ out << YAML::Key << "power";
+ out << YAML::Value << YAML::Flow << power;
+ }
+
+ if(!IsScalarEqualToOne(t->getSat()))
+ {
+ out << YAML::Key << "sat" << YAML::Value << t->getSat();
+ }
+
+ EmitBaseTransformKeyValues(out, t);
+ out << YAML::EndMap;
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, AllocationTransformRcPtr& t)
+ {
+ t = AllocationTransform::Create();
+
+ std::string key;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "allocation")
+ {
+ Allocation val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<Allocation>(val))
+ t->setAllocation(val);
+ }
+ else if(key == "vars")
+ {
+ std::vector<float> val;
+ if (iter.second().Type() != YAML::NodeType::Null)
+ {
+ iter.second() >> val;
+ if(!val.empty())
+ {
+ t->setVars(static_cast<int>(val.size()), &val[0]);
+ }
+ }
+ }
+ else if(key == "direction")
+ {
+ TransformDirection val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformDirection>(val))
+ t->setDirection(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstAllocationTransformRcPtr t)
+ {
+ out << YAML::VerbatimTag("AllocationTransform");
+ out << YAML::Flow << YAML::BeginMap;
+
+ out << YAML::Key << "allocation";
+ out << YAML::Value << YAML::Flow << t->getAllocation();
+
+ if(t->getNumVars() > 0)
+ {
+ std::vector<float> vars(t->getNumVars());
+ t->getVars(&vars[0]);
+ out << YAML::Key << "vars";
+ out << YAML::Flow << YAML::Value << vars;
+ }
+
+ EmitBaseTransformKeyValues(out, t);
+ out << YAML::EndMap;
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, TruelightTransformRcPtr& t)
+ {
+ t = TruelightTransform::Create();
+
+ std::string key, stringval;
+
+ for (YAML::Iterator iter = node.begin();
+ iter != node.end();
+ ++iter)
+ {
+ iter.first() >> key;
+
+ if(key == "config_root")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setConfigRoot(stringval.c_str());
+ }
+ else if(key == "profile")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setProfile(stringval.c_str());
+ }
+ else if(key == "camera")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setCamera(stringval.c_str());
+ }
+ else if(key == "input_display")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setInputDisplay(stringval.c_str());
+ }
+ else if(key == "recorder")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setRecorder(stringval.c_str());
+ }
+ else if(key == "print")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setPrint(stringval.c_str());
+ }
+ else if(key == "lamp")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setLamp(stringval.c_str());
+ }
+ else if(key == "output_camera")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setOutputCamera(stringval.c_str());
+ }
+ else if(key == "display")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setDisplay(stringval.c_str());
+ }
+ else if(key == "cube_input")
+ {
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<std::string>(stringval))
+ t->setCubeInput(stringval.c_str());
+ }
+ else if(key == "direction")
+ {
+ TransformDirection val;
+ if (iter.second().Type() != YAML::NodeType::Null &&
+ iter.second().Read<TransformDirection>(val))
+ t->setDirection(val);
+ }
+ else
+ {
+ LogUnknownKeyWarning(node.Tag(), iter.first());
+ }
+ }
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ConstTruelightTransformRcPtr t)
+ {
+
+ out << YAML::VerbatimTag("TruelightTransform");
+ out << YAML::Flow << YAML::BeginMap;
+ if(strcmp(t->getConfigRoot(), "") != 0)
+ {
+ out << YAML::Key << "config_root";
+ out << YAML::Value << YAML::Flow << t->getConfigRoot();
+ }
+ if(strcmp(t->getProfile(), "") != 0)
+ {
+ out << YAML::Key << "profile";
+ out << YAML::Value << YAML::Flow << t->getProfile();
+ }
+ if(strcmp(t->getCamera(), "") != 0)
+ {
+ out << YAML::Key << "camera";
+ out << YAML::Value << YAML::Flow << t->getCamera();
+ }
+ if(strcmp(t->getInputDisplay(), "") != 0)
+ {
+ out << YAML::Key << "input_display";
+ out << YAML::Value << YAML::Flow << t->getInputDisplay();
+ }
+ if(strcmp(t->getRecorder(), "") != 0)
+ {
+ out << YAML::Key << "recorder";
+ out << YAML::Value << YAML::Flow << t->getRecorder();
+ }
+ if(strcmp(t->getPrint(), "") != 0)
+ {
+ out << YAML::Key << "print";
+ out << YAML::Value << YAML::Flow << t->getPrint();
+ }
+ if(strcmp(t->getLamp(), "") != 0)
+ {
+ out << YAML::Key << "lamp";
+ out << YAML::Value << YAML::Flow << t->getLamp();
+ }
+ if(strcmp(t->getOutputCamera(), "") != 0)
+ {
+ out << YAML::Key << "output_camera";
+ out << YAML::Value << YAML::Flow << t->getOutputCamera();
+ }
+ if(strcmp(t->getDisplay(), "") != 0)
+ {
+ out << YAML::Key << "display";
+ out << YAML::Value << YAML::Flow << t->getDisplay();
+ }
+ if(strcmp(t->getCubeInput(), "") != 0)
+ {
+ out << YAML::Key << "cube_input";
+ out << YAML::Value << YAML::Flow << t->getCubeInput();
+ }
+
+ EmitBaseTransformKeyValues(out, t);
+
+ out << YAML::EndMap;
+ return out;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Enums
+
+ YAML::Emitter& operator << (YAML::Emitter& out, BitDepth depth) {
+ out << BitDepthToString(depth);
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, BitDepth& depth) {
+ std::string str;
+ node.Read<std::string>(str);
+ depth = BitDepthFromString(str.c_str());
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, Allocation alloc) {
+ out << AllocationToString(alloc);
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, Allocation& alloc) {
+ std::string str;
+ node.Read<std::string>(str);
+ alloc = AllocationFromString(str.c_str());
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, ColorSpaceDirection dir) {
+ out << ColorSpaceDirectionToString(dir);
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, ColorSpaceDirection& dir) {
+ std::string str;
+ node.Read<std::string>(str);
+ dir = ColorSpaceDirectionFromString(str.c_str());
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, TransformDirection dir) {
+ out << TransformDirectionToString(dir);
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, TransformDirection& dir) {
+ std::string str;
+ node.Read<std::string>(str);
+ dir = TransformDirectionFromString(str.c_str());
+ }
+
+ YAML::Emitter& operator << (YAML::Emitter& out, Interpolation interp) {
+ out << InterpolationToString(interp);
+ return out;
+ }
+
+ void operator >> (const YAML::Node& node, Interpolation& interp) {
+ std::string str;
+ node.Read<std::string>(str);
+ interp = InterpolationFromString(str.c_str());
+ }
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/OCIOYaml.h b/src/core/OCIOYaml.h
new file mode 100644
index 0000000..7104123
--- /dev/null
+++ b/src/core/OCIOYaml.h
@@ -0,0 +1,125 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "Platform.h"
+
+#ifndef WINDOWS
+
+// fwd declare yaml-cpp visibility
+#pragma GCC visibility push(hidden)
+namespace YAML {
+ class Exception;
+ class BadDereference;
+ class RepresentationException;
+ class EmitterException;
+ class ParserException;
+ class InvalidScalar;
+ class KeyNotFound;
+ template <typename T> class TypedKeyNotFound;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::ColorSpace>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::Config>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::Exception>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::GpuShaderDesc>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::ImageDesc>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::Look>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::Processor>;
+
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::Transform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::AllocationTransform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::CDLTransform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::ColorSpaceTransform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::DisplayTransform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::ExponentTransform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::FileTransform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::GroupTransform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::LogTransform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::LookTransform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::MatrixTransform>;
+ template <> class TypedKeyNotFound<OCIO_NAMESPACE::TruelightTransform>;
+}
+#pragma GCC visibility pop
+
+#endif
+
+#include <yaml-cpp/yaml.h>
+
+#ifndef INCLUDED_OCIO_YAML_H
+#define INCLUDED_OCIO_YAML_H
+
+OCIO_NAMESPACE_ENTER
+{
+
+ // Core
+ OCIOHIDDEN void operator >> (const YAML::Node& node, ColorSpaceRcPtr& cs);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ColorSpaceRcPtr cs);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, GroupTransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstGroupTransformRcPtr t);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, TransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstTransformRcPtr t);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, LookRcPtr& cs);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, LookRcPtr cs);
+
+ // Transforms
+ OCIOHIDDEN void operator >> (const YAML::Node& node, AllocationTransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstAllocationTransformRcPtr t);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, CDLTransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstCDLTransformRcPtr t);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, ColorSpaceTransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstColorSpaceTransformRcPtr t);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, ExponentTransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstExponentTransformRcPtr t);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, FileTransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstFileTransformRcPtr t);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, LogTransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstLogTransformRcPtr t);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, LookTransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstLookTransformRcPtr t);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, MatrixTransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstMatrixTransformRcPtr t);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, TruelightTransformRcPtr& t);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ConstTruelightTransformRcPtr t);
+
+ // Enums
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, BitDepth depth);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, BitDepth& depth);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, Allocation alloc);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, Allocation& alloc);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, ColorSpaceDirection dir);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, ColorSpaceDirection& dir);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, TransformDirection dir);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, TransformDirection& dir);
+ OCIOHIDDEN YAML::Emitter& operator << (YAML::Emitter& out, Interpolation iterp);
+ OCIOHIDDEN void operator >> (const YAML::Node& node, Interpolation& iterp);
+
+ void LogUnknownKeyWarning(const std::string & name, const YAML::Node& tag);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif // INCLUDED_OCIO_YAML_H
diff --git a/src/core/Op.cpp b/src/core/Op.cpp
new file mode 100644
index 0000000..e4e9da2
--- /dev/null
+++ b/src/core/Op.cpp
@@ -0,0 +1,126 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "Op.h"
+#include "pystring/pystring.h"
+
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+ Op::~Op()
+ { }
+
+ bool Op::canCombineWith(const OpRcPtr & /*op*/) const
+ {
+ return false;
+ }
+
+ void Op::combineWith(OpRcPtrVec & /*ops*/,
+ const OpRcPtr & /*secondOp*/) const
+ {
+ std::ostringstream os;
+ os << "Op: " << getInfo() << " cannot be combined. ";
+ os << "A type-specific combining function is not defined.";
+ throw Exception(os.str().c_str());
+ }
+
+ std::ostream& operator<< (std::ostream & os, const Op & op)
+ {
+ os << op.getInfo();
+ return os;
+ }
+
+ namespace
+ {
+ const int FLOAT_DECIMALS = 7;
+ }
+
+ std::string AllocationData::getCacheID() const
+ {
+ std::ostringstream os;
+ os.precision(FLOAT_DECIMALS);
+ os << AllocationToString(allocation) << " ";
+
+ for(unsigned int i=0; i<vars.size(); ++i)
+ {
+ os << vars[i] << " ";
+ }
+
+ return os.str();
+ }
+
+ std::ostream& operator<< (std::ostream & os, const AllocationData & allocation)
+ {
+ os << allocation.getCacheID();
+ return os;
+ }
+
+ std::string SerializeOpVec(const OpRcPtrVec & ops, int indent)
+ {
+ std::ostringstream os;
+
+ for(OpRcPtrVec::size_type i = 0, size = ops.size(); i < size; ++i)
+ {
+ os << pystring::mul(" ", indent);
+ os << "Op " << i << ": " << *ops[i] << " ";
+ os << ops[i]->getCacheID() << " supports_gpu:" << ops[i]->supportsGpuShader();
+ os << "\n";
+ }
+
+ return os.str();
+ }
+
+ bool IsOpVecNoOp(const OpRcPtrVec & ops)
+ {
+ for(OpRcPtrVec::size_type i = 0, size = ops.size(); i < size; ++i)
+ {
+ if(!ops[i]->isNoOp()) return false;
+ }
+
+ return true;
+ }
+
+ void FinalizeOpVec(OpRcPtrVec & ops, bool optimize)
+ {
+ // TODO: Add envvar to force disable optimizations
+
+ if(optimize)
+ {
+ OptimizeOpVec(ops);
+ }
+
+ for(OpRcPtrVec::size_type i = 0, size = ops.size(); i < size; ++i)
+ {
+ ops[i]->finalize();
+ }
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/Op.h b/src/core/Op.h
new file mode 100644
index 0000000..831c072
--- /dev/null
+++ b/src/core/Op.h
@@ -0,0 +1,136 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_OP_H
+#define INCLUDED_OCIO_OP_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include <sstream>
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+ struct AllocationData
+ {
+ Allocation allocation;
+ std::vector<float> vars;
+
+ AllocationData():
+ allocation(ALLOCATION_UNIFORM)
+ {};
+
+ std::string getCacheID() const;
+ };
+
+ std::ostream& operator<< (std::ostream&, const AllocationData&);
+
+ class Op;
+ typedef OCIO_SHARED_PTR<Op> OpRcPtr;
+ typedef std::vector<OpRcPtr> OpRcPtrVec;
+
+ std::string SerializeOpVec(const OpRcPtrVec & ops, int indent=0);
+ bool IsOpVecNoOp(const OpRcPtrVec & ops);
+
+ void FinalizeOpVec(OpRcPtrVec & opVec, bool optimize=true);
+
+ void OptimizeOpVec(OpRcPtrVec & result);
+
+ class Op
+ {
+ public:
+ virtual ~Op();
+
+ virtual OpRcPtr clone() const = 0;
+
+ //! Something short, and printable.
+ // The type of stuff you'd want to see in debugging.
+ virtual std::string getInfo() const = 0;
+
+ //! This should yield a string of not unreasonable length.
+ //! It can only be called after finalize()
+ virtual std::string getCacheID() const = 0;
+
+ //! Is the processing a noop? I.e, does apply do nothing.
+ //! (Even no-ops may define Allocation though.)
+ //! This must be implmented in a manner where its valid to call
+ //! *prior* to finalize. (Optimizers may make use of it)
+ virtual bool isNoOp() const = 0;
+
+ virtual bool isSameType(const OpRcPtr & op) const = 0;
+
+ virtual bool isInverse(const OpRcPtr & op) const = 0;
+
+ virtual bool canCombineWith(const OpRcPtr & op) const;
+
+ // Return a vector of result ops, which correspond to
+ // THIS combinedWith secondOp.
+ //
+ // If the result is a noOp, it is valid for the resulting opsVec
+ // to be empty.
+
+ virtual void combineWith(OpRcPtrVec & ops, const OpRcPtr & secondOp) const;
+
+ virtual bool hasChannelCrosstalk() const = 0;
+
+ virtual void dumpMetadata(ProcessorMetadataRcPtr & /*metadata*/) const
+ { }
+
+ // This is called a single time after construction.
+ // Final pre-processing and safety checks should happen here,
+ // rather than in the constructor.
+
+ virtual void finalize() = 0;
+
+ // Render the specified pixels.
+ //
+ // This must be safe to call in a multi-threaded context.
+ // Ops that have mutable data internally, or rely on external
+ // caching, must thus be appropriately mutexed.
+
+ virtual void apply(float* rgbaBuffer, long numPixels) const = 0;
+
+
+ //! Does this op support gpu shader text generation
+ virtual bool supportsGpuShader() const = 0;
+
+ // TODO: If temp variables are ever needed, also pass tempvar prefix.
+ virtual void writeGpuShader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc) const = 0;
+
+ private:
+ Op& operator= (const Op &);
+ };
+
+ std::ostream& operator<< (std::ostream&, const Op&);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/OpBuilders.h b/src/core/OpBuilders.h
new file mode 100644
index 0000000..f981478
--- /dev/null
+++ b/src/core/OpBuilders.h
@@ -0,0 +1,125 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_OPBUILDERS_H
+#define INCLUDED_OCIO_OPBUILDERS_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Op.h"
+#include "LookParse.h"
+#include "PrivateTypes.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ void BuildOps(OpRcPtrVec & ops,
+ const Config & config,
+ const ConstContextRcPtr & context,
+ const ConstTransformRcPtr & transform,
+ TransformDirection dir);
+
+
+ ////////////////////////////////////////////////////////////////////////
+
+ void BuildAllocationOps(OpRcPtrVec & ops,
+ const Config & config,
+ const AllocationTransform & transform,
+ TransformDirection dir);
+
+ void BuildCDLOps(OpRcPtrVec & ops,
+ const Config & config,
+ const CDLTransform & transform,
+ TransformDirection dir);
+
+ void BuildColorSpaceOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const ColorSpaceTransform & transform,
+ TransformDirection dir);
+
+ void BuildColorSpaceOps(OpRcPtrVec & ops,
+ const Config & config,
+ const ConstContextRcPtr & context,
+ const ConstColorSpaceRcPtr & srcColorSpace,
+ const ConstColorSpaceRcPtr & dstColorSpace);
+
+ void BuildDisplayOps(OpRcPtrVec & ops,
+ const Config & config,
+ const ConstContextRcPtr & context,
+ const DisplayTransform & transform,
+ TransformDirection dir);
+
+ void BuildExponentOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ExponentTransform & transform,
+ TransformDirection dir);
+
+ void BuildFileOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const FileTransform & transform,
+ TransformDirection dir);
+
+ void BuildGroupOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const GroupTransform & transform,
+ TransformDirection dir);
+
+ void BuildLogOps(OpRcPtrVec & ops,
+ const Config& config,
+ const LogTransform& transform,
+ TransformDirection dir);
+
+ void BuildLookOps(OpRcPtrVec & ops,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const LookTransform & lookTransform,
+ TransformDirection dir);
+
+ void BuildLookOps(OpRcPtrVec & ops,
+ ConstColorSpaceRcPtr & currentColorSpace,
+ bool skipColorSpaceConversions,
+ const Config& config,
+ const ConstContextRcPtr & context,
+ const LookParseResult & looks);
+
+ void BuildMatrixOps(OpRcPtrVec & ops,
+ const Config& config,
+ const MatrixTransform & transform,
+ TransformDirection dir);
+
+ void BuildTruelightOps(OpRcPtrVec & ops,
+ const Config & config,
+ const TruelightTransform & transform,
+ TransformDirection dir);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/OpOptimizers.cpp b/src/core/OpOptimizers.cpp
new file mode 100644
index 0000000..6e975eb
--- /dev/null
+++ b/src/core/OpOptimizers.cpp
@@ -0,0 +1,364 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "Logging.h"
+#include "Op.h"
+
+#include <iterator>
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ const int MAX_OPTIMIZATION_PASSES = 8;
+
+ int RemoveNoOps(OpRcPtrVec & opVec)
+ {
+ int count = 0;
+
+ OpRcPtrVec::iterator iter = opVec.begin();
+ while(iter != opVec.end())
+ {
+ if((*iter)->isNoOp())
+ {
+ iter = opVec.erase(iter);
+ ++count;
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ return count;
+ }
+
+ int RemoveInverseOps(OpRcPtrVec & opVec)
+ {
+ int count = 0;
+ int firstindex = 0; // this must be a signed int
+
+ while(firstindex < static_cast<int>(opVec.size()-1))
+ {
+ const OpRcPtr & first = opVec[firstindex];
+ const OpRcPtr & second = opVec[firstindex+1];
+
+ // The common case of inverse ops is to have a deep nesting:
+ // ..., A, B, B', A', ...
+ //
+ // Consider the above, when firstindex reaches B:
+ //
+ // |
+ // ..., A, B, B', A', ...
+ //
+ // We will remove B and B'.
+ // Firstindex remains pointing at the original location:
+ //
+ // |
+ // ..., A, A', ...
+ //
+ // We then decrement firstindex by 1,
+ // to backstep and reconsider the A, A' case:
+ //
+ // | <-- firstindex decremented
+ // ..., A, A', ...
+ //
+
+ if(first->isSameType(second) && first->isInverse(second))
+ {
+ opVec.erase(opVec.begin() + firstindex,
+ opVec.begin() + firstindex + 2);
+ ++count;
+
+ firstindex = std::max(0, firstindex-1);
+ }
+ else
+ {
+ ++firstindex;
+ }
+ }
+
+ return count;
+ }
+
+ int CombineOps(OpRcPtrVec & opVec)
+ {
+ int count = 0;
+ int firstindex = 0; // this must be a signed int
+
+ OpRcPtrVec tmpops;
+
+ while(firstindex < static_cast<int>(opVec.size()-1))
+ {
+ const OpRcPtr & first = opVec[firstindex];
+ const OpRcPtr & second = opVec[firstindex+1];
+
+ if(first->canCombineWith(second))
+ {
+ tmpops.clear();
+ first->combineWith(tmpops, second);
+
+ // tmpops may have any number of ops in it. (0, 1, 2, ...)
+ // (size 0 would occur potentially iff the combination
+ // results in a no-op)
+ //
+ // No matter the number, we need to swap them in for the
+ // original ops
+
+ // Erase the initial two ops we've combined
+ opVec.erase(opVec.begin() + firstindex,
+ opVec.begin() + firstindex + 2);
+
+ // Insert the new ops (which may be empty) at
+ // this location
+ std::copy(tmpops.begin(), tmpops.end(),
+ std::inserter(opVec, opVec.begin() + firstindex));
+
+ // Decrement firstindex by 1,
+ // to backstep and reconsider the A, A' case.
+ // See RemoveInverseOps for the full discussion of
+ // why this is appropriate
+ firstindex = std::max(0, firstindex-1);
+
+ // We've done something so increment the count!
+ ++count;
+ }
+ else
+ {
+ ++firstindex;
+ }
+ }
+
+ return count;
+ }
+ }
+
+ void OptimizeOpVec(OpRcPtrVec & ops)
+ {
+ if(ops.empty()) return;
+
+
+ if(IsDebugLoggingEnabled())
+ {
+ LogDebug("Optimizing Op Vec...");
+ LogDebug(SerializeOpVec(ops, 4));
+ }
+
+ OpRcPtrVec::size_type originalSize = ops.size();
+ int total_noops = 0;
+ int total_inverseops = 0;
+ int total_combines = 0;
+ int passes = 0;
+
+ while(passes<=MAX_OPTIMIZATION_PASSES)
+ {
+ int noops = RemoveNoOps(ops);
+ int inverseops = RemoveInverseOps(ops);
+ int combines = CombineOps(ops);
+
+ if(noops == 0 && inverseops==0 && combines==0)
+ {
+ // No optimization progress was made, so stop trying.
+ break;
+ }
+
+ total_noops += noops;
+ total_inverseops += inverseops;
+ total_combines += combines;
+
+ ++passes;
+ }
+
+ OpRcPtrVec::size_type finalSize = ops.size();
+
+ if(passes == MAX_OPTIMIZATION_PASSES)
+ {
+ std::ostringstream os;
+ os << "The max number of passes, " << passes << ", ";
+ os << "was reached during optimization. This is likely a sign ";
+ os << "that either the complexity of the color transform is ";
+ os << "very high, or that some internal optimizers are in conflict ";
+ os << "(undo-ing / redo-ing the other's results).";
+ LogDebug(os.str().c_str());
+ }
+
+ if(IsDebugLoggingEnabled())
+ {
+ std::ostringstream os;
+ os << "Optimized ";
+ os << originalSize << "->" << finalSize << ", ";
+ os << passes << " passes, ";
+ os << total_noops << " noops removed, ";
+ os << total_inverseops << " inverse ops removed\n";
+ os << total_combines << " ops combines\n";
+ os << SerializeOpVec(ops, 4);
+ LogDebug(os.str());
+ }
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+#include "ExponentOps.h"
+#include "LogOps.h"
+#include "Lut1DOp.h"
+#include "Lut3DOp.h"
+#include "MatrixOps.h"
+
+OIIO_ADD_TEST(OpOptimizers, RemoveInverseOps)
+{
+ OCIO::OpRcPtrVec ops;
+
+ float exp[4] = { 1.2f, 1.3f, 1.4f, 1.5f };
+
+
+ float k[3] = { 0.18f, 0.18f, 0.18f };
+ float m[3] = { 2.0f, 2.0f, 2.0f };
+ float b[3] = { 0.1f, 0.1f, 0.1f };
+ float base[3] = { 10.0f, 10.0f, 10.0f };
+ float kb[3] = { 1.0f, 1.0f, 1.0f };
+
+ OCIO::CreateExponentOp(ops, exp, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateLogOp(ops, k, m, b, base, kb, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateLogOp(ops, k, m, b, base, kb, OCIO::TRANSFORM_DIR_INVERSE);
+ OCIO::CreateExponentOp(ops, exp, OCIO::TRANSFORM_DIR_INVERSE);
+
+ OIIO_CHECK_EQUAL(ops.size(), 4);
+ OCIO::RemoveInverseOps(ops);
+ OIIO_CHECK_EQUAL(ops.size(), 0);
+
+
+ ops.clear();
+ OCIO::CreateExponentOp(ops, exp, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateExponentOp(ops, exp, OCIO::TRANSFORM_DIR_INVERSE);
+ OCIO::CreateLogOp(ops, k, m, b, base, kb, OCIO::TRANSFORM_DIR_INVERSE);
+ OCIO::CreateLogOp(ops, k, m, b, base, kb, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateExponentOp(ops, exp, OCIO::TRANSFORM_DIR_FORWARD);
+
+ OIIO_CHECK_EQUAL(ops.size(), 5);
+ OCIO::RemoveInverseOps(ops);
+ OIIO_CHECK_EQUAL(ops.size(), 1);
+}
+
+
+OIIO_ADD_TEST(OpOptimizers, CombineOps)
+{
+ float m1[4] = { 2.0f, 2.0f, 2.0f, 1.0f };
+ float m2[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
+ float m3[4] = { 0.6f, 0.6f, 0.6f, 1.0f };
+ float m4[4] = { 0.7f, 0.7f, 0.7f, 1.0f };
+
+ float exp[4] = { 1.2f, 1.3f, 1.4f, 1.5f };
+
+ {
+ OCIO::OpRcPtrVec ops;
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+
+ OIIO_CHECK_EQUAL(ops.size(), 1);
+ OCIO::CombineOps(ops);
+ OIIO_CHECK_EQUAL(ops.size(), 1);
+ }
+
+ {
+ OCIO::OpRcPtrVec ops;
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m3, OCIO::TRANSFORM_DIR_FORWARD);
+
+ OIIO_CHECK_EQUAL(ops.size(), 2);
+ OCIO::CombineOps(ops);
+ OIIO_CHECK_EQUAL(ops.size(), 1);
+ }
+
+ {
+ OCIO::OpRcPtrVec ops;
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m3, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m4, OCIO::TRANSFORM_DIR_FORWARD);
+
+ OIIO_CHECK_EQUAL(ops.size(), 3);
+ OCIO::CombineOps(ops);
+ OIIO_CHECK_EQUAL(ops.size(), 1);
+ }
+
+ {
+ OCIO::OpRcPtrVec ops;
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m2, OCIO::TRANSFORM_DIR_FORWARD);
+
+ OIIO_CHECK_EQUAL(ops.size(), 2);
+ OCIO::CombineOps(ops);
+ OIIO_CHECK_EQUAL(ops.size(), 0);
+ }
+
+ {
+ OCIO::OpRcPtrVec ops;
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_INVERSE);
+
+ OIIO_CHECK_EQUAL(ops.size(), 2);
+ OCIO::CombineOps(ops);
+ OIIO_CHECK_EQUAL(ops.size(), 0);
+ }
+
+ {
+ OCIO::OpRcPtrVec ops;
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+
+ OIIO_CHECK_EQUAL(ops.size(), 5);
+ OCIO::CombineOps(ops);
+ OIIO_CHECK_EQUAL(ops.size(), 1);
+ }
+
+ {
+ OCIO::OpRcPtrVec ops;
+ OCIO::CreateExponentOp(ops, exp, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m1, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateScaleOp(ops, m2, OCIO::TRANSFORM_DIR_FORWARD);
+ OCIO::CreateExponentOp(ops, exp, OCIO::TRANSFORM_DIR_INVERSE);
+
+ OIIO_CHECK_EQUAL(ops.size(), 4);
+ OCIO::CombineOps(ops);
+ OIIO_CHECK_EQUAL(ops.size(), 0);
+ }
+}
+
+#endif // OCIO_UNIT_TEST
diff --git a/src/core/ParseUtils.cpp b/src/core/ParseUtils.cpp
new file mode 100644
index 0000000..9110161
--- /dev/null
+++ b/src/core/ParseUtils.cpp
@@ -0,0 +1,438 @@
+/*
+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 <iostream>
+#include <set>
+#include <sstream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "ParseUtils.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ const char * BoolToString(bool val)
+ {
+ if(val) return "true";
+ return "false";
+ }
+
+ bool BoolFromString(const char * s)
+ {
+ std::string str = pystring::lower(s);
+ if((str == "true") || (str=="yes")) return true;
+ return false;
+ }
+
+ const char * LoggingLevelToString(LoggingLevel level)
+ {
+ if(level == LOGGING_LEVEL_NONE) return "none";
+ else if(level == LOGGING_LEVEL_WARNING) return "warning";
+ else if(level == LOGGING_LEVEL_INFO) return "info";
+ else if(level == LOGGING_LEVEL_DEBUG) return "debug";
+ return "unknown";
+ }
+
+ LoggingLevel LoggingLevelFromString(const char * s)
+ {
+ std::string str = pystring::lower(s);
+ if(str == "0" || str == "none") return LOGGING_LEVEL_NONE;
+ else if(str == "1" || str == "warning") return LOGGING_LEVEL_WARNING;
+ else if(str == "2" || str == "info") return LOGGING_LEVEL_INFO;
+ else if(str == "3" || str == "debug") return LOGGING_LEVEL_DEBUG;
+ return LOGGING_LEVEL_UNKNOWN;
+ }
+
+ const char * TransformDirectionToString(TransformDirection dir)
+ {
+ if(dir == TRANSFORM_DIR_FORWARD) return "forward";
+ else if(dir == TRANSFORM_DIR_INVERSE) return "inverse";
+ return "unknown";
+ }
+
+ TransformDirection TransformDirectionFromString(const char * s)
+ {
+ std::string str = pystring::lower(s);
+ if(str == "forward") return TRANSFORM_DIR_FORWARD;
+ else if(str == "inverse") return TRANSFORM_DIR_INVERSE;
+ return TRANSFORM_DIR_UNKNOWN;
+ }
+
+ TransformDirection CombineTransformDirections(TransformDirection d1,
+ TransformDirection d2)
+ {
+ // Any unknowns always combine to be unknown.
+ if(d1 == TRANSFORM_DIR_UNKNOWN || d2 == TRANSFORM_DIR_UNKNOWN)
+ return TRANSFORM_DIR_UNKNOWN;
+
+ if(d1 == TRANSFORM_DIR_FORWARD && d2 == TRANSFORM_DIR_FORWARD)
+ return TRANSFORM_DIR_FORWARD;
+
+ if(d1 == TRANSFORM_DIR_INVERSE && d2 == TRANSFORM_DIR_INVERSE)
+ return TRANSFORM_DIR_FORWARD;
+
+ return TRANSFORM_DIR_INVERSE;
+ }
+
+ TransformDirection GetInverseTransformDirection(TransformDirection dir)
+ {
+ if(dir == TRANSFORM_DIR_FORWARD) return TRANSFORM_DIR_INVERSE;
+ else if(dir == TRANSFORM_DIR_INVERSE) return TRANSFORM_DIR_FORWARD;
+ return TRANSFORM_DIR_UNKNOWN;
+ }
+
+ const char * ColorSpaceDirectionToString(ColorSpaceDirection dir)
+ {
+ if(dir == COLORSPACE_DIR_TO_REFERENCE) return "to_reference";
+ else if(dir == COLORSPACE_DIR_FROM_REFERENCE) return "from_reference";
+ return "unknown";
+ }
+
+ ColorSpaceDirection ColorSpaceDirectionFromString(const char * s)
+ {
+ std::string str = pystring::lower(s);
+ if(str == "to_reference") return COLORSPACE_DIR_TO_REFERENCE;
+ else if(str == "from_reference") return COLORSPACE_DIR_FROM_REFERENCE;
+ return COLORSPACE_DIR_UNKNOWN;
+ }
+
+ const char * BitDepthToString(BitDepth bitDepth)
+ {
+ if(bitDepth == BIT_DEPTH_UINT8) return "8ui";
+ else if(bitDepth == BIT_DEPTH_UINT10) return "10ui";
+ else if(bitDepth == BIT_DEPTH_UINT12) return "12ui";
+ else if(bitDepth == BIT_DEPTH_UINT14) return "14ui";
+ else if(bitDepth == BIT_DEPTH_UINT16) return "16ui";
+ else if(bitDepth == BIT_DEPTH_UINT32) return "32ui";
+ else if(bitDepth == BIT_DEPTH_F16) return "16f";
+ else if(bitDepth == BIT_DEPTH_F32) return "32f";
+ return "unknown";
+ }
+
+ BitDepth BitDepthFromString(const char * s)
+ {
+ std::string str = pystring::lower(s);
+ if(str == "8ui") return BIT_DEPTH_UINT8;
+ else if(str == "10ui") return BIT_DEPTH_UINT10;
+ else if(str == "12ui") return BIT_DEPTH_UINT12;
+ else if(str == "14ui") return BIT_DEPTH_UINT14;
+ else if(str == "16ui") return BIT_DEPTH_UINT16;
+ else if(str == "32ui") return BIT_DEPTH_UINT32;
+ else if(str == "16f") return BIT_DEPTH_F16;
+ else if(str == "32f") return BIT_DEPTH_F32;
+ return BIT_DEPTH_UNKNOWN;
+ }
+
+ bool BitDepthIsFloat(BitDepth bitDepth)
+ {
+ if(bitDepth == BIT_DEPTH_F16) return true;
+ else if(bitDepth == BIT_DEPTH_F32) return true;
+ return false;
+ }
+
+ int BitDepthToInt(BitDepth bitDepth)
+ {
+ if(bitDepth == BIT_DEPTH_UINT8) return 8;
+ else if(bitDepth == BIT_DEPTH_UINT10) return 10;
+ else if(bitDepth == BIT_DEPTH_UINT12) return 12;
+ else if(bitDepth == BIT_DEPTH_UINT14) return 14;
+ else if(bitDepth == BIT_DEPTH_UINT16) return 16;
+ else if(bitDepth == BIT_DEPTH_UINT32) return 32;
+
+ return 0;
+ }
+
+ const char * AllocationToString(Allocation alloc)
+ {
+ if(alloc == ALLOCATION_UNIFORM) return "uniform";
+ else if(alloc == ALLOCATION_LG2) return "lg2";
+ return "unknown";
+ }
+
+ Allocation AllocationFromString(const char * s)
+ {
+ std::string str = pystring::lower(s);
+ if(str == "uniform") return ALLOCATION_UNIFORM;
+ else if(str == "lg2") return ALLOCATION_LG2;
+ return ALLOCATION_UNKNOWN;
+ }
+
+ const char * InterpolationToString(Interpolation interp)
+ {
+ if(interp == INTERP_NEAREST) return "nearest";
+ else if(interp == INTERP_LINEAR) return "linear";
+ else if(interp == INTERP_TETRAHEDRAL) return "tetrahedral";
+ else if(interp == INTERP_BEST) return "best";
+ return "unknown";
+ }
+
+ Interpolation InterpolationFromString(const char * s)
+ {
+ std::string str = pystring::lower(s);
+ if(str == "nearest") return INTERP_NEAREST;
+ else if(str == "linear") return INTERP_LINEAR;
+ else if(str == "tetrahedral") return INTERP_TETRAHEDRAL;
+ else if(str == "best") return INTERP_BEST;
+ return INTERP_UNKNOWN;
+ }
+
+ const char * GpuLanguageToString(GpuLanguage language)
+ {
+ if(language == GPU_LANGUAGE_CG) return "cg";
+ else if(language == GPU_LANGUAGE_GLSL_1_0) return "glsl_1.0";
+ else if(language == GPU_LANGUAGE_GLSL_1_3) return "glsl_1.3";
+ return "unknown";
+ }
+
+ GpuLanguage GpuLanguageFromString(const char * s)
+ {
+ std::string str = pystring::lower(s);
+ if(str == "cg") return GPU_LANGUAGE_CG;
+ else if(str == "glsl_1.0") return GPU_LANGUAGE_GLSL_1_0;
+ else if(str == "glsl_1.3") return GPU_LANGUAGE_GLSL_1_3;
+ return GPU_LANGUAGE_UNKNOWN;
+ }
+
+
+ const char * ROLE_DEFAULT = "default";
+ const char * ROLE_REFERENCE = "reference";
+ const char * ROLE_DATA = "data";
+ const char * ROLE_COLOR_PICKING = "color_picking";
+ const char * ROLE_SCENE_LINEAR = "scene_linear";
+ const char * ROLE_COMPOSITING_LOG = "compositing_log";
+ const char * ROLE_COLOR_TIMING = "color_timing";
+ const char * ROLE_TEXTURE_PAINT = "texture_paint";
+ const char * ROLE_MATTE_PAINT = "matte_paint";
+
+ namespace
+ {
+ const int FLOAT_DECIMALS = 7;
+ const int DOUBLE_DECIMALS = 16;
+ }
+
+ std::string FloatToString(float value)
+ {
+ std::ostringstream pretty;
+ pretty.precision(FLOAT_DECIMALS);
+ pretty << value;
+ return pretty.str();
+ }
+
+ std::string FloatVecToString(const float * fval, unsigned int size)
+ {
+ if(size<=0) return "";
+
+ std::ostringstream pretty;
+ pretty.precision(FLOAT_DECIMALS);
+ for(unsigned int i=0; i<size; ++i)
+ {
+ if(i!=0) pretty << " ";
+ pretty << fval[i];
+ }
+
+ return pretty.str();
+ }
+
+ bool StringToFloat(float * fval, const char * str)
+ {
+ if(!str) return false;
+
+ std::istringstream inputStringstream(str);
+ float x;
+ if(!(inputStringstream >> x))
+ {
+ return false;
+ }
+
+ if(fval) *fval = x;
+ return true;
+ }
+
+ bool StringToInt(int * ival, const char * str)
+ {
+ if(!str) return false;
+
+ std::istringstream inputStringstream(str);
+ int x;
+ if(!(inputStringstream >> x))
+ {
+ return false;
+ }
+
+ if(ival) *ival = x;
+ return true;
+ }
+
+
+ std::string DoubleToString(double value)
+ {
+ std::ostringstream pretty;
+ pretty.precision(DOUBLE_DECIMALS);
+ pretty << value;
+ return pretty.str();
+ }
+
+
+ bool StringVecToFloatVec(std::vector<float> &floatArray,
+ const std::vector<std::string> &lineParts)
+ {
+ floatArray.resize(lineParts.size());
+
+ for(unsigned int i=0; i<lineParts.size(); i++)
+ {
+ std::istringstream inputStringstream(lineParts[i]);
+ float x;
+ if(!(inputStringstream >> x))
+ {
+ return false;
+ }
+ floatArray[i] = x;
+ }
+
+ return true;
+ }
+
+
+ bool StringVecToIntVec(std::vector<int> &intArray,
+ const std::vector<std::string> &lineParts)
+ {
+ intArray.resize(lineParts.size());
+
+ for(unsigned int i=0; i<lineParts.size(); i++)
+ {
+ std::istringstream inputStringstream(lineParts[i]);
+ int x;
+ if(!(inputStringstream >> x))
+ {
+ return false;
+ }
+ intArray[i] = x;
+ }
+
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ // read the next non empty line, and store it in 'line'
+ // return 'true' on success
+
+ bool nextline(std::istream &istream, std::string &line)
+ {
+ while ( istream.good() )
+ {
+ std::getline(istream, line);
+ if(!pystring::strip(line).empty())
+ {
+ return true;
+ }
+ }
+
+ line = "";
+ return false;
+ }
+
+
+ bool StrEqualsCaseIgnore(const std::string & a, const std::string & b)
+ {
+ return (pystring::lower(a) == pystring::lower(b));
+ }
+
+ // If a ',' is in the string, split on it
+ // If a ':' is in the string, split on it
+ // Otherwise, assume a single string.
+ // Also, strip whitespace from all parts.
+
+ void SplitStringEnvStyle(std::vector<std::string> & outputvec, const char * str)
+ {
+ if(!str) return;
+
+ std::string s = pystring::strip(str);
+ if(pystring::find(s, ",") > -1)
+ {
+ pystring::split(s, outputvec, ",");
+ }
+ else if(pystring::find(s, ":") > -1)
+ {
+ pystring::split(s, outputvec, ":");
+ }
+ else
+ {
+ outputvec.push_back(s);
+ }
+
+ for(unsigned int i=0; i<outputvec.size(); ++i)
+ {
+ outputvec[i] = pystring::strip(outputvec[i]);
+ }
+ }
+
+ std::string JoinStringEnvStyle(const std::vector<std::string> & outputvec)
+ {
+ return pystring::join(", ", outputvec);
+ }
+
+ // Ordering and capitalization from vec1 is preserved
+ std::vector<std::string> IntersectStringVecsCaseIgnore(const std::vector<std::string> & vec1,
+ const std::vector<std::string> & vec2)
+ {
+ std::vector<std::string> newvec;
+ std::set<std::string> allvalues;
+
+ // Seed the set with all values from vec2
+ for(unsigned int i=0; i<vec2.size(); ++i)
+ {
+ allvalues.insert(pystring::lower(vec2[i]));
+ }
+
+ for(unsigned int i=0; i<vec1.size(); ++i)
+ {
+ std::string key = pystring::lower(vec1[i]);
+ if(allvalues.find(key) != allvalues.end())
+ {
+ newvec.push_back(vec1[i]);
+ }
+ }
+
+ return newvec;
+ }
+
+
+ int FindInStringVecCaseIgnore(const std::vector<std::string> & vec, const std::string & str)
+ {
+ std::string teststr = pystring::lower(str);
+ for(unsigned int i=0; i<vec.size(); ++i)
+ {
+ if(pystring::lower(vec[i]) == teststr) return static_cast<int>(i);
+ }
+
+ return -1;
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/ParseUtils.h b/src/core/ParseUtils.h
new file mode 100644
index 0000000..efc07d4
--- /dev/null
+++ b/src/core/ParseUtils.h
@@ -0,0 +1,89 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_PARSEUTILS_H
+#define INCLUDED_OCIO_PARSEUTILS_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "PrivateTypes.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+
+ std::string FloatToString(float fval);
+ std::string FloatVecToString(const float * fval, unsigned int size);
+
+ std::string DoubleToString(double value);
+
+ bool StringToFloat(float * fval, const char * str);
+ bool StringToInt(int * ival, const char * str);
+
+ bool StringVecToFloatVec(std::vector<float> & floatArray,
+ const std::vector<std::string> & lineParts);
+
+ bool StringVecToIntVec(std::vector<int> & intArray,
+ const std::vector<std::string> & lineParts);
+
+ //////////////////////////////////////////////////////////////////////////
+
+ // read the next non empty line, and store it in 'line'
+ // return 'true' on success
+
+ bool nextline(std::istream &istream, std::string &line);
+
+ bool StrEqualsCaseIgnore(const std::string & a, const std::string & b);
+
+ // If a ',' is in the string, split on it
+ // If a ':' is in the string, split on it
+ // Otherwise, assume a single string.
+ // Also, strip whitespace from all parts.
+
+ void SplitStringEnvStyle(std::vector<std::string> & outputvec, const char * str);
+
+ // Join on ','
+ std::string JoinStringEnvStyle(const std::vector<std::string> & outputvec);
+
+ // Ordering and capitalization from vec1 is preserved
+ std::vector<std::string> IntersectStringVecsCaseIgnore(const std::vector<std::string> & vec1,
+ const std::vector<std::string> & vec2);
+
+ // Find the index of the specified string, ignoring case.
+ // return -1 if not found.
+
+ int FindInStringVecCaseIgnore(const std::vector<std::string> & vec, const std::string & str);
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/PathUtils.cpp b/src/core/PathUtils.cpp
new file mode 100644
index 0000000..a04ecee
--- /dev/null
+++ b/src/core/PathUtils.cpp
@@ -0,0 +1,229 @@
+/*
+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 <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <sstream>
+#include <sys/stat.h>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Mutex.h"
+#include "PathUtils.h"
+#include "Platform.h"
+#include "pystring/pystring.h"
+
+#if !defined(WINDOWS)
+#include <sys/param.h>
+#else
+#include <direct.h>
+#define MAXPATHLEN 4096
+#endif
+
+#if defined(__APPLE__) && !defined(__IPHONE__)
+#include <crt_externs.h> // _NSGetEnviron()
+#elif !defined(WINDOWS)
+#include <unistd.h>
+extern char **environ;
+#endif
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ typedef std::map<std::string, std::string> StringMap;
+
+ StringMap g_fastFileHashCache;
+ Mutex g_fastFileHashCache_mutex;
+
+ std::string ComputeHash(const std::string & filename)
+ {
+ struct stat results;
+ if (stat(filename.c_str(), &results) == 0)
+ {
+ // Treat the mtime + inode as a proxy for the contents
+ std::ostringstream fasthash;
+ fasthash << results.st_ino << ":";
+ fasthash << results.st_mtime;
+ return fasthash.str();
+ }
+
+ return "";
+ }
+ }
+
+ std::string GetFastFileHash(const std::string & filename)
+ {
+ AutoMutex lock(g_fastFileHashCache_mutex);
+
+ StringMap::iterator iter = g_fastFileHashCache.find(filename);
+ if(iter != g_fastFileHashCache.end())
+ {
+ return iter->second;
+ }
+
+ std::string hash = ComputeHash(filename);
+ g_fastFileHashCache[filename] = hash;
+
+ return hash;
+ }
+
+ bool FileExists(const std::string & filename)
+ {
+ std::string hash = GetFastFileHash(filename);
+ return (!hash.empty());
+ }
+
+ void ClearPathCaches()
+ {
+ AutoMutex lock(g_fastFileHashCache_mutex);
+ g_fastFileHashCache.clear();
+ }
+
+ namespace pystring
+ {
+ namespace os
+ {
+ std::string getcwd()
+ {
+#ifdef WINDOWS
+ char path[MAXPATHLEN];
+ _getcwd(path, MAXPATHLEN);
+ return path;
+#else
+ char path[MAXPATHLEN];
+ ::getcwd(path, MAXPATHLEN);
+ return path;
+#endif
+ }
+
+ namespace path
+ {
+ std::string abspath(const std::string & path)
+ {
+ std::string p = path;
+ if(!isabs(p)) p = join(getcwd(), p);
+ return normpath(p);
+ }
+ } // namespace path
+ } // namespace os
+ }
+
+ namespace
+ {
+ inline char** GetEnviron()
+ {
+#if __IPHONE__
+ // TODO: fix this
+ return NULL;
+#elif __APPLE__
+ return (*_NSGetEnviron());
+#else
+ return environ;
+#endif
+ }
+
+ const int MAX_PATH_LENGTH = 4096;
+ }
+
+ void LoadEnvironment(EnvMap & map)
+ {
+ for (char **env = GetEnviron(); *env != NULL; ++env)
+ {
+ // split environment up into std::map[name] = value
+ std::string env_str = (char*)*env;
+ int pos = static_cast<int>(env_str.find_first_of('='));
+ map.insert(
+ EnvMap::value_type(env_str.substr(0, pos),
+ env_str.substr(pos+1, env_str.length()))
+ );
+ }
+ }
+
+ std::string EnvExpand(const std::string & str, const EnvMap & map)
+ {
+ // Early exit if no magic characters are found.
+ if(pystring::find(str, "$") == -1 &&
+ pystring::find(str, "%") == -1) return str;
+
+ std::string orig = str;
+ std::string newstr = str;
+
+ // This walks through the envmap in key order,
+ // from longest to shortest to handle envvars which are
+ // substrings.
+ // ie. '$TEST_$TESTING_$TE' will expand in this order '2 1 3'
+
+ for (EnvMap::const_iterator iter = map.begin();
+ iter != map.end(); ++iter)
+ {
+ newstr = pystring::replace(newstr,
+ ("${"+iter->first+"}"), iter->second);
+ newstr = pystring::replace(newstr,
+ ("$"+iter->first), iter->second);
+ newstr = pystring::replace(newstr,
+ ("%"+iter->first+"%"), iter->second);
+ }
+
+ // recursively call till string doesn't expand anymore
+ if(newstr != orig)
+ {
+ return EnvExpand(newstr, map);
+ }
+
+ return orig;
+ }
+}
+OCIO_NAMESPACE_EXIT
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+OIIO_ADD_TEST(PathUtils, EnvExpand)
+{
+ // build env by hand for unit test
+ OCIO::EnvMap env_map; // = OCIO::GetEnvMap();
+
+ // add some fake env vars so the test runs
+ env_map.insert(OCIO::EnvMap::value_type("TEST1", "foo.bar"));
+ env_map.insert(OCIO::EnvMap::value_type("TEST1NG", "bar.foo"));
+ env_map.insert(OCIO::EnvMap::value_type("FOO_foo.bar", "cheese"));
+
+ //
+ std::string foo = "/a/b/${TEST1}/${TEST1NG}/$TEST1/$TEST1NG/${FOO_${TEST1}}/";
+ std::string foo_result = "/a/b/foo.bar/bar.foo/foo.bar/bar.foo/cheese/";
+ std::string testresult = OCIO::EnvExpand(foo, env_map);
+ OIIO_CHECK_ASSERT( testresult == foo_result );
+}
+
+#endif // OCIO_BUILD_TESTS
diff --git a/src/core/PathUtils.h b/src/core/PathUtils.h
new file mode 100644
index 0000000..649f52c
--- /dev/null
+++ b/src/core/PathUtils.h
@@ -0,0 +1,95 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_PATHUTILS_H
+#define INCLUDED_OCIO_PATHUTILS_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include <map>
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace pystring
+ {
+ namespace os
+ {
+ namespace path
+ {
+ // This is not currently included in pystring, but we need it
+ // So let's define it locally for now
+
+ std::string abspath(const std::string & path);
+ }
+ }
+ }
+
+ // The EnvMap is ordered by the length of the keys (long -> short). This
+ // is so that recursive string expansion will deal with similar prefixed
+ // keys as expected.
+ // ie. '$TEST_$TESTING_$TE' will expand in this order '2 1 3'
+ template <class T>
+ struct EnvMapKey : std::binary_function <T, T, bool>
+ {
+ bool
+ operator() (const T &x, const T &y) const
+ {
+ // If the lengths are unequal, sort by length
+ if(x.length() != y.length())
+ {
+ return (x.length() > y.length());
+ }
+ // Otherwise, use the standard string sort comparison
+ else
+ {
+ return (x<y);
+ }
+ }
+ };
+ typedef std::map< std::string, std::string, EnvMapKey< std::string > > EnvMap;
+
+ // Get map of current env key = value,
+ void LoadEnvironment(EnvMap & map);
+
+ // Expand a string with $VAR, ${VAR} or %VAR% with the keys passed
+ // in the EnvMap.
+ std::string EnvExpand(const std::string & str, const EnvMap & map);
+
+ // Check if a file exists
+ bool FileExists(const std::string & filename);
+
+ // Get a fast hash for a file, without reading all the contents.
+ // Currently, this checks the mtime and the inode number.
+ std::string GetFastFileHash(const std::string & filename);
+
+ void ClearPathCaches();
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/Platform.h b/src/core/Platform.h
new file mode 100644
index 0000000..56d0971
--- /dev/null
+++ b/src/core/Platform.h
@@ -0,0 +1,202 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_PLATFORM_H
+#define INCLUDED_OCIO_PLATFORM_H
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. 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.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER 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 BASED 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 DAMAGES.
+*/
+
+// platform-specific includes
+#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) || defined(_MSC_VER)
+#ifndef WINDOWS
+#define WINDOWS
+#endif
+#define _CRT_NONSTDC_NO_DEPRECATE 1
+#define _CRT_SECURE_NO_DEPRECATE 1
+#define NOMINMAX 1
+
+// windows - defined for both Win32 and Win64
+#include <Windows.h>
+#include <malloc.h>
+#include <io.h>
+#include <tchar.h>
+#include <process.h>
+
+#else
+
+// linux/unix/posix
+#include <stdlib.h>
+#include <alloca.h>
+#include <string.h>
+#include <pthread.h>
+// OS for spinlock
+#ifdef __APPLE__
+#include <libkern/OSAtomic.h>
+#include <sys/types.h>
+#endif
+#endif
+
+// general includes
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+
+// missing functions on Windows
+#ifdef WINDOWS
+#define snprintf sprintf_s
+#define strtok_r strtok_s
+typedef __int64 FilePos;
+#define fseeko _fseeki64
+#define ftello _ftelli64
+
+inline double log2(double x) {
+ return log(x) * 1.4426950408889634;
+}
+
+#else
+typedef off_t FilePos;
+#endif
+
+
+OCIO_NAMESPACE_ENTER
+{
+
+// TODO: Add proper endian detection using architecture / compiler mojo
+// In the meantime, hardcode to x86
+#define OCIO_LITTLE_ENDIAN 1 // This is correct on x86
+
+ /*
+ * Mutex/SpinLock classes
+ */
+
+#ifdef WINDOWS
+
+ class _Mutex {
+ public:
+ _Mutex() { _mutex = CreateMutex(NULL, FALSE, NULL); }
+ ~_Mutex() { CloseHandle(_mutex); }
+ void lock() { WaitForSingleObject(_mutex, INFINITE); }
+ void unlock() { ReleaseMutex(_mutex); }
+ private:
+ HANDLE _mutex;
+ };
+
+ class _SpinLock {
+ public:
+ _SpinLock() { InitializeCriticalSection(&_spinlock); }
+ ~_SpinLock() { DeleteCriticalSection(&_spinlock); }
+ void lock() { EnterCriticalSection(&_spinlock); }
+ void unlock() { LeaveCriticalSection(&_spinlock); }
+ private:
+ CRITICAL_SECTION _spinlock;
+ };
+
+#else
+ // assume linux/unix/posix
+
+ class _Mutex {
+ public:
+ _Mutex() { pthread_mutex_init(&_mutex, 0); }
+ ~_Mutex() { pthread_mutex_destroy(&_mutex); }
+ void lock() { pthread_mutex_lock(&_mutex); }
+ void unlock() { pthread_mutex_unlock(&_mutex); }
+ private:
+ pthread_mutex_t _mutex;
+ };
+
+#if __APPLE__
+ class _SpinLock {
+ public:
+ _SpinLock() { _spinlock = 0; }
+ ~_SpinLock() { }
+ void lock() { OSSpinLockLock(&_spinlock); }
+ void unlock() { OSSpinLockUnlock(&_spinlock); }
+ private:
+ OSSpinLock _spinlock;
+ };
+#elif ANDROID
+ // we don't have access to pthread on andriod so we just make an empty
+ // class that does nothing.
+ class _SpinLock {
+ public:
+ _SpinLock() { }
+ ~_SpinLock() { }
+ void lock() { }
+ void unlock() { }
+ };
+#else
+ class _SpinLock {
+ public:
+ _SpinLock() { pthread_spin_init(&_spinlock, PTHREAD_PROCESS_PRIVATE); }
+ ~_SpinLock() { pthread_spin_destroy(&_spinlock); }
+ void lock() { pthread_spin_lock(&_spinlock); }
+ void unlock() { pthread_spin_unlock(&_spinlock); }
+ private:
+ pthread_spinlock_t _spinlock;
+ };
+#endif // __APPLE__
+#endif // WINDOWS
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif // INCLUDED_OCIO_PLATFORM_H
diff --git a/src/core/PrivateTypes.h b/src/core/PrivateTypes.h
new file mode 100644
index 0000000..4f0b1ee
--- /dev/null
+++ b/src/core/PrivateTypes.h
@@ -0,0 +1,54 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_PRIVATE_TYPES_H
+#define INCLUDED_OCIO_PRIVATE_TYPES_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include <map>
+#include <set>
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+ // Stl types of OCIO classes
+ typedef std::map<std::string, std::string> StringMap;
+ typedef std::vector<std::string> StringVec;
+ typedef std::set<std::string> StringSet;
+
+ typedef std::vector<ConstTransformRcPtr> ConstTransformVec;
+ typedef std::vector<ColorSpaceRcPtr> ColorSpaceVec;
+ typedef std::vector<LookRcPtr> LookVec;
+
+ typedef std::vector<TransformDirection> TransformDirectionVec;
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/Processor.cpp b/src/core/Processor.cpp
new file mode 100644
index 0000000..06b08d2
--- /dev/null
+++ b/src/core/Processor.cpp
@@ -0,0 +1,640 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "AllocationOp.h"
+#include "GpuShaderUtils.h"
+#include "HashUtils.h"
+#include "Logging.h"
+#include "Lut3DOp.h"
+#include "NoOps.h"
+#include "OpBuilders.h"
+#include "Processor.h"
+#include "ScanlineHelper.h"
+
+#include <algorithm>
+#include <cstring>
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+
+
+
+ //////////////////////////////////////////////////////////////////////////
+
+ class ProcessorMetadata::Impl
+ {
+ public:
+ StringSet files;
+ StringVec looks;
+
+ Impl()
+ { }
+
+ ~Impl()
+ { }
+ };
+
+ ProcessorMetadataRcPtr ProcessorMetadata::Create()
+ {
+ return ProcessorMetadataRcPtr(new ProcessorMetadata(), &deleter);
+ }
+
+ ProcessorMetadata::ProcessorMetadata()
+ : m_impl(new ProcessorMetadata::Impl)
+ { }
+
+ ProcessorMetadata::~ProcessorMetadata()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ void ProcessorMetadata::deleter(ProcessorMetadata* c)
+ {
+ delete c;
+ }
+
+ int ProcessorMetadata::getNumFiles() const
+ {
+ return static_cast<int>(getImpl()->files.size());
+ }
+
+ const char * ProcessorMetadata::getFile(int index) const
+ {
+ if(index < 0 ||
+ index >= (static_cast<int>(getImpl()->files.size())))
+ {
+ return "";
+ }
+
+ StringSet::const_iterator iter = getImpl()->files.begin();
+ std::advance( iter, index );
+
+ return iter->c_str();
+ }
+
+ void ProcessorMetadata::addFile(const char * fname)
+ {
+ getImpl()->files.insert(fname);
+ }
+
+
+
+ int ProcessorMetadata::getNumLooks() const
+ {
+ return static_cast<int>(getImpl()->looks.size());
+ }
+
+ const char * ProcessorMetadata::getLook(int index) const
+ {
+ if(index < 0 ||
+ index >= (static_cast<int>(getImpl()->looks.size())))
+ {
+ return "";
+ }
+
+ return getImpl()->looks[index].c_str();
+ }
+
+ void ProcessorMetadata::addLook(const char * look)
+ {
+ getImpl()->looks.push_back(look);
+ }
+
+
+
+ //////////////////////////////////////////////////////////////////////////
+
+
+ ProcessorRcPtr Processor::Create()
+ {
+ return ProcessorRcPtr(new Processor(), &deleter);
+ }
+
+ void Processor::deleter(Processor* c)
+ {
+ delete c;
+ }
+
+ Processor::Processor()
+ : m_impl(new Processor::Impl)
+ {
+ }
+
+ Processor::~Processor()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ bool Processor::isNoOp() const
+ {
+ return getImpl()->isNoOp();
+ }
+
+ bool Processor::hasChannelCrosstalk() const
+ {
+ return getImpl()->hasChannelCrosstalk();
+ }
+
+ ConstProcessorMetadataRcPtr Processor::getMetadata() const
+ {
+ return getImpl()->getMetadata();
+ }
+
+ void Processor::apply(ImageDesc& img) const
+ {
+ getImpl()->apply(img);
+ }
+ void Processor::applyRGB(float * pixel) const
+ {
+ getImpl()->applyRGB(pixel);
+ }
+
+ void Processor::applyRGBA(float * pixel) const
+ {
+ getImpl()->applyRGBA(pixel);
+ }
+
+ const char * Processor::getCpuCacheID() const
+ {
+ return getImpl()->getCpuCacheID();
+ }
+
+ const char * Processor::getGpuShaderText(const GpuShaderDesc & shaderDesc) const
+ {
+ return getImpl()->getGpuShaderText(shaderDesc);
+ }
+
+ const char * Processor::getGpuShaderTextCacheID(const GpuShaderDesc & shaderDesc) const
+ {
+ return getImpl()->getGpuShaderTextCacheID(shaderDesc);
+ }
+
+ void Processor::getGpuLut3D(float* lut3d, const GpuShaderDesc & shaderDesc) const
+ {
+ return getImpl()->getGpuLut3D(lut3d, shaderDesc);
+ }
+
+ const char * Processor::getGpuLut3DCacheID(const GpuShaderDesc & shaderDesc) const
+ {
+ return getImpl()->getGpuLut3DCacheID(shaderDesc);
+ }
+
+
+
+ //////////////////////////////////////////////////////////////////////////
+
+
+
+ namespace
+ {
+ void WriteShaderHeader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc)
+ {
+ if(!shader) return;
+
+ std::string lut3dName = "lut3d";
+
+ shader << "\n// Generated by OpenColorIO\n\n";
+
+ GpuLanguage lang = shaderDesc.getLanguage();
+
+ std::string fcnName = shaderDesc.getFunctionName();
+
+ if(lang == GPU_LANGUAGE_CG)
+ {
+ shader << "half4 " << fcnName << "(in half4 inPixel," << "\n";
+ shader << " const uniform sampler3D " << lut3dName << ") \n";
+ }
+ else if(lang == GPU_LANGUAGE_GLSL_1_0)
+ {
+ shader << "vec4 " << fcnName << "(vec4 inPixel, \n";
+ shader << " sampler3D " << lut3dName << ") \n";
+ }
+ else if(lang == GPU_LANGUAGE_GLSL_1_3)
+ {
+ shader << "vec4 " << fcnName << "(in vec4 inPixel, \n";
+ shader << " const sampler3D " << lut3dName << ") \n";
+ }
+ else throw Exception("Unsupported shader language.");
+
+ shader << "{" << "\n";
+
+ if(lang == GPU_LANGUAGE_CG)
+ {
+ shader << "half4 " << pixelName << " = inPixel; \n";
+ }
+ else if(lang == GPU_LANGUAGE_GLSL_1_0 || lang == GPU_LANGUAGE_GLSL_1_3)
+ {
+ shader << "vec4 " << pixelName << " = inPixel; \n";
+ }
+ else throw Exception("Unsupported shader language.");
+ }
+
+
+ void WriteShaderFooter(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & /*shaderDesc*/)
+ {
+ shader << "return " << pixelName << ";\n";
+ shader << "}" << "\n\n";
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+
+
+ Processor::Impl::Impl():
+ m_metadata(ProcessorMetadata::Create())
+ {
+ }
+
+ Processor::Impl::~Impl()
+ { }
+
+ bool Processor::Impl::isNoOp() const
+ {
+ return IsOpVecNoOp(m_cpuOps);
+ }
+
+ bool Processor::Impl::hasChannelCrosstalk() const
+ {
+ for(OpRcPtrVec::size_type i=0, size = m_cpuOps.size(); i<size; ++i)
+ {
+ if(m_cpuOps[i]->hasChannelCrosstalk()) return true;
+ }
+
+ return false;
+ }
+
+ ConstProcessorMetadataRcPtr Processor::Impl::getMetadata() const
+ {
+ return m_metadata;
+ }
+
+ void Processor::Impl::apply(ImageDesc& img) const
+ {
+ if(m_cpuOps.empty()) return;
+
+ ScanlineHelper scanlineHelper(img);
+ float * rgbaBuffer = 0;
+ long numPixels = 0;
+
+ while(true)
+ {
+ scanlineHelper.prepRGBAScanline(&rgbaBuffer, &numPixels);
+ if(numPixels == 0) break;
+ if(!rgbaBuffer)
+ throw Exception("Cannot apply transform; null image.");
+
+ for(OpRcPtrVec::size_type i=0, size = m_cpuOps.size(); i<size; ++i)
+ {
+ m_cpuOps[i]->apply(rgbaBuffer, numPixels);
+ }
+
+ scanlineHelper.finishRGBAScanline();
+ }
+ }
+
+ void Processor::Impl::applyRGB(float * pixel) const
+ {
+ if(m_cpuOps.empty()) return;
+
+ // We need to allocate a temp array as the pixel must be 4 floats in size
+ // (otherwise, sse loads will potentially fail)
+
+ float rgbaBuffer[4] = { pixel[0], pixel[1], pixel[2], 0.0f };
+
+ for(OpRcPtrVec::size_type i=0, size = m_cpuOps.size(); i<size; ++i)
+ {
+ m_cpuOps[i]->apply(rgbaBuffer, 1);
+ }
+
+ pixel[0] = rgbaBuffer[0];
+ pixel[1] = rgbaBuffer[1];
+ pixel[2] = rgbaBuffer[2];
+ }
+
+ void Processor::Impl::applyRGBA(float * pixel) const
+ {
+ for(OpRcPtrVec::size_type i=0, size = m_cpuOps.size(); i<size; ++i)
+ {
+ m_cpuOps[i]->apply(pixel, 1);
+ }
+ }
+
+ const char * Processor::Impl::getCpuCacheID() const
+ {
+ AutoMutex lock(m_resultsCacheMutex);
+
+ if(!m_cpuCacheID.empty()) return m_cpuCacheID.c_str();
+
+ if(m_cpuOps.empty())
+ {
+ m_cpuCacheID = "<NOOP>";
+ }
+ else
+ {
+ std::ostringstream cacheid;
+ for(OpRcPtrVec::size_type i=0, size = m_cpuOps.size(); i<size; ++i)
+ {
+ cacheid << m_cpuOps[i]->getCacheID() << " ";
+ }
+ std::string fullstr = cacheid.str();
+
+ m_cpuCacheID = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
+ }
+
+ return m_cpuCacheID.c_str();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+
+ const char * Processor::Impl::getGpuShaderText(const GpuShaderDesc & shaderDesc) const
+ {
+ AutoMutex lock(m_resultsCacheMutex);
+
+ if(m_lastShaderDesc != shaderDesc.getCacheID())
+ {
+ m_lastShaderDesc = shaderDesc.getCacheID();
+ m_shader = "";
+ m_shaderCacheID = "";
+ m_lut3D.clear();
+ m_lut3DCacheID = "";
+ }
+
+ if(m_shader.empty())
+ {
+ std::ostringstream shader;
+ calcGpuShaderText(shader, shaderDesc);
+ m_shader = shader.str();
+
+ if(IsDebugLoggingEnabled())
+ {
+ LogDebug("GPU Shader");
+ LogDebug(m_shader);
+ }
+ }
+
+ return m_shader.c_str();
+ }
+
+ const char * Processor::Impl::getGpuShaderTextCacheID(const GpuShaderDesc & shaderDesc) const
+ {
+ AutoMutex lock(m_resultsCacheMutex);
+
+ if(m_lastShaderDesc != shaderDesc.getCacheID())
+ {
+ m_lastShaderDesc = shaderDesc.getCacheID();
+ m_shader = "";
+ m_shaderCacheID = "";
+ m_lut3D.clear();
+ m_lut3DCacheID = "";
+ }
+
+ if(m_shader.empty())
+ {
+ std::ostringstream shader;
+ calcGpuShaderText(shader, shaderDesc);
+ m_shader = shader.str();
+ }
+
+ if(m_shaderCacheID.empty())
+ {
+ m_shaderCacheID = CacheIDHash(m_shader.c_str(), (int)m_shader.size());
+ }
+
+ return m_shaderCacheID.c_str();
+ }
+
+
+ const char * Processor::Impl::getGpuLut3DCacheID(const GpuShaderDesc & shaderDesc) const
+ {
+ AutoMutex lock(m_resultsCacheMutex);
+
+ if(m_lastShaderDesc != shaderDesc.getCacheID())
+ {
+ m_lastShaderDesc = shaderDesc.getCacheID();
+ m_shader = "";
+ m_shaderCacheID = "";
+ m_lut3D.clear();
+ m_lut3DCacheID = "";
+ }
+
+ if(m_lut3DCacheID.empty())
+ {
+ if(m_gpuOpsCpuLatticeProcess.empty())
+ {
+ m_lut3DCacheID = "<NULL>";
+ }
+ else
+ {
+ std::ostringstream cacheid;
+ for(OpRcPtrVec::size_type i=0, size = m_gpuOpsCpuLatticeProcess.size(); i<size; ++i)
+ {
+ cacheid << m_gpuOpsCpuLatticeProcess[i]->getCacheID() << " ";
+ }
+ // Also, add a hash of the shader description
+ cacheid << shaderDesc.getCacheID();
+ std::string fullstr = cacheid.str();
+ m_lut3DCacheID = CacheIDHash(fullstr.c_str(), (int)fullstr.size());
+ }
+ }
+
+ return m_lut3DCacheID.c_str();
+ }
+
+ void Processor::Impl::getGpuLut3D(float* lut3d, const GpuShaderDesc & shaderDesc) const
+ {
+ if(!lut3d) return;
+
+ AutoMutex lock(m_resultsCacheMutex);
+
+ if(m_lastShaderDesc != shaderDesc.getCacheID())
+ {
+ m_lastShaderDesc = shaderDesc.getCacheID();
+ m_shader = "";
+ m_shaderCacheID = "";
+ m_lut3D.clear();
+ m_lut3DCacheID = "";
+ }
+
+ int lut3DEdgeLen = shaderDesc.getLut3DEdgeLen();
+ int lut3DNumPixels = lut3DEdgeLen*lut3DEdgeLen*lut3DEdgeLen;
+
+ // Can we write the entire shader using only shader text?
+ // If so, the lut3D is not needed so clear it.
+ // This is preferable to identity, as it lets people notice if
+ // it's accidentally being used.
+ if(m_gpuOpsCpuLatticeProcess.empty())
+ {
+ memset(lut3d, 0, sizeof(float) * 3 * lut3DNumPixels);
+ return;
+ }
+
+ if(m_lut3D.empty())
+ {
+ // Allocate 3dlut image, RGBA
+ m_lut3D.resize(lut3DNumPixels*4);
+ GenerateIdentityLut3D(&m_lut3D[0], lut3DEdgeLen, 4, LUT3DORDER_FAST_RED);
+
+ // Apply the lattice ops to it
+ for(int i=0; i<(int)m_gpuOpsCpuLatticeProcess.size(); ++i)
+ {
+ m_gpuOpsCpuLatticeProcess[i]->apply(&m_lut3D[0], lut3DNumPixels);
+ }
+
+ // Convert the RGBA image to an RGB image, in place.
+ // Of course, this only works because we're doing it from left to right
+ // so old pixels are read before they're written over
+ // TODO: is this bad for memory access patterns?
+ // see if this is faster with a 2nd temp float array
+
+ for(int i=1; i<lut3DNumPixels; ++i) // skip the 1st pixel, it's ok.
+ {
+ m_lut3D[3*i+0] = m_lut3D[4*i+0];
+ m_lut3D[3*i+1] = m_lut3D[4*i+1];
+ m_lut3D[3*i+2] = m_lut3D[4*i+2];
+ }
+ }
+
+ // Copy to the destination
+ memcpy(lut3d, &m_lut3D[0], sizeof(float) * 3 * lut3DNumPixels);
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+
+
+ void Processor::Impl::addColorSpaceConversion(const Config & config,
+ const ConstContextRcPtr & context,
+ const ConstColorSpaceRcPtr & srcColorSpace,
+ const ConstColorSpaceRcPtr & dstColorSpace)
+ {
+ BuildColorSpaceOps(m_cpuOps, config, context, srcColorSpace, dstColorSpace);
+ }
+
+
+ void Processor::Impl::addTransform(const Config & config,
+ const ConstContextRcPtr & context,
+ const ConstTransformRcPtr& transform,
+ TransformDirection direction)
+ {
+ BuildOps(m_cpuOps, config, context, transform, direction);
+ }
+
+ void Processor::Impl::finalize()
+ {
+ // Pull out metadata, before the no-ops are removed.
+ for(unsigned int i=0; i<m_cpuOps.size(); ++i)
+ {
+ m_cpuOps[i]->dumpMetadata(m_metadata);
+ }
+
+ // GPU Process setup
+ //
+ // Partition the original, raw opvec into 3 segments for GPU Processing
+ //
+ // Interior index range does not support the gpu shader.
+ // This is used to bound our analytical shader text generation
+ // start index and end index are inclusive.
+
+ PartitionGPUOps(m_gpuOpsHwPreProcess,
+ m_gpuOpsCpuLatticeProcess,
+ m_gpuOpsHwPostProcess,
+ m_cpuOps);
+
+ LogDebug("GPU Ops: Pre-3DLUT");
+ FinalizeOpVec(m_gpuOpsHwPreProcess);
+
+ LogDebug("GPU Ops: 3DLUT");
+ FinalizeOpVec(m_gpuOpsCpuLatticeProcess);
+
+ LogDebug("GPU Ops: Post-3DLUT");
+ FinalizeOpVec(m_gpuOpsHwPostProcess);
+
+ LogDebug("CPU Ops");
+ FinalizeOpVec(m_cpuOps);
+ }
+
+ void Processor::Impl::calcGpuShaderText(std::ostream & shader,
+ const GpuShaderDesc & shaderDesc) const
+ {
+ std::string pixelName = "out_pixel";
+ std::string lut3dName = "lut3d";
+
+ WriteShaderHeader(shader, pixelName, shaderDesc);
+
+
+ for(unsigned int i=0; i<m_gpuOpsHwPreProcess.size(); ++i)
+ {
+ m_gpuOpsHwPreProcess[i]->writeGpuShader(shader, pixelName, shaderDesc);
+ }
+
+ if(!m_gpuOpsCpuLatticeProcess.empty())
+ {
+ // Sample the 3D LUT.
+ int lut3DEdgeLen = shaderDesc.getLut3DEdgeLen();
+ shader << pixelName << ".rgb = ";
+ Write_sampleLut3D_rgb(shader, pixelName,
+ lut3dName, lut3DEdgeLen,
+ shaderDesc.getLanguage());
+ }
+#ifdef __APPLE__
+ else
+ {
+ // Force a no-op sampling of the 3d lut on OSX to work around a segfault.
+ int lut3DEdgeLen = shaderDesc.getLut3DEdgeLen();
+ shader << "// OSX segfault work-around: Force a no-op sampling of the 3d lut.\n";
+ Write_sampleLut3D_rgb(shader, pixelName,
+ lut3dName, lut3DEdgeLen,
+ shaderDesc.getLanguage());
+ }
+#endif // __APPLE__
+ for(unsigned int i=0; i<m_gpuOpsHwPostProcess.size(); ++i)
+ {
+ m_gpuOpsHwPostProcess[i]->writeGpuShader(shader, pixelName, shaderDesc);
+ }
+
+ WriteShaderFooter(shader, pixelName, shaderDesc);
+ }
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/Processor.h b/src/core/Processor.h
new file mode 100644
index 0000000..c0bdb51
--- /dev/null
+++ b/src/core/Processor.h
@@ -0,0 +1,132 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_PROCESSOR_H
+#define INCLUDED_OCIO_PROCESSOR_H
+
+#include <sstream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Mutex.h"
+#include "Op.h"
+#include "PrivateTypes.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ class Processor::Impl
+ {
+ private:
+ ProcessorMetadataRcPtr m_metadata;
+
+ OpRcPtrVec m_cpuOps;
+
+ // These 3 op vecs represent the 3 stages in our gpu pipe.
+ // 1) preprocess shader text
+ // 2) 3d lut process lookup
+ // 3) postprocess shader text
+
+ OpRcPtrVec m_gpuOpsHwPreProcess;
+ OpRcPtrVec m_gpuOpsCpuLatticeProcess;
+ OpRcPtrVec m_gpuOpsHwPostProcess;
+
+ mutable std::string m_cpuCacheID;
+
+ // Cache the last last queried value,
+ // for the specified shader description
+ mutable std::string m_lastShaderDesc;
+ mutable std::string m_shader;
+ mutable std::string m_shaderCacheID;
+ mutable std::vector<float> m_lut3D;
+ mutable std::string m_lut3DCacheID;
+
+ mutable Mutex m_resultsCacheMutex;
+
+ public:
+ Impl();
+ ~Impl();
+
+ bool isNoOp() const;
+ bool hasChannelCrosstalk() const;
+
+ ConstProcessorMetadataRcPtr getMetadata() const;
+
+ void apply(ImageDesc& img) const;
+
+ void applyRGB(float * pixel) const;
+ void applyRGBA(float * pixel) const;
+ const char * getCpuCacheID() const;
+
+ const char * getGpuShaderText(const GpuShaderDesc & gpuDesc) const;
+ const char * getGpuShaderTextCacheID(const GpuShaderDesc & shaderDesc) const;
+
+ void getGpuLut3D(float* lut3d, const GpuShaderDesc & shaderDesc) const;
+ const char * getGpuLut3DCacheID(const GpuShaderDesc & shaderDesc) const;
+
+ ////////////////////////////////////////////
+ //
+ // Builder functions, Not exposed
+
+ void addColorSpaceConversion(const Config & config,
+ const ConstContextRcPtr & context,
+ const ConstColorSpaceRcPtr & srcColorSpace,
+ const ConstColorSpaceRcPtr & dstColorSpace);
+
+ void addTransform(const Config & config,
+ const ConstContextRcPtr & context,
+ const ConstTransformRcPtr& transform,
+ TransformDirection direction);
+
+ void finalize();
+
+ void calcGpuShaderText(std::ostream & shader,
+ const GpuShaderDesc & shaderDesc) const;
+
+ };
+
+ // TODO: Move these!
+ // TODO: Its not ideal that buildops requires a config to be passed around
+ // but the only alternative is to make build ops a function on it?
+ // and even if it were, what about the build calls it dispatches to...
+
+ // TODO: all of the build op functions shouldnt take a LocalProcessor class
+ // Instead, they should take an abstract interface class that defines
+ // registerOp(OpRcPtr op), annotateColorSpace, finalizeOps, etc.
+ // of which LocalProcessor happens to be one example.
+ // Then the only location in the codebase that knows of LocalProcessor is
+ // in Config.cpp, which creates one.
+
+ void BuildOps(OpRcPtrVec & ops,
+ const Config & config,
+ const ConstTransformRcPtr & transform,
+ TransformDirection dir);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/SSE.h b/src/core/SSE.h
new file mode 100644
index 0000000..5b825e2
--- /dev/null
+++ b/src/core/SSE.h
@@ -0,0 +1,37 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_SSE_H
+#define INCLUDED_OCIO_SSE_H
+
+#ifdef USE_SSE
+#include <xmmintrin.h>
+#endif
+
+#endif
diff --git a/src/core/ScanlineHelper.cpp b/src/core/ScanlineHelper.cpp
new file mode 100644
index 0000000..b090ae7
--- /dev/null
+++ b/src/core/ScanlineHelper.cpp
@@ -0,0 +1,123 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+#include "ScanlineHelper.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ const int PIXELS_PER_LINE = 4096;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ ScanlineHelper::ScanlineHelper(ImageDesc& img):
+ m_buffer(0),
+ m_imagePixelIndex(0),
+ m_numPixelsCopied(0),
+ m_yIndex(0),
+ m_inPlaceMode(false)
+ {
+ m_img.init(img);
+
+ if(m_img.isPackedRGBA())
+ {
+ m_inPlaceMode = true;
+ }
+ else
+ {
+ // TODO: Re-use memory from thread-safe memory pool, rather
+ // than doing a new allocation each time.
+
+ m_buffer = (float*)malloc(sizeof(float)*PIXELS_PER_LINE*4);
+ }
+ }
+
+ ScanlineHelper::~ScanlineHelper()
+ {
+ free(m_buffer);
+ }
+
+ // Copy from the src image to our scanline, in our preferred
+ // pixel layout.
+
+ void ScanlineHelper::prepRGBAScanline(float** buffer, long* numPixels)
+ {
+ if(m_inPlaceMode)
+ {
+ // TODO: what if scanline is too short, or too long?
+ if(m_yIndex >= m_img.height)
+ {
+ *numPixels = 0;
+ return;
+ }
+
+ char* rowPtr = reinterpret_cast<char*>(m_img.rData);
+ rowPtr += m_img.yStrideBytes*m_yIndex;
+
+ *buffer = reinterpret_cast<float*>(rowPtr);
+ *numPixels = m_img.width;
+ }
+ else
+ {
+ PackRGBAFromImageDesc(m_img, m_buffer,
+ &m_numPixelsCopied,
+ PIXELS_PER_LINE,
+ m_imagePixelIndex);
+ *buffer = m_buffer;
+ *numPixels = m_numPixelsCopied;
+ }
+ }
+
+ // Write back the result of our work, from the scanline to our
+ // destination image.
+
+ void ScanlineHelper::finishRGBAScanline()
+ {
+ if(m_inPlaceMode)
+ {
+ m_yIndex += 1;
+ }
+ else
+ {
+ UnpackRGBAToImageDesc(m_img,
+ m_buffer,
+ m_numPixelsCopied,
+ m_imagePixelIndex);
+ m_imagePixelIndex += m_numPixelsCopied;
+ }
+ }
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/ScanlineHelper.h b/src/core/ScanlineHelper.h
new file mode 100644
index 0000000..016f1d9
--- /dev/null
+++ b/src/core/ScanlineHelper.h
@@ -0,0 +1,78 @@
+/*
+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.
+*/
+
+
+#ifndef INCLUDED_OCIO_SCANLINEHELPER_H
+#define INCLUDED_OCIO_SCANLINEHELPER_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "ImagePacking.h"
+
+OCIO_NAMESPACE_ENTER
+{
+
+ class ScanlineHelper
+ {
+ public:
+
+ ScanlineHelper(ImageDesc& img);
+
+ ~ScanlineHelper();
+
+ // Copy from the src image to our scanline, in our preferred
+ // pixel layout. Return the number of pixels to process;
+
+ void prepRGBAScanline(float** buffer, long* numPixels);
+
+ // Write back the result of our work, from the scanline to our
+ // destination image.
+
+ void finishRGBAScanline();
+
+ private:
+ GenericImageDesc m_img;
+
+ // Copy mode
+ float* m_buffer;
+ long m_imagePixelIndex;
+ int m_numPixelsCopied;
+
+ // In place mode
+ int m_yIndex;
+
+ bool m_inPlaceMode;
+
+ ScanlineHelper(const ScanlineHelper &);
+ ScanlineHelper& operator= (const ScanlineHelper &);
+ };
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
diff --git a/src/core/Transform.cpp b/src/core/Transform.cpp
new file mode 100644
index 0000000..2f4bf7f
--- /dev/null
+++ b/src/core/Transform.cpp
@@ -0,0 +1,175 @@
+/*
+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 <OpenColorIO/OpenColorIO.h>
+
+#include "FileTransform.h"
+#include "OpBuilders.h"
+#include "Processor.h"
+
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+ Transform::~Transform()
+ { }
+
+
+ void BuildOps(OpRcPtrVec & ops,
+ const Config & config,
+ const ConstContextRcPtr & context,
+ const ConstTransformRcPtr & transform,
+ TransformDirection dir)
+ {
+ // A null transform is valid, and corresponds to a no-op.
+ if(!transform)
+ return;
+
+ if(ConstAllocationTransformRcPtr allocationTransform = \
+ DynamicPtrCast<const AllocationTransform>(transform))
+ {
+ BuildAllocationOps(ops, config, *allocationTransform, dir);
+ }
+ else if(ConstCDLTransformRcPtr cdlTransform = \
+ DynamicPtrCast<const CDLTransform>(transform))
+ {
+ BuildCDLOps(ops, config, *cdlTransform, dir);
+ }
+ else if(ConstColorSpaceTransformRcPtr colorSpaceTransform = \
+ DynamicPtrCast<const ColorSpaceTransform>(transform))
+ {
+ BuildColorSpaceOps(ops, config, context, *colorSpaceTransform, dir);
+ }
+ else if(ConstDisplayTransformRcPtr displayTransform = \
+ DynamicPtrCast<const DisplayTransform>(transform))
+ {
+ BuildDisplayOps(ops, config, context, *displayTransform, dir);
+ }
+ else if(ConstExponentTransformRcPtr exponentTransform = \
+ DynamicPtrCast<const ExponentTransform>(transform))
+ {
+ BuildExponentOps(ops, config, *exponentTransform, dir);
+ }
+ else if(ConstFileTransformRcPtr fileTransform = \
+ DynamicPtrCast<const FileTransform>(transform))
+ {
+ BuildFileOps(ops, config, context, *fileTransform, dir);
+ }
+ else if(ConstGroupTransformRcPtr groupTransform = \
+ DynamicPtrCast<const GroupTransform>(transform))
+ {
+ BuildGroupOps(ops, config, context, *groupTransform, dir);
+ }
+ else if(ConstLogTransformRcPtr logTransform = \
+ DynamicPtrCast<const LogTransform>(transform))
+ {
+ BuildLogOps(ops, config, *logTransform, dir);
+ }
+ else if(ConstLookTransformRcPtr lookTransform = \
+ DynamicPtrCast<const LookTransform>(transform))
+ {
+ BuildLookOps(ops, config, context, *lookTransform, dir);
+ }
+ else if(ConstMatrixTransformRcPtr matrixTransform = \
+ DynamicPtrCast<const MatrixTransform>(transform))
+ {
+ BuildMatrixOps(ops, config, *matrixTransform, dir);
+ }
+ else if(ConstTruelightTransformRcPtr truelightTransform = \
+ DynamicPtrCast<const TruelightTransform>(transform))
+ {
+ BuildTruelightOps(ops, config, *truelightTransform, dir);
+ }
+ else
+ {
+ std::ostringstream os;
+ os << "Unknown transform type for Op Creation.";
+ throw Exception(os.str().c_str());
+ }
+ }
+
+ std::ostream& operator<< (std::ostream & os, const Transform & transform)
+ {
+ const Transform* t = &transform;
+
+ if(const AllocationTransform * allocationTransform = \
+ dynamic_cast<const AllocationTransform*>(t))
+ {
+ os << *allocationTransform;
+ }
+ else if(const CDLTransform * cdlTransform = \
+ dynamic_cast<const CDLTransform*>(t))
+ {
+ os << *cdlTransform;
+ }
+ else if(const ColorSpaceTransform * colorSpaceTransform = \
+ dynamic_cast<const ColorSpaceTransform*>(t))
+ {
+ os << *colorSpaceTransform;
+ }
+ else if(const DisplayTransform * displayTransform = \
+ dynamic_cast<const DisplayTransform*>(t))
+ {
+ os << *displayTransform;
+ }
+ else if(const ExponentTransform * exponentTransform = \
+ dynamic_cast<const ExponentTransform*>(t))
+ {
+ os << *exponentTransform;
+ }
+ else if(const FileTransform * fileTransform = \
+ dynamic_cast<const FileTransform*>(t))
+ {
+ os << *fileTransform;
+ }
+ else if(const GroupTransform * groupTransform = \
+ dynamic_cast<const GroupTransform*>(t))
+ {
+ os << *groupTransform;
+ }
+ else if(const MatrixTransform * matrixTransform = \
+ dynamic_cast<const MatrixTransform*>(t))
+ {
+ os << *matrixTransform;
+ }
+ else if(const TruelightTransform * truelightTransform = \
+ dynamic_cast<const TruelightTransform*>(t))
+ {
+ os << *truelightTransform;
+ }
+ else
+ {
+ std::ostringstream error;
+ os << "Unknown transform type for serialization.";
+ throw Exception(error.str().c_str());
+ }
+
+ return os;
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/TruelightOp.cpp b/src/core/TruelightOp.cpp
new file mode 100644
index 0000000..50f3915
--- /dev/null
+++ b/src/core/TruelightOp.cpp
@@ -0,0 +1,395 @@
+/*
+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 <iostream>
+
+#ifdef OCIO_TRUELIGHT_SUPPORT
+#include <truelight.h>
+#else
+#define TL_INPUT_LOG 0
+#define TL_INPUT_LIN 1
+#define TL_INPUT_VID 2
+#endif // OCIO_TRUELIGHT_SUPPORT
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "TruelightOp.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ namespace
+ {
+ class TruelightOp : public Op
+ {
+ public:
+ TruelightOp(const char * configroot,
+ const char * profile,
+ const char * camera,
+ const char * inputdisplay,
+ const char * recorder,
+ const char * print,
+ const char * lamp,
+ const char * outputcamera,
+ const char * display,
+ const char * cubeinput,
+ TransformDirection direction);
+ virtual ~TruelightOp();
+
+ virtual OpRcPtr clone() const;
+
+ virtual std::string getInfo() const;
+ virtual std::string getCacheID() const;
+
+ virtual bool isNoOp() const;
+ virtual bool isSameType(const OpRcPtr & op) const;
+ virtual bool isInverse(const OpRcPtr & op) const;
+ virtual bool hasChannelCrosstalk() const;
+
+ virtual void finalize();
+ virtual void apply(float* rgbaBuffer, long numPixels) const;
+
+ virtual bool supportsGpuShader() const;
+ virtual void writeGpuShader(std::ostream & shader,
+ const std::string & pixelName,
+ const GpuShaderDesc & shaderDesc) const;
+
+ private:
+ TransformDirection m_direction;
+ void *m_truelight;
+ std::string m_configroot;
+ std::string m_profile;
+ std::string m_camera;
+ std::string m_inputdisplay;
+ std::string m_recorder;
+ std::string m_print;
+ std::string m_lamp;
+ std::string m_outputcamera;
+ std::string m_display;
+ int m_cubeinput;
+ std::string m_cacheID;
+ };
+
+ TruelightOp::TruelightOp(const char * configroot,
+ const char * profile,
+ const char * camera,
+ const char * inputdisplay,
+ const char * recorder,
+ const char * print,
+ const char * lamp,
+ const char * outputcamera,
+ const char * display,
+ const char * cubeinput,
+ TransformDirection direction):
+ Op(),
+ m_direction(direction),
+ m_configroot(configroot),
+ m_profile(profile),
+ m_camera(camera),
+ m_inputdisplay(inputdisplay),
+ m_recorder(recorder),
+ m_print(print),
+ m_lamp(lamp),
+ m_outputcamera(outputcamera),
+ m_display(display)
+ {
+
+ if(m_direction == TRANSFORM_DIR_UNKNOWN)
+ {
+ throw Exception("Cannot apply TruelightOp op, unspecified transform direction.");
+ }
+
+ std::string _tmp = pystring::lower(cubeinput);
+ if(_tmp == "log") m_cubeinput = TL_INPUT_LOG;
+ else if(_tmp == "linear") m_cubeinput = TL_INPUT_LIN;
+ else if(_tmp == "video") m_cubeinput = TL_INPUT_VID;
+ else
+ {
+ std::ostringstream err;
+ err << "we don't support cubeinput of type " << cubeinput;
+ err << " try log, linear or video.";
+ throw Exception(err.str().c_str());
+ }
+
+#ifdef OCIO_TRUELIGHT_SUPPORT
+
+ if((TruelightBegin("")) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+
+ m_truelight = TruelightCreateInstance();
+ if(!m_truelight)
+ {
+ std::ostringstream err;
+ err << "Error: '" << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+
+ // floating point
+ TruelightInstanceSetMax(m_truelight, 1);
+
+ // where too look for the profiles, prints etc
+ TruelightSetRoot(m_configroot.c_str());
+
+ // invert the transform depending on direction
+ if(m_direction == TRANSFORM_DIR_FORWARD)
+ {
+ TruelightInstanceSetInvertFlag(m_truelight, 0);
+ }
+ else if(m_direction == TRANSFORM_DIR_INVERSE)
+ {
+ TruelightInstanceSetInvertFlag(m_truelight, 1);
+ }
+
+#endif // OCIO_TRUELIGHT_SUPPORT
+
+ }
+
+ OpRcPtr TruelightOp::clone() const
+ {
+ std::string _cubeinput = "unknown";
+ if(m_cubeinput == TL_INPUT_LOG) _cubeinput = "log";
+ else if(m_cubeinput == TL_INPUT_LIN) _cubeinput = "linear";
+ else if(m_cubeinput == TL_INPUT_VID) _cubeinput = "video";
+ OpRcPtr op = OpRcPtr(new TruelightOp(m_configroot.c_str(),
+ m_profile.c_str(),
+ m_camera.c_str(),
+ m_inputdisplay.c_str(),
+ m_recorder.c_str(),
+ m_print.c_str(),
+ m_lamp.c_str(),
+ m_outputcamera.c_str(),
+ m_display.c_str(),
+ _cubeinput.c_str(),
+ m_direction));
+ return op;
+ }
+
+ TruelightOp::~TruelightOp()
+ {
+#ifdef OCIO_TRUELIGHT_SUPPORT
+ if(m_truelight) TruelightDestroyInstance(m_truelight);
+#endif // OCIO_TRUELIGHT_SUPPORT
+ }
+
+ std::string TruelightOp::getInfo() const
+ {
+ return "<TruelightOp>";
+ }
+
+ std::string TruelightOp::getCacheID() const
+ {
+ return m_cacheID;
+ }
+
+ bool TruelightOp::isNoOp() const
+ {
+ return false;
+ }
+ bool TruelightOp::isSameType(const OpRcPtr & /*op*/) const
+ {
+ // TODO: TruelightOp::isSameType
+ return false;
+ }
+
+ bool TruelightOp::isInverse(const OpRcPtr & /*op*/) const
+ {
+ // TODO: TruelightOp::isInverse
+ return false;
+ }
+
+ bool TruelightOp::hasChannelCrosstalk() const
+ {
+ return true;
+ }
+
+ void TruelightOp::finalize()
+ {
+#ifndef OCIO_TRUELIGHT_SUPPORT
+ std::ostringstream err;
+ err << "OCIO has been built without Truelight support";
+ throw Exception(err.str().c_str());
+#else
+ if(m_profile != "")
+ {
+ if(TruelightInstanceSetProfile(m_truelight, m_profile.c_str()) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+ }
+
+ if(m_camera != "")
+ {
+ if(TruelightInstanceSetCamera(m_truelight, m_camera.c_str()) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+ }
+
+ if(m_inputdisplay != "")
+ {
+ if(TruelightInstanceSetInputDisplay(m_truelight, m_inputdisplay.c_str()) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+ }
+
+ if(m_recorder != "")
+ {
+ if(TruelightInstanceSetRecorder(m_truelight, m_recorder.c_str()) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+ }
+
+ if(m_print != "")
+ {
+ if(TruelightInstanceSetPrint(m_truelight, m_print.c_str()) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+ }
+
+ if(m_lamp != "")
+ {
+ if(TruelightInstanceSetLamp(m_truelight, m_lamp.c_str()) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+ }
+
+ if(m_outputcamera != "")
+ {
+ if(TruelightInstanceSetOutputCamera(m_truelight, m_outputcamera.c_str()) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+ }
+
+ if(m_display != "")
+ {
+ if(TruelightInstanceSetDisplay(m_truelight, m_display.c_str()) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+ }
+
+ if(TruelightInstanceSetCubeInput(m_truelight, m_cubeinput) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+
+ if(TruelightInstanceSetUp(m_truelight) == 0)
+ {
+ std::ostringstream err;
+ err << "Error: " << TruelightGetErrorString();
+ throw Exception(err.str().c_str());
+ }
+#endif // OCIO_TRUELIGHT_SUPPORT
+
+ // build cache id
+ std::ostringstream cacheIDStream;
+ cacheIDStream << "<TruelightOp ";
+ cacheIDStream << m_profile << " ";
+ cacheIDStream << m_camera << " ";
+ cacheIDStream << m_inputdisplay << " ";
+ cacheIDStream << m_recorder << " ";
+ cacheIDStream << m_print << " ";
+ cacheIDStream << m_lamp << " ";
+ cacheIDStream << m_outputcamera << " ";
+ cacheIDStream << m_display << " ";
+ cacheIDStream << m_cubeinput << " ";
+ cacheIDStream << TransformDirectionToString(m_direction) << " ";
+ cacheIDStream << ">";
+ m_cacheID = cacheIDStream.str();
+ }
+
+ void TruelightOp::apply(float* rgbaBuffer, long numPixels) const
+ {
+ for(long pixelIndex = 0; pixelIndex < numPixels; ++pixelIndex)
+ {
+#ifdef OCIO_TRUELIGHT_SUPPORT
+ TruelightInstanceTransformF(m_truelight, rgbaBuffer);
+#endif // OCIO_TRUELIGHT_SUPPORT
+ rgbaBuffer += 4; // skip alpha
+ }
+ }
+
+ bool TruelightOp::supportsGpuShader() const
+ {
+ return false;
+ }
+
+ void TruelightOp::writeGpuShader(std::ostream & /*shader*/,
+ const std::string & /*pixelName*/,
+ const GpuShaderDesc & /*shaderDesc*/) const
+ {
+ throw Exception("TruelightOp does not define an gpu shader.");
+ }
+
+ } // anonymous namespace
+
+ void CreateTruelightOps(OpRcPtrVec & ops,
+ const TruelightTransform & data,
+ TransformDirection direction)
+ {
+ ops.push_back(OpRcPtr(new TruelightOp(data.getConfigRoot(),
+ data.getProfile(),
+ data.getCamera(),
+ data.getInputDisplay(),
+ data.getRecorder(),
+ data.getPrint(),
+ data.getLamp(),
+ data.getOutputCamera(),
+ data.getDisplay(),
+ data.getCubeInput(),
+ direction)));
+ }
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/TruelightOp.h b/src/core/TruelightOp.h
new file mode 100644
index 0000000..fe78b6a
--- /dev/null
+++ b/src/core/TruelightOp.h
@@ -0,0 +1,44 @@
+/*
+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.
+*/
+
+#ifndef INCLUDED_OCIO_TRUELIGHTOP_H
+#define INCLUDED_OCIO_TRUELIGHTOP_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "Op.h"
+
+OCIO_NAMESPACE_ENTER
+{
+ void CreateTruelightOps(OpRcPtrVec & ops,
+ const TruelightTransform & data,
+ TransformDirection dir);
+}
+OCIO_NAMESPACE_EXIT
+
+#endif // INCLUDED_OCIO_TRUELIGHTOP_H
diff --git a/src/core/TruelightTransform.cpp b/src/core/TruelightTransform.cpp
new file mode 100644
index 0000000..7e95875
--- /dev/null
+++ b/src/core/TruelightTransform.cpp
@@ -0,0 +1,365 @@
+/*
+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 <iostream>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "OpBuilders.h"
+#include "TruelightOp.h"
+#include "pystring/pystring.h"
+
+OCIO_NAMESPACE_ENTER
+{
+
+ TruelightTransformRcPtr TruelightTransform::Create()
+ {
+ return TruelightTransformRcPtr(new TruelightTransform(), &deleter);
+ }
+
+ void TruelightTransform::deleter(TruelightTransform* t)
+ {
+ delete t;
+ }
+
+ class TruelightTransform::Impl
+ {
+ public:
+ TransformDirection dir_;
+ std::string configroot_;
+ std::string profile_;
+ std::string camera_;
+ std::string inputdisplay_;
+ std::string recorder_;
+ std::string print_;
+ std::string lamp_;
+ std::string outputcamera_;
+ std::string display_;
+ std::string cubeinput_;
+
+ Impl() : dir_(TRANSFORM_DIR_FORWARD)
+ { }
+
+ ~Impl()
+ { }
+
+ Impl& operator= (const Impl & rhs)
+ {
+ dir_ = rhs.dir_;
+ configroot_ = rhs.configroot_;
+ profile_ = rhs.profile_;
+ camera_ = rhs.camera_;
+ inputdisplay_ = rhs.inputdisplay_;
+ recorder_ = rhs.recorder_;
+ print_ = rhs.print_;
+ lamp_ = rhs.lamp_;
+ outputcamera_ = rhs.outputcamera_;
+ display_ = rhs.display_;
+ cubeinput_ = rhs.cubeinput_;
+ return *this;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ TruelightTransform::TruelightTransform()
+ : m_impl(new TruelightTransform::Impl)
+ {
+ getImpl()->configroot_ = "/usr/fl/truelight";
+ getImpl()->profile_ = "";
+ getImpl()->camera_ = "";
+ getImpl()->inputdisplay_ = "";
+ getImpl()->recorder_ = "";
+ getImpl()->print_ = "";
+ getImpl()->lamp_ = "";
+ getImpl()->outputcamera_ = "";
+ getImpl()->display_ = "";
+ getImpl()->cubeinput_ = "log";
+ }
+
+ TransformRcPtr TruelightTransform::createEditableCopy() const
+ {
+ TruelightTransformRcPtr transform = TruelightTransform::Create();
+ *(transform->m_impl) = *m_impl;
+ return transform;
+ }
+
+ TruelightTransform::~TruelightTransform()
+ {
+ delete m_impl;
+ m_impl = NULL;
+ }
+
+ TruelightTransform& TruelightTransform::operator= (const TruelightTransform & rhs)
+ {
+ *m_impl = *rhs.m_impl;
+ return *this;
+ }
+
+ TransformDirection TruelightTransform::getDirection() const
+ {
+ return getImpl()->dir_;
+ }
+
+ void TruelightTransform::setDirection(TransformDirection dir)
+ {
+ getImpl()->dir_ = dir;
+ }
+
+ void TruelightTransform::setConfigRoot(const char * configroot)
+ {
+ getImpl()->configroot_ = configroot;
+ }
+
+ const char * TruelightTransform::getConfigRoot() const
+ {
+ return getImpl()->configroot_.c_str();
+ }
+
+ void TruelightTransform::setProfile(const char * profile)
+ {
+ getImpl()->profile_ = profile;
+ }
+
+ const char * TruelightTransform::getProfile() const
+ {
+ return getImpl()->profile_.c_str();
+ }
+
+ void TruelightTransform::setCamera(const char * camera)
+ {
+ getImpl()->camera_ = camera;
+ }
+
+ const char * TruelightTransform::getCamera() const
+ {
+ return getImpl()->camera_.c_str();
+ }
+
+ void TruelightTransform::setInputDisplay(const char * display)
+ {
+ getImpl()->inputdisplay_ = display;
+ }
+
+ const char * TruelightTransform::getInputDisplay() const
+ {
+ return getImpl()->inputdisplay_.c_str();
+ }
+
+ void TruelightTransform::setRecorder(const char * recorder)
+ {
+ getImpl()->recorder_ = recorder;
+ }
+
+ const char * TruelightTransform::getRecorder() const
+ {
+ return getImpl()->recorder_.c_str();
+ }
+
+ void TruelightTransform::setPrint(const char * print)
+ {
+ getImpl()->print_ = print;
+ }
+
+ const char * TruelightTransform::getPrint() const
+ {
+ return getImpl()->print_.c_str();
+ }
+
+ void TruelightTransform::setLamp(const char * lamp)
+ {
+ getImpl()->lamp_ = lamp;
+ }
+
+ const char * TruelightTransform::getLamp() const
+ {
+ return getImpl()->lamp_.c_str();
+ }
+
+ void TruelightTransform::setOutputCamera(const char * camera)
+ {
+ getImpl()->outputcamera_ = camera;
+ }
+
+ const char * TruelightTransform::getOutputCamera() const
+ {
+ return getImpl()->outputcamera_.c_str();
+ }
+
+ void TruelightTransform::setDisplay(const char * display)
+ {
+ getImpl()->display_ = display;
+ }
+
+ const char * TruelightTransform::getDisplay() const
+ {
+ return getImpl()->display_.c_str();
+ }
+
+ void TruelightTransform::setCubeInput(const char * cubeinput)
+ {
+ getImpl()->cubeinput_ = pystring::lower(cubeinput);
+ }
+
+ const char * TruelightTransform::getCubeInput() const
+ {
+ return getImpl()->cubeinput_.c_str();
+ }
+
+ std::ostream& operator<< (std::ostream& os, const TruelightTransform& t)
+ {
+ os << "<TruelightTransform ";
+ os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
+ os << ">\n";
+ return os;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ void BuildTruelightOps(OpRcPtrVec & ops,
+ const Config& /*config*/,
+ const TruelightTransform & transform,
+ TransformDirection dir)
+ {
+ TransformDirection combinedDir = CombineTransformDirections(dir,
+ transform.getDirection());
+ CreateTruelightOps(ops, transform, combinedDir);
+ }
+
+}
+OCIO_NAMESPACE_EXIT
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef OCIO_UNIT_TEST
+
+namespace OCIO = OCIO_NAMESPACE;
+#include "UnitTest.h"
+
+OIIO_ADD_TEST(TruelightTransform, simpletest)
+{
+
+ OCIO::ConfigRcPtr config = OCIO::Config::Create();
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("log");
+ cs->setFamily("log");
+ config->addColorSpace(cs);
+ config->setRole(OCIO::ROLE_COMPOSITING_LOG, cs->getName());
+ }
+ {
+ OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
+ cs->setName("sRGB");
+ cs->setFamily("srgb");
+ OCIO::TruelightTransformRcPtr transform1 = OCIO::TruelightTransform::Create();
+ transform1->setConfigRoot("/usr/fl/truelight");
+ transform1->setPrint("internal-LowContrast");
+ //transform1->setInputDisplay("DCIrgb");
+ transform1->setDisplay("sRGB");
+ transform1->setCubeInput("log");
+ cs->setTransform(transform1, OCIO::COLORSPACE_DIR_FROM_REFERENCE);
+ config->addColorSpace(cs);
+ }
+
+ // check the transform round trip
+ OCIO::ConstProcessorRcPtr tosrgb;
+ OCIO::ConstProcessorRcPtr tolog;
+
+#ifdef OCIO_TRUELIGHT_SUPPORT
+ OIIO_CHECK_NO_THOW(tosrgb = config->getProcessor("log", "sRGB"));
+ OIIO_CHECK_NO_THOW(tolog = config->getProcessor("sRGB", "log"));
+#else
+ OIIO_CHECK_THOW(tosrgb = config->getProcessor("log", "sRGB"), OCIO::Exception);
+ OIIO_CHECK_THOW(tolog = config->getProcessor("sRGB", "log"), OCIO::Exception);
+#endif
+
+#ifdef OCIO_TRUELIGHT_SUPPORT
+ float input[3] = {0.5f, 0.5f, 0.5f};
+ float output[3] = {0.500098f, 0.500317f, 0.501134f};
+ OIIO_CHECK_NO_THOW(tosrgb->applyRGB(input));
+ OIIO_CHECK_NO_THOW(tolog->applyRGB(input));
+ OIIO_CHECK_CLOSE(input[0], output[0], 1e-4);
+ OIIO_CHECK_CLOSE(input[1], output[1], 1e-4);
+ OIIO_CHECK_CLOSE(input[2], output[2], 1e-4);
+#endif
+
+ std::ostringstream os;
+ OIIO_CHECK_NO_THOW(config->serialize(os));
+
+ std::string referenceconfig =
+ "ocio_profile_version: 1\n"
+ "\n"
+ "search_path: \"\"\n"
+ "strictparsing: true\n"
+ "luma: [0.2126, 0.7152, 0.0722]\n"
+ "\n"
+ "roles:\n"
+ " compositing_log: log\n"
+ "\n"
+ "displays:\n"
+ " {}\n"
+ "\n"
+ "active_displays: []\n"
+ "active_views: []\n"
+ "\n"
+ "colorspaces:\n"
+ " - !<ColorSpace>\n"
+ " name: log\n"
+ " family: log\n"
+ " equalitygroup: \"\"\n"
+ " bitdepth: unknown\n"
+ " isdata: false\n"
+ " allocation: uniform\n"
+ "\n"
+ " - !<ColorSpace>\n"
+ " name: sRGB\n"
+ " family: srgb\n"
+ " equalitygroup: \"\"\n"
+ " bitdepth: unknown\n"
+ " isdata: false\n"
+ " allocation: uniform\n"
+ " from_reference: !<TruelightTransform> {config_root: /usr/fl/truelight, print: internal-LowContrast, display: sRGB, cube_input: log}\n";
+
+
+ std::vector<std::string> osvec;
+ OCIO::pystring::splitlines(os.str(), osvec);
+ std::vector<std::string> referenceconfigvec;
+ OCIO::pystring::splitlines(referenceconfig, referenceconfigvec);
+
+ OIIO_CHECK_EQUAL(osvec.size(), referenceconfigvec.size());
+ for(unsigned int i = 0; i < referenceconfigvec.size(); ++i)
+ OIIO_CHECK_EQUAL(osvec[i], referenceconfigvec[i]);
+
+ std::istringstream is;
+ is.str(referenceconfig);
+ OCIO::ConstConfigRcPtr rtconfig;
+ OIIO_CHECK_NO_THOW(rtconfig = OCIO::Config::CreateFromStream(is));
+
+}
+
+#endif // OCIO_BUILD_TESTS
diff --git a/src/core/UnitTest.cpp b/src/core/UnitTest.cpp
new file mode 100644
index 0000000..8b1270d
--- /dev/null
+++ b/src/core/UnitTest.cpp
@@ -0,0 +1,39 @@
+/*
+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.
+*/
+
+#ifndef INCLUDED_OCIO_UNITTEST_H
+#define INCLUDED_OCIO_UNITTEST_H
+
+#ifdef OCIO_UNIT_TEST
+#pragma GCC visibility push(default)
+#include <unittest.h> // OIIO unit tests header
+OIIO_TEST_APP(OpenColorIO_Core_Unit_Tests)
+#pragma GCC visibility pop
+#endif // OCIO_UNIT_TEST
+
+#endif // INCLUDED_OCIO_UNITTEST_H
diff --git a/src/core/UnitTest.h b/src/core/UnitTest.h
new file mode 100644
index 0000000..33625c5
--- /dev/null
+++ b/src/core/UnitTest.h
@@ -0,0 +1,38 @@
+/*
+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.
+*/
+
+#ifndef INCLUDED_OCIO_UNITTEST_H
+#define INCLUDED_OCIO_UNITTEST_H
+
+#ifdef OCIO_UNIT_TEST
+#pragma GCC visibility push(default)
+#include <unittest.h> // OIIO unit tests header
+#pragma GCC visibility pop
+#endif // OCIO_UNIT_TEST
+
+#endif // INCLUDED_OCIO_UNITTEST_H
diff --git a/src/core/md5/md5.cpp b/src/core/md5/md5.cpp
new file mode 100644
index 0000000..3eb5673
--- /dev/null
+++ b/src/core/md5/md5.cpp
@@ -0,0 +1,391 @@
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+// This file was altered for OCIO compilation purposes
+
+#include "md5.h"
+#include <cstring>
+
+
+OCIO_NAMESPACE_ENTER
+{
+
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/md5/md5.h b/src/core/md5/md5.h
new file mode 100644
index 0000000..cb36edb
--- /dev/null
+++ b/src/core/md5/md5.h
@@ -0,0 +1,97 @@
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+// This file was altered for OCIO compilation purposes
+
+#ifndef INCLUDED_OCIO_md5_INCLUDED
+#define INCLUDED_OCIO_md5_INCLUDED
+
+
+#include <OpenColorIO/OpenColorIO.h>
+
+OCIO_NAMESPACE_ENTER
+{
+
+// Note: the md5 functions should not be wrapped in extern "C', otherwise
+// the symbols will not be appropriately wrapped in the OCIO namespace
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif /* md5_INCLUDED */
diff --git a/src/core/pystring/pystring.cpp b/src/core/pystring/pystring.cpp
new file mode 100644
index 0000000..7805162
--- /dev/null
+++ b/src/core/pystring/pystring.cpp
@@ -0,0 +1,1658 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2008-2010, Sony Pictures Imageworks Inc
+// 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 the organization 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 <OpenColorIO/OpenColorIO.h>
+
+#include "pystring.h"
+
+#include <algorithm>
+#include <cctype>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+
+OCIO_NAMESPACE_ENTER
+{
+
+namespace pystring
+{
+
+#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) || defined(_MSC_VER)
+#ifndef WINDOWS
+#define WINDOWS
+#endif
+#endif
+
+// This definition codes from configure.in in the python src.
+// Strictly speaking this limits us to str sizes of 2**31.
+// Should we wish to handle this limit, we could use an architecture
+// specific #defines and read from ssize_t (unistd.h) if the header exists.
+// But in the meantime, the use of int assures maximum arch compatibility.
+// This must also equal the size used in the end = MAX_32BIT_INT default arg.
+
+typedef int Py_ssize_t;
+
+/* helper macro to fixup start/end slice values */
+#define ADJUST_INDICES(start, end, len) \
+ if (end > len) \
+ end = len; \
+ else if (end < 0) { \
+ end += len; \
+ if (end < 0) \
+ end = 0; \
+ } \
+ if (start < 0) { \
+ start += len; \
+ if (start < 0) \
+ start = 0; \
+ }
+
+
+ namespace {
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// why doesn't the std::reverse work?
+ ///
+ void reverse_strings( std::vector< std::string > & result)
+ {
+ for (std::vector< std::string >::size_type i = 0; i < result.size() / 2; i++ )
+ {
+ std::swap(result[i], result[result.size() - 1 - i]);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ void split_whitespace( const std::string & str, std::vector< std::string > & result, int maxsplit )
+ {
+ std::string::size_type i, j, len = str.size();
+ for (i = j = 0; i < len; )
+ {
+
+ while ( i < len && ::isspace( str[i] ) ) i++;
+ j = i;
+
+ while ( i < len && ! ::isspace( str[i]) ) i++;
+
+
+
+ if (j < i)
+ {
+ if ( maxsplit-- <= 0 ) break;
+
+ result.push_back( str.substr( j, i - j ));
+
+ while ( i < len && ::isspace( str[i])) i++;
+ j = i;
+ }
+ }
+ if (j < len)
+ {
+ result.push_back( str.substr( j, len - j ));
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ void rsplit_whitespace( const std::string & str, std::vector< std::string > & result, int maxsplit )
+ {
+ std::string::size_type len = str.size();
+ std::string::size_type i, j;
+ for (i = j = len; i > 0; )
+ {
+
+ while ( i > 0 && ::isspace( str[i - 1] ) ) i--;
+ j = i;
+
+ while ( i > 0 && ! ::isspace( str[i - 1]) ) i--;
+
+
+
+ if (j > i)
+ {
+ if ( maxsplit-- <= 0 ) break;
+
+ result.push_back( str.substr( i, j - i ));
+
+ while ( i > 0 && ::isspace( str[i - 1])) i--;
+ j = i;
+ }
+ }
+ if (j > 0)
+ {
+ result.push_back( str.substr( 0, j ));
+ }
+ //std::reverse( result, result.begin(), result.end() );
+ reverse_strings( result );
+ }
+
+ } //anonymous namespace
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ void split( const std::string & str, std::vector< std::string > & result, const std::string & sep, int maxsplit )
+ {
+ result.clear();
+
+ if ( maxsplit < 0 ) maxsplit = MAX_32BIT_INT;//result.max_size();
+
+
+ if ( sep.size() == 0 )
+ {
+ split_whitespace( str, result, maxsplit );
+ return;
+ }
+
+ std::string::size_type i,j, len = str.size(), n = sep.size();
+
+ i = j = 0;
+
+ while ( i+n <= len )
+ {
+ if ( str[i] == sep[0] && str.substr( i, n ) == sep )
+ {
+ if ( maxsplit-- <= 0 ) break;
+
+ result.push_back( str.substr( j, i - j ) );
+ i = j = i + n;
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ result.push_back( str.substr( j, len-j ) );
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ void rsplit( const std::string & str, std::vector< std::string > & result, const std::string & sep, int maxsplit )
+ {
+ if ( maxsplit < 0 )
+ {
+ split( str, result, sep, 0 );
+ return;
+ }
+
+ result.clear();
+
+ if ( sep.size() == 0 )
+ {
+ rsplit_whitespace( str, result, maxsplit );
+ return;
+ }
+
+ std::string::size_type i,j, len = str.size(), n = sep.size();
+
+ i = j = len;
+
+ while ( i > n )
+ {
+ if ( str[i - 1] == sep[n - 1] && str.substr( i - n, n ) == sep )
+ {
+ if ( maxsplit-- <= 0 ) break;
+
+ result.push_back( str.substr( i, j - i ) );
+ i = j = i - n;
+ }
+ else
+ {
+ i--;
+ }
+ }
+
+ result.push_back( str.substr( 0, j ) );
+ reverse_strings( result );
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ #define LEFTSTRIP 0
+ #define RIGHTSTRIP 1
+ #define BOTHSTRIP 2
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string do_strip( const std::string & str, int striptype, const std::string & chars )
+ {
+ Py_ssize_t len = (Py_ssize_t) str.size(), i, j, charslen = (Py_ssize_t) chars.size();
+
+ if ( charslen == 0 )
+ {
+ i = 0;
+ if ( striptype != RIGHTSTRIP )
+ {
+ while ( i < len && ::isspace( str[i] ) )
+ {
+ i++;
+ }
+ }
+
+ j = len;
+ if ( striptype != LEFTSTRIP )
+ {
+ do
+ {
+ j--;
+ }
+ while (j >= i && ::isspace(str[j]));
+
+ j++;
+ }
+
+
+ }
+ else
+ {
+ const char * sep = chars.c_str();
+
+ i = 0;
+ if ( striptype != RIGHTSTRIP )
+ {
+ while ( i < len && memchr(sep, str[i], charslen) )
+ {
+ i++;
+ }
+ }
+
+ j = len;
+ if (striptype != LEFTSTRIP)
+ {
+ do
+ {
+ j--;
+ }
+ while (j >= i && memchr(sep, str[j], charslen) );
+ j++;
+ }
+
+
+ }
+
+ if ( i == 0 && j == len )
+ {
+ return str;
+ }
+ else
+ {
+ return str.substr( i, j - i );
+ }
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ void partition( const std::string & str, const std::string & sep, std::vector< std::string > & result )
+ {
+ result.resize(3);
+ int index = find( str, sep );
+ if ( index < 0 )
+ {
+ result[0] = str;
+ result[1] = "";
+ result[2] = "";
+ }
+ else
+ {
+ result[0] = str.substr( 0, index );
+ result[1] = sep;
+ result[2] = str.substr( index + sep.size(), str.size() );
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ void rpartition( const std::string & str, const std::string & sep, std::vector< std::string > & result )
+ {
+ result.resize(3);
+ int index = rfind( str, sep );
+ if ( index < 0 )
+ {
+ result[0] = "";
+ result[1] = "";
+ result[2] = str;
+ }
+ else
+ {
+ result[0] = str.substr( 0, index );
+ result[1] = sep;
+ result[2] = str.substr( index + sep.size(), str.size() );
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string strip( const std::string & str, const std::string & chars )
+ {
+ return do_strip( str, BOTHSTRIP, chars );
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string lstrip( const std::string & str, const std::string & chars )
+ {
+ return do_strip( str, LEFTSTRIP, chars );
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string rstrip( const std::string & str, const std::string & chars )
+ {
+ return do_strip( str, RIGHTSTRIP, chars );
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string join( const std::string & str, const std::vector< std::string > & seq )
+ {
+ std::vector< std::string >::size_type seqlen = seq.size(), i;
+
+ if ( seqlen == 0 ) return "";
+ if ( seqlen == 1 ) return seq[0];
+
+ std::string result( seq[0] );
+
+ for ( i = 1; i < seqlen; ++i )
+ {
+ result += str + seq[i];
+
+ }
+
+
+ return result;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+
+ namespace
+ {
+ /* Matches the end (direction >= 0) or start (direction < 0) of self
+ * against substr, using the start and end arguments. Returns
+ * -1 on error, 0 if not found and 1 if found.
+ */
+
+ int _string_tailmatch(const std::string & self, const std::string & substr,
+ Py_ssize_t start, Py_ssize_t end,
+ int direction)
+ {
+ Py_ssize_t len = (Py_ssize_t) self.size();
+ Py_ssize_t slen = (Py_ssize_t) substr.size();
+
+ const char* sub = substr.c_str();
+ const char* str = self.c_str();
+
+ ADJUST_INDICES(start, end, len);
+
+ if (direction < 0) {
+ // startswith
+ if (start+slen > len)
+ return 0;
+ } else {
+ // endswith
+ if (end-start < slen || start > len)
+ return 0;
+ if (end-slen > start)
+ start = end - slen;
+ }
+ if (end-start >= slen)
+ return (!std::memcmp(str+start, sub, slen));
+
+ return 0;
+ }
+ }
+
+ bool endswith( const std::string & str, const std::string & suffix, int start, int end )
+ {
+ int result = _string_tailmatch(str, suffix,
+ (Py_ssize_t) start, (Py_ssize_t) end, +1);
+ //if (result == -1) // TODO: Error condition
+
+ return static_cast<bool>(result);
+ }
+
+
+ bool startswith( const std::string & str, const std::string & prefix, int start, int end )
+ {
+ int result = _string_tailmatch(str, prefix,
+ (Py_ssize_t) start, (Py_ssize_t) end, -1);
+ //if (result == -1) // TODO: Error condition
+
+ return static_cast<bool>(result);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+
+ bool isalnum( const std::string & str )
+ {
+ std::string::size_type len = str.size(), i;
+ if ( len == 0 ) return false;
+
+
+ if( len == 1 )
+ {
+ return ::isalnum( str[0] );
+ }
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( !::isalnum( str[i] ) ) return false;
+ }
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ bool isalpha( const std::string & str )
+ {
+ std::string::size_type len = str.size(), i;
+ if ( len == 0 ) return false;
+ if( len == 1 ) return ::isalpha( (int) str[0] );
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( !::isalpha( (int) str[i] ) ) return false;
+ }
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ bool isdigit( const std::string & str )
+ {
+ std::string::size_type len = str.size(), i;
+ if ( len == 0 ) return false;
+ if( len == 1 ) return ::isdigit( str[0] );
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( ! ::isdigit( str[i] ) ) return false;
+ }
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ bool islower( const std::string & str )
+ {
+ std::string::size_type len = str.size(), i;
+ if ( len == 0 ) return false;
+ if( len == 1 ) return ::islower( str[0] );
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( !::islower( str[i] ) ) return false;
+ }
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ bool isspace( const std::string & str )
+ {
+ std::string::size_type len = str.size(), i;
+ if ( len == 0 ) return false;
+ if( len == 1 ) return ::isspace( str[0] );
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( !::isspace( str[i] ) ) return false;
+ }
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ bool istitle( const std::string & str )
+ {
+ std::string::size_type len = str.size(), i;
+
+ if ( len == 0 ) return false;
+ if ( len == 1 ) return ::isupper( str[0] );
+
+ bool cased = false, previous_is_cased = false;
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( ::isupper( str[i] ) )
+ {
+ if ( previous_is_cased )
+ {
+ return false;
+ }
+
+ previous_is_cased = true;
+ cased = true;
+ }
+ else if ( ::islower( str[i] ) )
+ {
+ if (!previous_is_cased)
+ {
+ return false;
+ }
+
+ previous_is_cased = true;
+ cased = true;
+
+ }
+ else
+ {
+ previous_is_cased = false;
+ }
+ }
+
+ return cased;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ bool isupper( const std::string & str )
+ {
+ std::string::size_type len = str.size(), i;
+ if ( len == 0 ) return false;
+ if( len == 1 ) return ::isupper( str[0] );
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( !::isupper( str[i] ) ) return false;
+ }
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string capitalize( const std::string & str )
+ {
+ std::string s( str );
+ std::string::size_type len = s.size(), i;
+
+ if ( len > 0)
+ {
+ if (::islower(s[0])) s[0] = (char) ::toupper( s[0] );
+ }
+
+ for ( i = 1; i < len; ++i )
+ {
+ if (::isupper(s[i])) s[i] = (char) ::tolower( s[i] );
+ }
+
+ return s;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string lower( const std::string & str )
+ {
+ std::string s( str );
+ std::string::size_type len = s.size(), i;
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( ::isupper( s[i] ) ) s[i] = (char) ::tolower( s[i] );
+ }
+
+ return s;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string upper( const std::string & str )
+ {
+ std::string s( str ) ;
+ std::string::size_type len = s.size(), i;
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( ::islower( s[i] ) ) s[i] = (char) ::toupper( s[i] );
+ }
+
+ return s;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string swapcase( const std::string & str )
+ {
+ std::string s( str );
+ std::string::size_type len = s.size(), i;
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( ::islower( s[i] ) ) s[i] = (char) ::toupper( s[i] );
+ else if (::isupper( s[i] ) ) s[i] = (char) ::tolower( s[i] );
+ }
+
+ return s;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string title( const std::string & str )
+ {
+ std::string s( str );
+ std::string::size_type len = s.size(), i;
+ bool previous_is_cased = false;
+
+ for ( i = 0; i < len; ++i )
+ {
+ int c = s[i];
+ if ( ::islower(c) )
+ {
+ if ( !previous_is_cased )
+ {
+ s[i] = (char) ::toupper(c);
+ }
+ previous_is_cased = true;
+ }
+ else if ( ::isupper(c) )
+ {
+ if ( previous_is_cased )
+ {
+ s[i] = (char) ::tolower(c);
+ }
+ previous_is_cased = true;
+ }
+ else
+ {
+ previous_is_cased = false;
+ }
+ }
+
+ return s;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string translate( const std::string & str, const std::string & table, const std::string & deletechars )
+ {
+ std::string s;
+ std::string::size_type len = str.size(), dellen = deletechars.size();
+
+ if ( table.size() != 256 )
+ {
+ // TODO : raise exception instead
+ return str;
+ }
+
+ //if nothing is deleted, use faster code
+ if ( dellen == 0 )
+ {
+ s = str;
+ for ( std::string::size_type i = 0; i < len; ++i )
+ {
+ s[i] = table[ s[i] ];
+ }
+ return s;
+ }
+
+
+ int trans_table[256];
+ for ( int i = 0; i < 256; i++)
+ {
+ trans_table[i] = table[i];
+ }
+
+ for ( std::string::size_type i = 0; i < dellen; i++)
+ {
+ trans_table[(int) deletechars[i] ] = -1;
+ }
+
+ for ( std::string::size_type i = 0; i < len; ++i )
+ {
+ if ( trans_table[ (int) str[i] ] != -1 )
+ {
+ s += table[ str[i] ];
+ }
+ }
+
+ return s;
+
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string zfill( const std::string & str, int width )
+ {
+ int len = (int)str.size();
+
+ if ( len >= width )
+ {
+ return str;
+ }
+
+ std::string s( str );
+
+ int fill = width - len;
+
+ s = std::string( fill, '0' ) + s;
+
+
+ if ( s[fill] == '+' || s[fill] == '-' )
+ {
+ s[0] = s[fill];
+ s[fill] = '0';
+ }
+
+ return s;
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string ljust( const std::string & str, int width )
+ {
+ std::string::size_type len = str.size();
+ if ( (( int ) len ) >= width ) return str;
+ return str + std::string( width - len, ' ' );
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string rjust( const std::string & str, int width )
+ {
+ std::string::size_type len = str.size();
+ if ( (( int ) len ) >= width ) return str;
+ return std::string( width - len, ' ' ) + str;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string center( const std::string & str, int width )
+ {
+ int len = (int) str.size();
+ int marg, left;
+
+ if ( len >= width ) return str;
+
+ marg = width - len;
+ left = marg / 2 + (marg & width & 1);
+
+ return std::string( left, ' ' ) + str + std::string( marg - left, ' ' );
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string slice( const std::string & str, int start, int end )
+ {
+ ADJUST_INDICES(start, end, (int) str.size());
+ if ( start >= end ) return "";
+ return str.substr( start, end - start );
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ int find( const std::string & str, const std::string & sub, int start, int end )
+ {
+ ADJUST_INDICES(start, end, (int) str.size());
+
+ std::string::size_type result = str.find( sub, start );
+
+ // If we cannot find the string, or if the end-point of our found substring is past
+ // the allowed end limit, return that it can't be found.
+ if( result == std::string::npos ||
+ (result + sub.size() > (std::string::size_type)end) )
+ {
+ return -1;
+ }
+
+ return (int) result;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ int index( const std::string & str, const std::string & sub, int start, int end )
+ {
+ return find( str, sub, start, end );
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ int rfind( const std::string & str, const std::string & sub, int start, int end )
+ {
+ ADJUST_INDICES(start, end, (int) str.size());
+
+ std::string::size_type result = str.rfind( sub, end );
+
+ if( result == std::string::npos ||
+ result < (std::string::size_type)start ||
+ (result + sub.size() > (std::string::size_type)end))
+ return -1;
+
+ return (int)result;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ int rindex( const std::string & str, const std::string & sub, int start, int end )
+ {
+ return rfind( str, sub, start, end );
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string expandtabs( const std::string & str, int tabsize )
+ {
+ std::string s( str );
+
+ std::string::size_type len = str.size(), i = 0;
+ int offset = 0;
+
+ int j = 0;
+
+ for ( i = 0; i < len; ++i )
+ {
+ if ( str[i] == '\t' )
+ {
+
+ if ( tabsize > 0 )
+ {
+ int fillsize = tabsize - (j % tabsize);
+ j += fillsize;
+ s.replace( i + offset, 1, std::string( fillsize, ' ' ));
+ offset += fillsize - 1;
+ }
+ else
+ {
+ s.replace( i + offset, 1, "" );
+ offset -= 1;
+ }
+
+ }
+ else
+ {
+ j++;
+
+ if (str[i] == '\n' || str[i] == '\r')
+ {
+ j = 0;
+ }
+ }
+ }
+
+ return s;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ int count( const std::string & str, const std::string & substr, int start, int end )
+ {
+ int nummatches = 0;
+ int cursor = start;
+
+ while ( 1 )
+ {
+ cursor = find( str, substr, cursor, end );
+
+ if ( cursor < 0 ) break;
+
+ cursor += (int) substr.size();
+ nummatches += 1;
+ }
+
+ return nummatches;
+
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string replace( const std::string & str, const std::string & oldstr, const std::string & newstr, int count )
+ {
+ int sofar = 0;
+ int cursor = 0;
+ std::string s( str );
+
+ std::string::size_type oldlen = oldstr.size(), newlen = newstr.size();
+
+ while ( ( cursor = find( s, oldstr, cursor ) ) != -1 )
+ {
+ if ( count > -1 && sofar >= count )
+ {
+ break;
+ }
+
+ s.replace( cursor, oldlen, newstr );
+
+ cursor += (int) newlen;
+ ++sofar;
+ }
+
+ return s;
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ void splitlines( const std::string & str, std::vector< std::string > & result, bool keepends )
+ {
+ result.clear();
+ std::string::size_type len = str.size(), i, j, eol;
+
+ for (i = j = 0; i < len; )
+ {
+ while (i < len && str[i] != '\n' && str[i] != '\r') i++;
+
+ eol = i;
+ if (i < len)
+ {
+ if (str[i] == '\r' && i + 1 < len && str[i+1] == '\n')
+ {
+ i += 2;
+ }
+ else
+ {
+ i++;
+ }
+ if (keepends)
+ eol = i;
+
+ }
+
+ result.push_back( str.substr( j, eol - j ) );
+ j = i;
+
+ }
+
+ if (j < len)
+ {
+ result.push_back( str.substr( j, len - j ) );
+ }
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ std::string mul( const std::string & str, int n )
+ {
+ // Early exits
+ if (n <= 0) return "";
+ if (n == 1) return str;
+
+ std::ostringstream os;
+ for(int i=0; i<n; ++i)
+ {
+ os << str;
+ }
+ return os.str();
+ }
+
+
+
+namespace os
+{
+namespace path
+{
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+ /// These functions are C++ ports of the python2.6 versions of os.path,
+ /// and come from genericpath.py, ntpath.py, posixpath.py
+
+ /// Split a pathname into drive and path specifiers.
+ /// Returns drivespec, pathspec. Either part may be empty.
+ void splitdrive_nt(std::string & drivespec, std::string & pathspec,
+ const std::string & p)
+ {
+ if(pystring::slice(p, 1, 2) == ":")
+ {
+ std::string path = p; // In case drivespec == p
+ drivespec = pystring::slice(path, 0, 2);
+ pathspec = pystring::slice(path, 2);
+ }
+ else
+ {
+ drivespec = "";
+ pathspec = p;
+ }
+ }
+
+ // On Posix, drive is always empty
+ void splitdrive_posix(std::string & drivespec, std::string & pathspec,
+ const std::string & path)
+ {
+ drivespec = "";
+ pathspec = path;
+ }
+
+ void splitdrive(std::string & drivespec, std::string & pathspec,
+ const std::string & path)
+ {
+#ifdef WINDOWS
+ return splitdrive_nt(drivespec, pathspec, path);
+#else
+ return splitdrive_posix(drivespec, pathspec, path);
+#endif
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+
+ // Test whether a path is absolute
+ // In windows, if the character to the right of the colon
+ // is a forward or backslash it's absolute.
+ bool isabs_nt(const std::string & path)
+ {
+ std::string drivespec, pathspec;
+ splitdrive_nt(drivespec, pathspec, path);
+ if(pathspec.empty()) return false;
+ return ((pathspec[0] == '/') || (pathspec[0] == '\\'));
+ }
+
+ bool isabs_posix(const std::string & s)
+ {
+ return pystring::startswith(s, "/");
+ }
+
+ bool isabs(const std::string & path)
+ {
+#ifdef WINDOWS
+ return isabs_nt(path);
+#else
+ return isabs_posix(path);
+#endif
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+
+ std::string abspath_nt(const std::string & path, const std::string & cwd)
+ {
+ std::string p = path;
+ if(!isabs_nt(p)) p = join_nt(cwd, p);
+ return normpath_nt(p);
+ }
+
+ std::string abspath_posix(const std::string & path, const std::string & cwd)
+ {
+ std::string p = path;
+ if(!isabs_posix(p)) p = join_posix(cwd, p);
+ return normpath_posix(p);
+ }
+
+ std::string abspath(const std::string & path, const std::string & cwd)
+ {
+#ifdef WINDOWS
+ return abspath_nt(path, cwd);
+#else
+ return abspath_posix(path, cwd);
+#endif
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+
+ std::string join_nt(const std::vector< std::string > & paths)
+ {
+ if(paths.empty()) return "";
+ if(paths.size() == 1) return paths[0];
+
+ std::string path = paths[0];
+
+ for(unsigned int i=1; i<paths.size(); ++i)
+ {
+ std::string b = paths[i];
+
+ bool b_nts = false;
+ if(path.empty())
+ {
+ b_nts = true;
+ }
+ else if(isabs_nt(b))
+ {
+ // This probably wipes out path so far. However, it's more
+ // complicated if path begins with a drive letter:
+ // 1. join('c:', '/a') == 'c:/a'
+ // 2. join('c:/', '/a') == 'c:/a'
+ // But
+ // 3. join('c:/a', '/b') == '/b'
+ // 4. join('c:', 'd:/') = 'd:/'
+ // 5. join('c:/', 'd:/') = 'd:/'
+
+ if( (pystring::slice(path, 1, 2) != ":") ||
+ (pystring::slice(b, 1, 2) == ":") )
+ {
+ // Path doesnt start with a drive letter
+ b_nts = true;
+ }
+ // Else path has a drive letter, and b doesn't but is absolute.
+ else if((path.size()>3) ||
+ ((path.size()==3) && !pystring::endswith(path, "/") && !pystring::endswith(path, "\\")))
+ {
+ b_nts = true;
+ }
+ }
+
+ if(b_nts)
+ {
+ path = b;
+ }
+ else
+ {
+ // Join, and ensure there's a separator.
+ // assert len(path) > 0
+ if( pystring::endswith(path, "/") || pystring::endswith(path, "\\"))
+ {
+ if(pystring::startswith(b,"/") || pystring::startswith(b,"\\"))
+ {
+ path += pystring::slice(b, 1);
+ }
+ else
+ {
+ path += b;
+ }
+ }
+ else if(pystring::endswith(path, ":"))
+ {
+ path += b;
+ }
+ else if(!b.empty())
+ {
+ if(pystring::startswith(b,"/") || pystring::startswith(b,"\\"))
+ {
+ path += b;
+ }
+ else
+ {
+ path += "\\" + b;
+ }
+ }
+ else
+ {
+ // path is not empty and does not end with a backslash,
+ // but b is empty; since, e.g., split('a/') produces
+ // ('a', ''), it's best if join() adds a backslash in
+ // this case.
+ path += "\\";
+ }
+ }
+ }
+
+ return path;
+ }
+
+ // Join two or more pathname components, inserting "\\" as needed.
+ std::string join_nt(const std::string & a, const std::string & b)
+ {
+ std::vector< std::string > paths(2);
+ paths[0] = a;
+ paths[1] = b;
+ return join_nt(paths);
+ }
+
+ // Join pathnames.
+ // If any component is an absolute path, all previous path components
+ // will be discarded.
+ // Ignore the previous parts if a part is absolute.
+ // Insert a '/' unless the first part is empty or already ends in '/'.
+
+ std::string join_posix(const std::vector< std::string > & paths)
+ {
+ if(paths.empty()) return "";
+ if(paths.size() == 1) return paths[0];
+
+ std::string path = paths[0];
+
+ for(unsigned int i=1; i<paths.size(); ++i)
+ {
+ std::string b = paths[i];
+ if(pystring::startswith(b, "/"))
+ {
+ path = b;
+ }
+ else if(path.empty() || pystring::endswith(path, "/"))
+ {
+ path += b;
+ }
+ else
+ {
+ path += "/" + b;
+ }
+ }
+
+ return path;
+ }
+
+ std::string join_posix(const std::string & a, const std::string & b)
+ {
+ std::vector< std::string > paths(2);
+ paths[0] = a;
+ paths[1] = b;
+ return join_posix(paths);
+ }
+
+ std::string join(const std::string & path1, const std::string & path2)
+ {
+#ifdef WINDOWS
+ return join_nt(path1, path2);
+#else
+ return join_posix(path1, path2);
+#endif
+ }
+
+
+ std::string join(const std::vector< std::string > & paths)
+ {
+#ifdef WINDOWS
+ return join_nt(paths);
+#else
+ return join_posix(paths);
+#endif
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+
+
+ // Split a pathname.
+ // Return (head, tail) where tail is everything after the final slash.
+ // Either part may be empty
+
+ void split_nt(std::string & head, std::string & tail, const std::string & path)
+ {
+ std::string d, p;
+ splitdrive_nt(d, p, path);
+
+ // set i to index beyond p's last slash
+ int i = (int)p.size();
+
+ while(i>0 && (p[i-1] != '\\') && (p[i-1] != '/'))
+ {
+ i = i - 1;
+ }
+
+ head = pystring::slice(p,0,i);
+ tail = pystring::slice(p,i); // now tail has no slashes
+
+ // remove trailing slashes from head, unless it's all slashes
+ std::string head2 = head;
+ while(!head2.empty() && ((pystring::slice(head2,-1) == "/") ||
+ (pystring::slice(head2,-1) == "\\")))
+ {
+ head2 = pystring::slice(head,0,-1);
+ }
+
+ if(!head2.empty()) head = head2;
+ head = d + head;
+ }
+
+
+ // Split a path in head (everything up to the last '/') and tail (the
+ // rest). If the path ends in '/', tail will be empty. If there is no
+ // '/' in the path, head will be empty.
+ // Trailing '/'es are stripped from head unless it is the root.
+
+ void split_posix(std::string & head, std::string & tail, const std::string & p)
+ {
+ int i = pystring::rfind(p, "/") + 1;
+
+ head = pystring::slice(p,0,i);
+ tail = pystring::slice(p,i);
+
+ if(!head.empty() && (head != pystring::mul("/", (int) head.size())))
+ {
+ head = pystring::rstrip(head, "/");
+ }
+ }
+
+ void split(std::string & head, std::string & tail, const std::string & path)
+ {
+#ifdef WINDOWS
+ return split_nt(head, tail, path);
+#else
+ return split_posix(head, tail, path);
+#endif
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+
+ std::string basename_nt(const std::string & path)
+ {
+ std::string head, tail;
+ split_nt(head, tail, path);
+ return tail;
+ }
+
+ std::string basename_posix(const std::string & path)
+ {
+ std::string head, tail;
+ split_posix(head, tail, path);
+ return tail;
+ }
+
+ std::string basename(const std::string & path)
+ {
+#ifdef WINDOWS
+ return basename_nt(path);
+#else
+ return basename_posix(path);
+#endif
+ }
+
+ std::string dirname_nt(const std::string & path)
+ {
+ std::string head, tail;
+ split_nt(head, tail, path);
+ return head;
+ }
+
+ std::string dirname_posix(const std::string & path)
+ {
+ std::string head, tail;
+ split_posix(head, tail, path);
+ return head;
+ }
+
+ std::string dirname(const std::string & path)
+ {
+#ifdef WINDOWS
+ return dirname_nt(path);
+#else
+ return dirname_posix(path);
+#endif
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+
+ // Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
+ std::string normpath_nt(const std::string & p)
+ {
+ std::string path = p;
+ path = pystring::replace(path, "/","\\");
+
+ std::string prefix;
+ splitdrive_nt(prefix, path, path);
+
+ // We need to be careful here. If the prefix is empty, and the path starts
+ // with a backslash, it could either be an absolute path on the current
+ // drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It
+ // is therefore imperative NOT to collapse multiple backslashes blindly in
+ // that case.
+ // The code below preserves multiple backslashes when there is no drive
+ // letter. This means that the invalid filename \\\a\b is preserved
+ // unchanged, where a\\\b is normalised to a\b. It's not clear that there
+ // is any better behaviour for such edge cases.
+
+ if(prefix.empty())
+ {
+ // No drive letter - preserve initial backslashes
+ while(pystring::slice(path,0,1) == "\\")
+ {
+ prefix = prefix + "\\";
+ path = pystring::slice(path,1);
+ }
+ }
+ else
+ {
+ // We have a drive letter - collapse initial backslashes
+ if(pystring::startswith(path, "\\"))
+ {
+ prefix = prefix + "\\";
+ path = pystring::lstrip(path, "\\");
+ }
+ }
+
+ std::vector<std::string> comps;
+ pystring::split(path, comps, "\\");
+
+ int i = 0;
+
+ while(i<(int)comps.size())
+ {
+ if(comps[i].empty() || comps[i] == ".")
+ {
+ comps.erase(comps.begin()+i);
+ }
+ else if(comps[i] == "..")
+ {
+ if(i>0 && comps[i-1] != "..")
+ {
+ comps.erase(comps.begin()+i-1, comps.begin()+i+1);
+ i -= 1;
+ }
+ else if(i == 0 && pystring::endswith(prefix, "\\"))
+ {
+ comps.erase(comps.begin()+i);
+ }
+ else
+ {
+ i += 1;
+ }
+ }
+ else
+ {
+ i += 1;
+ }
+ }
+
+ // If the path is now empty, substitute '.'
+ if(prefix.empty() && comps.empty())
+ {
+ comps.push_back(".");
+ }
+
+ return prefix + pystring::join("\\", comps);
+ }
+
+ // Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
+ // It should be understood that this may change the meaning of the path
+ // if it contains symbolic links!
+ // Normalize path, eliminating double slashes, etc.
+
+ std::string normpath_posix(const std::string & p)
+ {
+ if(p.empty()) return ".";
+
+ std::string path = p;
+
+ int initial_slashes = pystring::startswith(path,"/") ? 1 : 0;
+
+ // POSIX allows one or two initial slashes, but treats three or more
+ // as single slash.
+
+ if (initial_slashes && pystring::startswith(path,"//")
+ && !pystring::startswith(path,"///"))
+ initial_slashes = 2;
+
+ std::vector<std::string> comps, new_comps;
+ pystring::split(path, comps, "/");
+
+ for(unsigned int i=0; i<comps.size(); ++i)
+ {
+ std::string comp = comps[i];
+ if(comp.empty() || comp == ".")
+ continue;
+
+ if( (comp != "..") || ((initial_slashes == 0) && new_comps.empty()) ||
+ (!new_comps.empty() && new_comps[new_comps.size()-1] == ".."))
+ {
+ new_comps.push_back(comp);
+ }
+ else if (!new_comps.empty())
+ {
+ new_comps.pop_back();
+ }
+ }
+
+ path = pystring::join("/", new_comps);
+
+ if (initial_slashes > 0)
+ path = pystring::mul("/",initial_slashes) + path;
+
+ if(path.empty()) return ".";
+ return path;
+ }
+
+ std::string normpath(const std::string & path)
+ {
+#ifdef WINDOWS
+ return normpath_nt(path);
+#else
+ return normpath_posix(path);
+#endif
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ ///
+ ///
+
+ // Split the extension from a pathname.
+ // Extension is everything from the last dot to the end, ignoring
+ // leading dots. Returns "(root, ext)"; ext may be empty.
+ // It is always true that root + ext == p
+
+ void splitext_generic(std::string & root, std::string & ext,
+ const std::string & p,
+ const std::string & sep,
+ const std::string & altsep,
+ const std::string & extsep)
+ {
+ int sepIndex = pystring::rfind(p, sep);
+ if(!altsep.empty())
+ {
+ int altsepIndex = pystring::rfind(p, altsep);
+ sepIndex = std::max(sepIndex, altsepIndex);
+ }
+
+ int dotIndex = pystring::rfind(p, extsep);
+ if(dotIndex > sepIndex)
+ {
+ // Skip all leading dots
+ int filenameIndex = sepIndex + 1;
+
+ while(filenameIndex < dotIndex)
+ {
+ if(pystring::slice(p,filenameIndex) != extsep)
+ {
+ root = pystring::slice(p, 0, dotIndex);
+ ext = pystring::slice(p, dotIndex);
+ return;
+ }
+
+ filenameIndex += 1;
+ }
+ }
+
+ root = p;
+ ext = "";
+ }
+
+ void splitext_nt(std::string & root, std::string & ext, const std::string & path)
+ {
+ return splitext_generic(root, ext, path,
+ "\\", "/", ".");
+ }
+
+ void splitext_posix(std::string & root, std::string & ext, const std::string & path)
+ {
+ return splitext_generic(root, ext, path,
+ "/", "", ".");
+ }
+
+ void splitext(std::string & root, std::string & ext, const std::string & path)
+ {
+#ifdef WINDOWS
+ return splitext_nt(root, ext, path);
+#else
+ return splitext_posix(root, ext, path);
+#endif
+ }
+
+} // namespace path
+} // namespace os
+
+
+}//namespace pystring
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/core/pystring/pystring.h b/src/core/pystring/pystring.h
new file mode 100644
index 0000000..f0729db
--- /dev/null
+++ b/src/core/pystring/pystring.h
@@ -0,0 +1,438 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2008-2010, Sony Pictures Imageworks Inc
+// 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 the organization 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.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDED_OCIO_PYSTRING_H
+#define INCLUDED_OCIO_PYSTRING_H
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include <string>
+#include <vector>
+
+OCIO_NAMESPACE_ENTER
+{
+// Version 1.1.2
+// https://github.com/imageworks/pystring/tarball/v1.1.2
+
+namespace pystring
+{
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @mainpage pystring
+ ///
+ /// This is a set of functions matching the interface and behaviors of python string methods
+ /// (as of python 2.3) using std::string.
+ ///
+ /// Overlapping functionality ( such as index and slice/substr ) of std::string is included
+ /// to match python interfaces.
+ ///
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @defgroup functions pystring
+ /// @{
+
+
+ #define MAX_32BIT_INT 2147483647
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string with only its first character capitalized.
+ ///
+ std::string capitalize( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return centered in a string of length width. Padding is done using spaces.
+ ///
+ std::string center( const std::string & str, int width );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return the number of occurrences of substring sub in string S[start:end]. Optional
+ /// arguments start and end are interpreted as in slice notation.
+ ///
+ int count( const std::string & str, const std::string & substr, int start = 0, int end = MAX_32BIT_INT);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return True if the string ends with the specified suffix, otherwise return False. With
+ /// optional start, test beginning at that position. With optional end, stop comparing at that position.
+ ///
+ bool endswith( const std::string & str, const std::string & suffix, int start = 0, int end = MAX_32BIT_INT );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string where all tab characters are expanded using spaces. If tabsize
+ /// is not given, a tab size of 8 characters is assumed.
+ ///
+ std::string expandtabs( const std::string & str, int tabsize = 8);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return the lowest index in the string where substring sub is found, such that sub is
+ /// contained in the range [start, end). Optional arguments start and end are interpreted as
+ /// in slice notation. Return -1 if sub is not found.
+ ///
+ int find( const std::string & str, const std::string & sub, int start = 0, int end = MAX_32BIT_INT );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Synonym of find right now. Python version throws exceptions. This one currently doesn't
+ ///
+ int index( const std::string & str, const std::string & sub, int start = 0, int end = MAX_32BIT_INT );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return true if all characters in the string are alphanumeric and there is at least one
+ /// character, false otherwise.
+ ///
+ bool isalnum( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return true if all characters in the string are alphabetic and there is at least one
+ /// character, false otherwise
+ ///
+ bool isalpha( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return true if all characters in the string are digits and there is at least one
+ /// character, false otherwise.
+ ///
+ bool isdigit( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return true if all cased characters in the string are lowercase and there is at least one
+ /// cased character, false otherwise.
+ ///
+ bool islower( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return true if there are only whitespace characters in the string and there is at least
+ /// one character, false otherwise.
+ ///
+ bool isspace( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return true if the string is a titlecased string and there is at least one character,
+ /// i.e. uppercase characters may only follow uncased characters and lowercase characters only
+ /// cased ones. Return false otherwise.
+ ///
+ bool istitle( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return true if all cased characters in the string are uppercase and there is at least one
+ /// cased character, false otherwise.
+ ///
+ bool isupper( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a string which is the concatenation of the strings in the sequence seq.
+ /// The separator between elements is the str argument
+ ///
+ std::string join( const std::string & str, const std::vector< std::string > & seq );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return the string left justified in a string of length width. Padding is done using
+ /// spaces. The original string is returned if width is less than str.size().
+ ///
+ std::string ljust( const std::string & str, int width );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string converted to lowercase.
+ ///
+ std::string lower( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string with leading characters removed. If chars is omitted or None,
+ /// whitespace characters are removed. If given and not "", chars must be a string; the
+ /// characters in the string will be stripped from the beginning of the string this method
+ /// is called on (argument "str" ).
+ ///
+ std::string lstrip( const std::string & str, const std::string & chars = "" );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string, concatenated N times, together.
+ /// Corresponds to the __mul__ operator.
+ ///
+ std::string mul( const std::string & str, int n);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Split the string around first occurance of sep.
+ /// Three strings will always placed into result. If sep is found, the strings will
+ /// be the text before sep, sep itself, and the remaining text. If sep is
+ /// not found, the original string will be returned with two empty strings.
+ ///
+ void partition( const std::string & str, const std::string & sep, std::vector< std::string > & result );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string with all occurrences of substring old replaced by new. If
+ /// the optional argument count is given, only the first count occurrences are replaced.
+ ///
+ std::string replace( const std::string & str, const std::string & oldstr, const std::string & newstr, int count = -1);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return the highest index in the string where substring sub is found, such that sub is
+ /// contained within s[start,end]. Optional arguments start and end are interpreted as in
+ /// slice notation. Return -1 on failure.
+ ///
+ int rfind( const std::string & str, const std::string & sub, int start = 0, int end = MAX_32BIT_INT );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Currently a synonym of rfind. The python version raises exceptions. This one currently
+ /// does not
+ ///
+ int rindex( const std::string & str, const std::string & sub, int start = 0, int end = MAX_32BIT_INT );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return the string right justified in a string of length width. Padding is done using
+ /// spaces. The original string is returned if width is less than str.size().
+ ///
+ std::string rjust( const std::string & str, int width);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Split the string around last occurance of sep.
+ /// Three strings will always placed into result. If sep is found, the strings will
+ /// be the text before sep, sep itself, and the remaining text. If sep is
+ /// not found, the original string will be returned with two empty strings.
+ ///
+ void rpartition( const std::string & str, const std::string & sep, std::vector< std::string > & result );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string with trailing characters removed. If chars is "", whitespace
+ /// characters are removed. If not "", the characters in the string will be stripped from the
+ /// end of the string this method is called on.
+ ///
+ std::string rstrip( const std::string & str, const std::string & chars = "" );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Fills the "result" list with the words in the string, using sep as the delimiter string.
+ /// If maxsplit is > -1, at most maxsplit splits are done. If sep is "",
+ /// any whitespace string is a separator.
+ ///
+ void split( const std::string & str, std::vector< std::string > & result, const std::string & sep = "", int maxsplit = -1);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Fills the "result" list with the words in the string, using sep as the delimiter string.
+ /// Does a number of splits starting at the end of the string, the result still has the
+ /// split strings in their original order.
+ /// If maxsplit is > -1, at most maxsplit splits are done. If sep is "",
+ /// any whitespace string is a separator.
+ ///
+ void rsplit( const std::string & str, std::vector< std::string > & result, const std::string & sep = "", int maxsplit = -1);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a list of the lines in the string, breaking at line boundaries. Line breaks
+ /// are not included in the resulting list unless keepends is given and true.
+ ///
+ void splitlines( const std::string & str, std::vector< std::string > & result, bool keepends = false );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return True if string starts with the prefix, otherwise return False. With optional start,
+ /// test string beginning at that position. With optional end, stop comparing string at that
+ /// position
+ ///
+ bool startswith( const std::string & str, const std::string & prefix, int start = 0, int end = MAX_32BIT_INT );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string with leading and trailing characters removed. If chars is "",
+ /// whitespace characters are removed. If given not "", the characters in the string will be
+ /// stripped from the both ends of the string this method is called on.
+ ///
+ std::string strip( const std::string & str, const std::string & chars = "" );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string with uppercase characters converted to lowercase and vice versa.
+ ///
+ std::string swapcase( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a titlecased version of the string: words start with uppercase characters,
+ /// all remaining cased characters are lowercase.
+ ///
+ std::string title( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string where all characters occurring in the optional argument
+ /// deletechars are removed, and the remaining characters have been mapped through the given
+ /// translation table, which must be a string of length 256.
+ ///
+ std::string translate( const std::string & str, const std::string & table, const std::string & deletechars = "");
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a copy of the string converted to uppercase.
+ ///
+ std::string upper( const std::string & str );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return the numeric string left filled with zeros in a string of length width. The original
+ /// string is returned if width is less than str.size().
+ ///
+ std::string zfill( const std::string & str, int width );
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief function matching python's slice functionality.
+ ///
+ std::string slice( const std::string & str, int start = 0, int end = MAX_32BIT_INT);
+
+ ///
+ /// @ }
+ ///
+
+
+namespace os
+{
+namespace path
+{
+ // All of the function below have three versions.
+ // Example:
+ // join(...)
+ // join_nt(...)
+ // join_posix(...)
+ //
+ // The regular function dispatches to the other versions - based on the OS
+ // at compile time - to match the result you'd get from the python
+ // interepreter on the same operating system
+ //
+ // Should you want to 'lock off' to a particular version of the string
+ // manipulation across *all* operating systems, use the version with the
+ // _OS you are interested in. I.e., you can use posix style path joining,
+ // even on Windows, with join_posix.
+ //
+ // The naming, (nt, posix) matches the cpython source implementation.
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @defgroup functions pystring::os::path
+ /// @{
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return the base name of pathname path. This is the second half of the pair returned
+ /// by split(path). Note that the result of this function is different from the Unix basename
+ /// program; where basename for '/foo/bar/' returns 'bar', the basename() function returns an
+ /// empty string ('').
+
+ std::string basename(const std::string & path);
+ std::string basename_nt(const std::string & path);
+ std::string basename_posix(const std::string & path);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return the directory name of pathname path. This is the first half of the pair
+ /// returned by split(path).
+
+ std::string dirname(const std::string & path);
+ std::string dirname_nt(const std::string & path);
+ std::string dirname_posix(const std::string & path);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return True if path is an absolute pathname. On Unix, that means it begins with a
+ /// slash, on Windows that it begins with a (back)slash after chopping off a potential drive
+ /// letter.
+
+ bool isabs(const std::string & path);
+ bool isabs_nt(const std::string & path);
+ bool isabs_posix(const std::string & s);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Return a normalized absolutized version of the pathname path.
+ ///
+ /// NOTE: This differs from the interface of the python equivalent in that it requires you
+ /// to pass in the current working directory as an argument.
+
+ std::string abspath(const std::string & path, const std::string & cwd);
+ std::string abspath_nt(const std::string & path, const std::string & cwd);
+ std::string abspath_posix(const std::string & path, const std::string & cwd);
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Join one or more path components intelligently. If any component is an absolute
+ /// path, all previous components (on Windows, including the previous drive letter, if there
+ /// was one) are thrown away, and joining continues. The return value is the concatenation of
+ /// path1, and optionally path2, etc., with exactly one directory separator (os.sep) inserted
+ /// between components, unless path2 is empty. Note that on Windows, since there is a current
+ /// directory for each drive, os.path.join("c:", "foo") represents a path relative to the
+ /// current directory on drive C: (c:foo), not c:\foo.
+
+ /// This dispatches based on the compilation OS
+ std::string join(const std::string & path1, const std::string & path2);
+ std::string join_nt(const std::string & path1, const std::string & path2);
+ std::string join_posix(const std::string & path1, const std::string & path2);
+
+ std::string join(const std::vector< std::string > & paths);
+ std::string join_nt(const std::vector< std::string > & paths);
+ std::string join_posix(const std::vector< std::string > & paths);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Normalize a pathname. This collapses redundant separators and up-level references
+ /// so that A//B, A/B/, A/./B and A/foo/../B all become A/B. It does not normalize the case
+ /// (use normcase() for that). On Windows, it converts forward slashes to backward slashes.
+ /// It should be understood that this may change the meaning of the path if it contains
+ /// symbolic links!
+
+ std::string normpath(const std::string & path);
+ std::string normpath_nt(const std::string & path);
+ std::string normpath_posix(const std::string & path);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Split the pathname path into a pair, (head, tail) where tail is the last pathname
+ /// component and head is everything leading up to that. The tail part will never contain a
+ /// slash; if path ends in a slash, tail will be empty. If there is no slash in path, head
+ /// will be empty. If path is empty, both head and tail are empty. Trailing slashes are
+ /// stripped from head unless it is the root (one or more slashes only). In all cases,
+ /// join(head, tail) returns a path to the same location as path (but the strings may
+ /// differ).
+
+ void split(std::string & head, std::string & tail, const std::string & path);
+ void split_nt(std::string & head, std::string & tail, const std::string & path);
+ void split_posix(std::string & head, std::string & tail, const std::string & path);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Split the pathname path into a pair (drive, tail) where drive is either a drive
+ /// specification or the empty string. On systems which do not use drive specifications,
+ /// drive will always be the empty string. In all cases, drive + tail will be the same as
+ /// path.
+
+ void splitdrive(std::string & drivespec, std::string & pathspec, const std::string & path);
+ void splitdrive_nt(std::string & drivespec, std::string & pathspec, const std::string & p);
+ void splitdrive_posix(std::string & drivespec, std::string & pathspec, const std::string & path);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ /// @brief Split the pathname path into a pair (root, ext) such that root + ext == path, and
+ /// ext is empty or begins with a period and contains at most one period. Leading periods on
+ /// the basename are ignored; splitext('.cshrc') returns ('.cshrc', '').
+
+ void splitext(std::string & root, std::string & ext, const std::string & path);
+ void splitext_nt(std::string & root, std::string & ext, const std::string & path);
+ void splitext_posix(std::string & root, std::string & ext, const std::string & path);
+
+ ///
+ /// @ }
+ ///
+} // namespace path
+} // namespace os
+
+} // namespace pystring
+}
+OCIO_NAMESPACE_EXIT
+
+#endif