summaryrefslogtreecommitdiff
path: root/src/apps/ociobakelut
diff options
context:
space:
mode:
Diffstat (limited to 'src/apps/ociobakelut')
-rw-r--r--src/apps/ociobakelut/CMakeLists.txt46
-rw-r--r--src/apps/ociobakelut/main.cpp537
-rw-r--r--src/apps/ociobakelut/ocioicc.cpp251
-rw-r--r--src/apps/ociobakelut/ocioicc.h71
4 files changed, 905 insertions, 0 deletions
diff --git a/src/apps/ociobakelut/CMakeLists.txt b/src/apps/ociobakelut/CMakeLists.txt
new file mode 100644
index 0000000..c15d156
--- /dev/null
+++ b/src/apps/ociobakelut/CMakeLists.txt
@@ -0,0 +1,46 @@
+# LCMS
+include(FindPkgConfig FindPackageMessage)
+pkg_check_modules(LCMS QUIET lcms2)
+if(LCMS_FOUND AND (LCMS_VERSION VERSION_EQUAL 2.1 OR LCMS_VERSION VERSION_GREATER 2.1))
+ FIND_PACKAGE_MESSAGE(LCMS "Found lcms: ${LCMS_LIBRARIES}"
+ "${LCMS_INCLUDE_DIR}")
+else()
+ message(STATUS "Using bundled lcms.")
+ set(LCMS_VERSION 2.1)
+ set(LCMS_BUNDLED TRUE)
+ ExternalProject_Add(LCMS
+ URL ${CMAKE_SOURCE_DIR}/ext/lcms2-${LCMS_VERSION}.tar.gz
+ BUILD_IN_SOURCE 1
+ CONFIGURE_COMMAND ./configure --prefix=${PROJECT_BINARY_DIR}/ext/dist --without-jpeg --without-tiff --without-zlib
+ BUILD_COMMAND make
+ INSTALL_COMMAND make install
+ )
+ set(LCMS_INCLUDE_DIRS ${PROJECT_BINARY_DIR}/ext/dist/include)
+ set(LCMS_LIBRARY_DIRS ${PROJECT_BINARY_DIR}/ext/dist/lib)
+ set(LCMS_LIBRARIES ${PROJECT_BINARY_DIR}/ext/dist/lib/liblcms2.a)
+endif()
+
+file(GLOB_RECURSE share_src_files "${CMAKE_SOURCE_DIR}/src/apps/share/*.cpp")
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}/export/
+ ${CMAKE_BINARY_DIR}/export/
+ ${CMAKE_SOURCE_DIR}/src/apps/share/
+ ${LCMS_INCLUDE_DIRS}
+)
+
+link_directories(
+ ${LCMS_LIBRARY_DIRS}
+)
+
+add_executable(ociobakelut ${share_src_files} main.cpp ocioicc.cpp)
+
+if(LCMS_BUNDLED)
+ add_dependencies(ociobakelut LCMS)
+endif()
+
+target_link_libraries(ociobakelut
+ ${LCMS_LIBRARIES}
+ OpenColorIO)
+
+install(TARGETS ociobakelut DESTINATION ${CMAKE_INSTALL_EXEC_PREFIX}/bin)
diff --git a/src/apps/ociobakelut/main.cpp b/src/apps/ociobakelut/main.cpp
new file mode 100644
index 0000000..265d464
--- /dev/null
+++ b/src/apps/ociobakelut/main.cpp
@@ -0,0 +1,537 @@
+/*
+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 <cstdlib>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <vector>
+
+#include <OpenColorIO/OpenColorIO.h>
+namespace OCIO = OCIO_NAMESPACE;
+
+#include "argparse.h"
+#include "ocioicc.h"
+
+static std::string outputfile;
+
+static int
+parse_end_args(int argc, const char *argv[])
+{
+ if(argc>0)
+ {
+ outputfile = argv[0];
+ }
+
+ return 0;
+}
+
+OCIO::GroupTransformRcPtr
+parse_luts(int argc, const char *argv[]);
+
+int main (int argc, const char* argv[])
+{
+
+ bool help = false;
+ int cubesize = -1;
+ int shapersize = -1; // cubsize^2
+ std::string format;
+ std::string inputconfig;
+ std::string inputspace;
+ std::string shaperspace;
+ std::string looks;
+ std::string outputspace;
+ bool usestdout = false;
+ bool verbose = false;
+
+ int whitepointtemp = 6505;
+ std::string displayicc;
+ std::string description;
+ std::string copyright = "OpenColorIO (Sony Imageworks)";
+
+ // What are the allowed baker output formats?
+ std::ostringstream formats;
+ formats << "the lut format to bake: ";
+ for(int i=0; i<OCIO::Baker::getNumFormats(); ++i)
+ {
+ if(i!=0) formats << ", ";
+ formats << OCIO::Baker::getFormatNameByIndex(i);
+ formats << " (." << OCIO::Baker::getFormatExtensionByIndex(i) << ")";
+ }
+ formats << ", icc (.icc)";
+
+ std::string formatstr = formats.str();
+
+ std::string dummystr;
+ float dummyf1, dummyf2, dummyf3;
+
+ ArgParse ap;
+ ap.options("ociobakelut -- create a new LUT or icc profile from an OCIO config or lut file(s)\n\n"
+ "usage: ociobakelut [options] <OUTPUTFILE.LUT>\n\n"
+ "example: ociobakelut --inputspace lg10 --outputspace srgb8 --format flame lg_to_srgb.3dl\n"
+ "example: ociobakelut --lut filmlut.3dl --lut calibration.3dl --format flame display.3dl\n"
+ "example: ociobakelut --lut look.3dl --offset 0.01 -0.02 0.03 --lut display.3dl --format flame display_with_look.3dl\n"
+ "example: ociobakelut --inputspace lg10 --outputspace srgb8 --format icc ~/Library/ColorSync/Profiles/test.icc\n"
+ "example: ociobakelut --lut filmlut.3dl --lut calibration.3dl --format icc ~/Library/ColorSync/Profiles/test.icc\n\n",
+ "%*", parse_end_args, "",
+ "<SEPARATOR>", "Using Existing OCIO Configurations",
+ "--inputspace %s", &inputspace, "Input OCIO ColorSpace (or Role)",
+ "--outputspace %s", &outputspace, "Output OCIO ColorSpace (or Role)",
+ "--shaperspace %s", &shaperspace, "the OCIO ColorSpace or Role, for the shaper",
+ "--looks %s", &looks, "the OCIO looks to apply",
+ "--iconfig %s", &inputconfig, "Input .ocio configuration file (default: $OCIO)\n",
+ "<SEPARATOR>", "Config-Free LUT Baking",
+ "<SEPARATOR>", " (all options can be specified multiple times, each is applied in order)",
+ "--lut %s", &dummystr, "Specify a LUT (forward direction)",
+ "--invlut %s", &dummystr, "Specify a LUT (inverse direction)",
+ "--slope %f %f %f", &dummyf1, &dummyf2, &dummyf3, "slope",
+ "--offset %f %f %f", &dummyf1, &dummyf2, &dummyf3, "offset (float)",
+ "--offset10 %f %f %f", &dummyf1, &dummyf2, &dummyf3, "offset (10-bit)",
+ "--power %f %f %f", &dummyf1, &dummyf2, &dummyf3, "power",
+ "--sat %f", &dummyf1, "saturation (ASC-CDL luma coefficients)\n",
+ "<SEPARATOR>", "Baking Options",
+ "--format %s", &format, formatstr.c_str(),
+ "--shapersize %d", &shapersize, "size of the shaper (default: format specific)",
+ "--cubesize %d", &cubesize, "size of the cube (default: format specific)",
+ "--stdout", &usestdout, "Write to stdout (rather than file)",
+ "--v", &verbose, "Verbose",
+ "--help", &help, "Print help message\n",
+ "<SEPARATOR>", "ICC Options",
+ //"--cubesize %d", &cubesize, "size of the icc CLUT cube (default: 32)",
+ "--whitepoint %d", &whitepointtemp, "whitepoint for the profile (default: 6505)",
+ "--displayicc %s", &displayicc , "an icc profile which matches the OCIO profiles target display",
+ "--description %s", &description , "a meaningful description, this will show up in UI like photoshop",
+ "--copyright %s", &copyright , "a copyright field\n",
+ // TODO: add --metadata option
+ NULL);
+
+ if (ap.parse(argc, argv) < 0)
+ {
+ std::cout << ap.geterror() << std::endl;
+ ap.usage();
+ std::cout << "\n";
+ return 1;
+ }
+
+ if (help || (argc == 1 ))
+ {
+ ap.usage();
+ std::cout << "\n";
+ return 1;
+ }
+
+ // If we're printing to stdout, disable verbose printouts
+ if(usestdout)
+ {
+ verbose = false;
+ }
+
+ // Create the OCIO processor for the specified transform.
+ OCIO::ConstConfigRcPtr config;
+
+
+ OCIO::GroupTransformRcPtr groupTransform;
+
+ try
+ {
+ groupTransform = parse_luts(argc, argv);
+ }
+ catch(const OCIO::Exception & e)
+ {
+ std::cerr << "\nERROR: " << e.what() << std::endl;
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+ catch(...)
+ {
+ std::cerr << "\nERROR: An unknown error occurred in parse_luts" << std::endl;
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ if(!groupTransform)
+ {
+ std::cerr << "\nERROR: parse_luts returned null transform" << std::endl;
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ // If --luts have been specified, synthesize a new (temporary) configuration
+ // with the transformation embedded in a colorspace.
+ if(!groupTransform->empty())
+ {
+ if(!inputspace.empty())
+ {
+ std::cerr << "\nERROR: --inputspace is not allowed when using --lut\n\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+ if(!outputspace.empty())
+ {
+ std::cerr << "\nERROR: --outputspace is not allowed when using --lut\n\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+ if(!looks.empty())
+ {
+ std::cerr << "\nERROR: --looks is not allowed when using --lut\n\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+ if(!shaperspace.empty())
+ {
+ std::cerr << "\nERROR: --shaperspace is not allowed when using --lut\n\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ OCIO::ConfigRcPtr editableConfig = OCIO::Config::Create();
+
+ OCIO::ColorSpaceRcPtr inputColorSpace = OCIO::ColorSpace::Create();
+ inputspace = "RawInput";
+ inputColorSpace->setName(inputspace.c_str());
+ editableConfig->addColorSpace(inputColorSpace);
+
+ OCIO::ColorSpaceRcPtr outputColorSpace = OCIO::ColorSpace::Create();
+ outputspace = "ProcessedOutput";
+ outputColorSpace->setName(outputspace.c_str());
+
+ outputColorSpace->setTransform(groupTransform,
+ OCIO::COLORSPACE_DIR_FROM_REFERENCE);
+
+ if(verbose)
+ {
+ std::cout << "[OpenColorIO DEBUG]: Specified Transform:";
+ std::cout << *groupTransform;
+ std::cout << "\n";
+ }
+
+ editableConfig->addColorSpace(outputColorSpace);
+ config = editableConfig;
+ }
+ else
+ {
+
+ if(inputspace.empty())
+ {
+ std::cerr << "\nERROR: You must specify the --inputspace.\n\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ if(outputspace.empty())
+ {
+ std::cerr << "\nERROR: You must specify the --outputspace.\n\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ if(format.empty())
+ {
+ std::cerr << "\nERROR: You must specify the lut format using --format.\n\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ if(!inputconfig.empty())
+ {
+ if(!usestdout && verbose)
+ std::cout << "[OpenColorIO INFO]: Loading " << inputconfig << std::endl;
+ config = OCIO::Config::CreateFromFile(inputconfig.c_str());
+ }
+ else if(getenv("OCIO"))
+ {
+ if(!usestdout && verbose)
+ std::cout << "[OpenColorIO INFO]: Loading $OCIO " << getenv("OCIO") << std::endl;
+ config = OCIO::Config::CreateFromEnv();
+ }
+ else
+ {
+ std::cerr << "ERROR: You must specify an input ocio configuration ";
+ std::cerr << "(either with --iconfig or $OCIO).\n\n";
+ ap.usage ();
+ return 1;
+ }
+ }
+
+ if(outputfile.empty() && !usestdout)
+ {
+ std::cerr << "\nERROR: You must specify the outputfile or --stdout.\n\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ try
+ {
+ if(format == "icc")
+ {
+ if(usestdout)
+ {
+ std::cerr << "\nERROR: --stdout not supported when writing icc profiles.\n\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ if(outputfile.empty())
+ {
+ std::cerr << "ERROR: you need to specify a output icc path\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ if(copyright.empty())
+ {
+ std::cerr << "ERROR: need to specify a --copyright to embed in the icc profile\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ if(cubesize<2) cubesize = 32; // default
+
+ OCIO::ConstProcessorRcPtr processor;
+ if (!looks.empty())
+ {
+ OCIO::LookTransformRcPtr transform =
+ OCIO::LookTransform::Create();
+ transform->setLooks(looks.c_str());
+ transform->setSrc(inputspace.c_str());
+ transform->setDst(outputspace.c_str());
+ processor = config->getProcessor(transform,
+ OCIO::TRANSFORM_DIR_FORWARD);
+ }
+ else
+ {
+ processor = config->getProcessor(inputspace.c_str(),
+ outputspace.c_str());
+ }
+
+ SaveICCProfileToFile(outputfile,
+ processor,
+ cubesize,
+ whitepointtemp,
+ displayicc,
+ description,
+ copyright,
+ verbose);
+ }
+ else
+ {
+
+ OCIO::BakerRcPtr baker = OCIO::Baker::Create();
+
+ // setup the baker for our lut type
+ baker->setConfig(config);
+ baker->setFormat(format.c_str());
+ baker->setInputSpace(inputspace.c_str());
+ baker->setShaperSpace(shaperspace.c_str());
+ baker->setLooks(looks.c_str());
+ baker->setTargetSpace(outputspace.c_str());
+ if(shapersize!=-1) baker->setShaperSize(shapersize);
+ if(cubesize!=-1) baker->setCubeSize(cubesize);
+
+ // output lut
+ std::ostringstream output;
+
+ if(!usestdout && verbose)
+ std::cout << "[OpenColorIO INFO]: Baking '" << format << "' lut" << std::endl;
+
+ if(usestdout)
+ {
+ baker->bake(std::cout);
+ }
+ else
+ {
+ std::ofstream f(outputfile.c_str());
+ baker->bake(f);
+ if(verbose)
+ std::cout << "[OpenColorIO INFO]: Wrote '" << outputfile << "'" << std::endl;
+ }
+ }
+ }
+ catch(OCIO::Exception & exception)
+ {
+ std::cerr << "OCIO Error: " << exception.what() << std::endl;
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+ catch (std::exception& exception)
+ {
+ std::cerr << "Error: " << exception.what() << "\n";
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+ catch(...)
+ {
+ std::cerr << "Unknown OCIO error encountered." << std::endl;
+ std::cerr << "See --help for more info." << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+// TODO: Replace this dirty argument parsing code with a clean version
+// that leverages the same codepath for the standard arguments. If
+// the OIIO derived argparse does not suffice, options we may want to consider
+// include simpleopt, tclap, ultraopt
+
+// TODO: Use better input validation, instead of atof.
+// If too few arguments are provides for scale (let's say only two) and
+// the following argument is the start of another flag (let's say "--invlut")
+// then atof() will likely try to convert "--invlut" to its double equivalent,
+// resulting in an invalid (or at least undesired) scale value.
+
+OCIO::GroupTransformRcPtr
+parse_luts(int argc, const char *argv[])
+{
+ OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create();
+
+ for(int i=0; i<argc; ++i)
+ {
+ std::string arg(argv[i]);
+
+ if(arg == "--lut" || arg == "-lut")
+ {
+ if(i+1>=argc)
+ {
+ throw OCIO::Exception("Error parsing --lut. Invalid num args");
+ }
+
+ OCIO::FileTransformRcPtr t = OCIO::FileTransform::Create();
+ t->setSrc(argv[i+1]);
+ t->setInterpolation(OCIO::INTERP_BEST);
+ groupTransform->push_back(t);
+
+ i += 1;
+ }
+ else if(arg == "--invlut" || arg == "-invlut")
+ {
+ if(i+1>=argc)
+ {
+ throw OCIO::Exception("Error parsing --invlut. Invalid num args");
+ }
+
+ OCIO::FileTransformRcPtr t = OCIO::FileTransform::Create();
+ t->setSrc(argv[i+1]);
+ t->setInterpolation(OCIO::INTERP_BEST);
+ t->setDirection(OCIO::TRANSFORM_DIR_INVERSE);
+ groupTransform->push_back(t);
+
+ i += 1;
+ }
+ else if(arg == "--slope" || arg == "-slope")
+ {
+ if(i+3>=argc)
+ {
+ throw OCIO::Exception("Error parsing --slope. Invalid num args");
+ }
+
+ OCIO::CDLTransformRcPtr t = OCIO::CDLTransform::Create();
+
+ float scale[3];
+ scale[0] = (float) atof(argv[i+1]);
+ scale[1] = (float) atof(argv[i+2]);
+ scale[2] = (float) atof(argv[i+3]);
+ t->setSlope(scale);
+ groupTransform->push_back(t);
+
+ i += 3;
+ }
+ else if(arg == "--offset" || arg == "-offset")
+ {
+ if(i+3>=argc)
+ {
+ throw OCIO::Exception("Error parsing --offset. Invalid num args");
+ }
+
+ OCIO::CDLTransformRcPtr t = OCIO::CDLTransform::Create();
+
+ float offset[3];
+ offset[0] = (float) atof(argv[i+1]);
+ offset[1] = (float) atof(argv[i+2]);
+ offset[2] = (float) atof(argv[i+3]);
+ t->setOffset(offset);
+ groupTransform->push_back(t);
+
+ i += 3;
+ }
+ else if(arg == "--offset10" || arg == "-offset10")
+ {
+ if(i+3>=argc)
+ {
+ throw OCIO::Exception("Error parsing --offset10. Invalid num args");
+ }
+
+ OCIO::CDLTransformRcPtr t = OCIO::CDLTransform::Create();
+
+ float offset[3];
+ offset[0] = (float) atof(argv[i+1]) / 1023.0f;
+ offset[1] = (float) atof(argv[i+2]) / 1023.0f;
+ offset[2] = (float) atof(argv[i+3]) / 1023.0f;
+ t->setOffset(offset);
+ groupTransform->push_back(t);
+ i += 3;
+ }
+ else if(arg == "--power" || arg == "-power")
+ {
+ if(i+3>=argc)
+ {
+ throw OCIO::Exception("Error parsing --power. Invalid num args");
+ }
+
+ OCIO::CDLTransformRcPtr t = OCIO::CDLTransform::Create();
+
+ float power[3];
+ power[0] = (float) atof(argv[i+1]);
+ power[1] = (float) atof(argv[i+2]);
+ power[2] = (float) atof(argv[i+3]);
+ t->setPower(power);
+ groupTransform->push_back(t);
+
+ i += 3;
+ }
+ else if(arg == "--sat" || arg == "-sat")
+ {
+ if(i+1>=argc)
+ {
+ throw OCIO::Exception("Error parsing --sat. Invalid num args");
+ }
+
+ OCIO::CDLTransformRcPtr t = OCIO::CDLTransform::Create();
+ t->setSat((float) atof(argv[i+1]));
+ groupTransform->push_back(t);
+
+ i += 1;
+ }
+ }
+
+ return groupTransform;
+}
+
diff --git a/src/apps/ociobakelut/ocioicc.cpp b/src/apps/ociobakelut/ocioicc.cpp
new file mode 100644
index 0000000..2975f4a
--- /dev/null
+++ b/src/apps/ociobakelut/ocioicc.cpp
@@ -0,0 +1,251 @@
+/*
+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 <cstdlib>
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+#include <OpenColorIO/OpenColorIO.h>
+
+#include "ocioicc.h"
+
+#include "lcms2.h"
+#include "lcms2_plugin.h"
+
+OCIO_NAMESPACE_ENTER
+{
+
+
+namespace
+{
+void ErrorHandler(cmsContext /*ContextID*/, cmsUInt32Number /*ErrorCode*/, const char *Text)
+{
+ std::cerr << "OCIO Error: " << Text << "\n";
+ return;
+}
+
+typedef struct
+{
+ cmsHTRANSFORM to_PCS16;
+ cmsHTRANSFORM from_PCS16;
+ //OCIO::ConstProcessorRcPtr shaper_processor;
+ OCIO::ConstProcessorRcPtr processor;
+} SamplerData;
+
+static void Add3GammaCurves(cmsPipeline* lut, cmsFloat64Number Curve)
+{
+ cmsToneCurve* id = cmsBuildGamma(NULL, Curve);
+ cmsToneCurve* id3[3];
+ id3[0] = id;
+ id3[1] = id;
+ id3[2] = id;
+ cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(NULL, 3, id3));
+ cmsFreeToneCurve(id);
+}
+
+static void AddIdentityMatrix(cmsPipeline* lut)
+{
+ const cmsFloat64Number Identity[] = {
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1,
+ 0, 0, 0 };
+ cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocMatrix(NULL, 3, 3, Identity, NULL));
+}
+
+static cmsInt32Number Display2PCS_Sampler16(const cmsUInt16Number in[], cmsUInt16Number out[], void* userdata)
+{
+ //std::cout << "r" << in[0] << " g" << in[1] << " b" << in[2] << "\n";
+ SamplerData* data = (SamplerData*) userdata;
+ cmsFloat32Number pix[3] = { static_cast<float>(in[0])/65535.f,
+ static_cast<float>(in[1])/65535.f,
+ static_cast<float>(in[2])/65535.f};
+ data->processor->applyRGB(pix);
+ out[0] = (cmsUInt16Number)std::max(std::min(pix[0] * 65535.f, 65535.f), 0.f);
+ out[1] = (cmsUInt16Number)std::max(std::min(pix[1] * 65535.f, 65535.f), 0.f);
+ out[2] = (cmsUInt16Number)std::max(std::min(pix[2] * 65535.f, 65535.f), 0.f);
+ cmsDoTransform(data->to_PCS16, out, out, 1);
+ return 1;
+}
+
+static cmsInt32Number PCS2Display_Sampler16(const cmsUInt16Number in[], cmsUInt16Number out[], void* userdata)
+{
+ //std::cout << "r" << in[0] << " g" << in[1] << " b" << in[2] << "\n";
+ SamplerData* data = (SamplerData*) userdata;
+ cmsDoTransform(data->from_PCS16, in, out, 1);
+ // we don't have a reverse Lab -> Display transform
+ return 1;
+}
+} // anon namespace
+
+
+void SaveICCProfileToFile(const std::string & outputfile,
+ ConstProcessorRcPtr & processor,
+ int cubesize,
+ int whitepointtemp,
+ const std::string & displayicc,
+ const std::string & description,
+ const std::string & copyright,
+ bool verbose)
+{
+
+ // Create the ICC Profile
+
+ // Setup the Error Handler
+ cmsSetLogErrorHandler(ErrorHandler);
+
+ // D65 white point
+ cmsCIExyY whitePoint;
+ cmsWhitePointFromTemp(&whitePoint, whitepointtemp);
+
+ // LAB PCS
+ cmsHPROFILE labProfile = cmsCreateLab4ProfileTHR(NULL, &whitePoint);
+
+ // Display (OCIO sRGB cube -> LAB)
+ cmsHPROFILE DisplayProfile;
+ if(displayicc != "") DisplayProfile = cmsOpenProfileFromFile(displayicc.c_str(), "r");
+ else DisplayProfile = cmsCreate_sRGBProfileTHR(NULL);
+
+ // Create an empty RGB Profile
+ cmsHPROFILE hProfile = cmsCreateRGBProfileTHR(NULL, &whitePoint, NULL, NULL);
+
+ if(verbose)
+ std::cout << "[OpenColorIO INFO]: Setting up Profile: " << outputfile << "\n";
+
+ // Added Header fields
+ cmsSetProfileVersion(hProfile, 4.2);
+ cmsSetDeviceClass(hProfile, cmsSigDisplayClass);
+ cmsSetColorSpace(hProfile, cmsSigRgbData);
+ cmsSetPCS(hProfile, cmsSigLabData);
+ cmsSetHeaderRenderingIntent(hProfile, INTENT_PERCEPTUAL);
+
+ //
+ cmsMLU* DescriptionMLU = cmsMLUalloc(NULL, 1);
+ cmsMLU* CopyrightMLU = cmsMLUalloc(NULL, 1);
+ cmsMLUsetASCII(DescriptionMLU, "en", "US", description.c_str());
+ cmsMLUsetASCII(CopyrightMLU, "en", "US", copyright.c_str());
+ cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU);
+ cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU);
+
+ //
+ SamplerData data;
+ data.processor = processor;
+
+ // 16Bit
+ data.to_PCS16 = cmsCreateTransform(DisplayProfile, TYPE_RGB_16, labProfile, TYPE_LabV2_16,
+ INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
+ data.from_PCS16 = cmsCreateTransform(labProfile, TYPE_LabV2_16, DisplayProfile, TYPE_RGB_16,
+ INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
+
+ //
+ // AToB0Tag - Device to PCS (16-bit) intent of 0 (perceptual)
+ //
+ // cmsSigCurveSetElemType
+ // `- cmsSigCLutElemType
+ // `- cmsSigCurveSetElemType
+ // `- cmsSigMatrixElemType
+ // `- cmsSigCurveSetElemType
+ //
+
+ if(verbose)
+ std::cout << "[OpenColorIO INFO]: Adding AToB0Tag\n";
+ cmsPipeline* AToB0Tag = cmsPipelineAlloc(NULL, 3, 3);
+
+ Add3GammaCurves(AToB0Tag, 1.f); // cmsSigCurveSetElemType
+
+ // cmsSigCLutElemType
+ cmsStage* AToB0Clut = cmsStageAllocCLut16bit(NULL, cubesize, 3, 3, NULL);
+
+ if(verbose)
+ std::cout << "[OpenColorIO INFO]: Sampling AToB0 CLUT from Display to Lab\n";
+ cmsStageSampleCLut16bit(AToB0Clut, Display2PCS_Sampler16, &data, 0);
+ cmsPipelineInsertStage(AToB0Tag, cmsAT_END, AToB0Clut);
+
+ Add3GammaCurves(AToB0Tag, 1.f); // cmsSigCurveSetElemType
+ AddIdentityMatrix(AToB0Tag); // cmsSigMatrixElemType
+ Add3GammaCurves(AToB0Tag, 1.f); // cmsSigCurveSetElemType
+
+ // Add AToB0Tag
+ cmsWriteTag(hProfile, cmsSigAToB0Tag, AToB0Tag);
+ cmsPipelineFree(AToB0Tag);
+
+ //
+ // BToA0Tag - PCS to Device space (16-bit) intent of 0 (perceptual)
+ //
+ // cmsSigCurveSetElemType
+ // `- cmsSigMatrixElemType
+ // `- cmsSigCurveSetElemType
+ // `- cmsSigCLutElemType
+ // `- cmsSigCurveSetElemType
+ //
+ if(verbose)
+ std::cout << "[OpenColorIO INFO]: Adding BToA0Tag\n";
+ cmsPipeline* BToA0Tag = cmsPipelineAlloc(NULL, 3, 3);
+
+ Add3GammaCurves(BToA0Tag, 1.f); // cmsSigCurveSetElemType
+ AddIdentityMatrix(BToA0Tag); // cmsSigMatrixElemType
+ Add3GammaCurves(BToA0Tag, 1.f); // cmsSigCurveSetElemType
+
+ // cmsSigCLutElemType
+ cmsStage* BToA0Clut = cmsStageAllocCLut16bit(NULL, cubesize, 3, 3, NULL);
+ if(verbose)
+ std::cout << "[OpenColorIO INFO]: Sampling BToA0 CLUT from Lab to Display\n";
+ cmsStageSampleCLut16bit(BToA0Clut, PCS2Display_Sampler16, &data, 0);
+ cmsPipelineInsertStage(BToA0Tag, cmsAT_END, BToA0Clut);
+
+ Add3GammaCurves(BToA0Tag, 1.f); // cmsSigCurveSetElemType
+
+ // Add BToA0Tag
+ cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA0Tag);
+ cmsPipelineFree(BToA0Tag);
+
+ //
+ // D2Bx - Device to PCS (float) (Not Yet Impl)
+ //
+
+ //
+ // B2Dx - PCS to Device (float) (Not Yet Impl)
+ //
+
+ //
+ // Write
+ //
+ if(verbose)
+ std::cout << "[OpenColorIO INFO]: Writing " << outputfile << std::endl;
+ cmsSaveProfileToFile(hProfile, outputfile.c_str());
+ cmsCloseProfile(hProfile);
+
+ if(verbose)
+ std::cout << "[OpenColorIO INFO]: Finished\n";
+}
+
+}
+OCIO_NAMESPACE_EXIT
diff --git a/src/apps/ociobakelut/ocioicc.h b/src/apps/ociobakelut/ocioicc.h
new file mode 100644
index 0000000..030fbe3
--- /dev/null
+++ b/src/apps/ociobakelut/ocioicc.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_OCIOICC_H
+#define INCLUDED_OCIO_OCIOICC_H
+
+#include <string>
+
+#include <OpenColorIO/OpenColorIO.h>
+namespace OCIO = OCIO_NAMESPACE;
+
+OCIO_NAMESPACE_ENTER
+{
+
+//
+// Build an ICC profile for doing soft proofing
+//
+// N-component LUT-based display profile required tags
+// ------------------------------------------------------------------------------
+// Tag Name General Description
+// ------------------------------------------------------------------------------
+// profileDescriptionTag Structure containing invariant and localizable
+// versions of the profile name for display
+// AToB0Tag Device to PCS: 8-bit or 16-bit data: intent of 0
+// BToA0Tag PCS to Device space: 8-bit or 16-bit data: intent of 0
+// mediaWhitePointTag Media XYZ white point
+// copyrightTag Profile copyright information
+// chromaticAdaptationTag Converts XYZ colour from the actual illumination
+// source to PCS illuminant. Required only if the actual
+// illumination source is not D50.
+
+void SaveICCProfileToFile(const std::string & outputfile,
+ ConstProcessorRcPtr & processor,
+ int cubesize,
+ int whitepointtemp,
+ const std::string & displayicc,
+ const std::string & description,
+ const std::string & copyright,
+ bool verbose);
+
+}
+OCIO_NAMESPACE_EXIT
+
+#endif
+