/src/core/FileFormatHDL.cpp
C++ | 1481 lines | 1081 code | 204 blank | 196 comment | 148 complexity | 3a21d222f96fc2aa4a87335dbfd052f5 MD5 | raw file
Possible License(s): BSD-3-Clause
- /*
- 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