1 /* 2 Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al. 3 All Rights Reserved. 4 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions are 7 met: 8 * Redistributions of source code must retain the above copyright 9 notice, this list of conditions and the following disclaimer. 10 * Redistributions in binary form must reproduce the above copyright 11 notice, this list of conditions and the following disclaimer in the 12 documentation and/or other materials provided with the distribution. 13 * Neither the name of Sony Pictures Imageworks nor the names of its 14 contributors may be used to endorse or promote products derived from 15 this software without specific prior written permission. 16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <cmath> 30 #include <cstring> 31 #include <cstdlib> 32 #include <iostream> 33 #include <sstream> 34 #include <vector> 35 #include <algorithm> 36 37 #include <OpenColorIO/OpenColorIO.h> 38 39 #include "ocioicc.h" 40 41 #include "lcms2.h" 42 #include "lcms2_plugin.h" 43 44 OCIO_NAMESPACE_ENTER 45 { 46 47 48 namespace 49 { 50 void ErrorHandler(cmsContext /*ContextID*/, cmsUInt32Number /*ErrorCode*/, const char *Text) 51 { 52 std::cerr << "OCIO Error: " << Text << "\n"; 53 return; 54 } 55 56 typedef struct 57 { 58 cmsHTRANSFORM to_PCS16; 59 cmsHTRANSFORM from_PCS16; 60 //OCIO::ConstProcessorRcPtr shaper_processor; 61 OCIO::ConstProcessorRcPtr processor; 62 } SamplerData; 63 64 static void Add3GammaCurves(cmsPipeline* lut, cmsFloat64Number Curve) 65 { 66 cmsToneCurve* id = cmsBuildGamma(NULL, Curve); 67 cmsToneCurve* id3[3]; 68 id3[0] = id; 69 id3[1] = id; 70 id3[2] = id; 71 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(NULL, 3, id3)); 72 cmsFreeToneCurve(id); 73 } 74 75 static void AddIdentityMatrix(cmsPipeline* lut) 76 { 77 const cmsFloat64Number Identity[] = { 78 1, 0, 0, 79 0, 1, 0, 80 0, 0, 1, 81 0, 0, 0 }; 82 cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocMatrix(NULL, 3, 3, Identity, NULL)); 83 } 84 85 static cmsInt32Number Display2PCS_Sampler16(const cmsUInt16Number in[], cmsUInt16Number out[], void* userdata) 86 { 87 //std::cout << "r" << in[0] << " g" << in[1] << " b" << in[2] << "\n"; 88 SamplerData* data = (SamplerData*) userdata; 89 cmsFloat32Number pix[3] = { static_cast<float>(in[0])/65535.f, 90 static_cast<float>(in[1])/65535.f, 91 static_cast<float>(in[2])/65535.f}; 92 data->processor->applyRGB(pix); 93 out[0] = (cmsUInt16Number)std::max(std::min(pix[0] * 65535.f, 65535.f), 0.f); 94 out[1] = (cmsUInt16Number)std::max(std::min(pix[1] * 65535.f, 65535.f), 0.f); 95 out[2] = (cmsUInt16Number)std::max(std::min(pix[2] * 65535.f, 65535.f), 0.f); 96 cmsDoTransform(data->to_PCS16, out, out, 1); 97 return 1; 98 } 99 100 static cmsInt32Number PCS2Display_Sampler16(const cmsUInt16Number in[], cmsUInt16Number out[], void* userdata) 101 { 102 //std::cout << "r" << in[0] << " g" << in[1] << " b" << in[2] << "\n"; 103 SamplerData* data = (SamplerData*) userdata; 104 cmsDoTransform(data->from_PCS16, in, out, 1); 105 // we don't have a reverse Lab -> Display transform 106 return 1; 107 } 108 } // anon namespace 109 110 111 void SaveICCProfileToFile(const std::string & outputfile, 112 ConstProcessorRcPtr & processor, 113 int cubesize, 114 int whitepointtemp, 115 const std::string & displayicc, 116 const std::string & description, 117 const std::string & copyright, 118 bool verbose) 119 { 120 121 // Create the ICC Profile 122 123 // Setup the Error Handler 124 cmsSetLogErrorHandler(ErrorHandler); 125 126 // D65 white point 127 cmsCIExyY whitePoint; 128 cmsWhitePointFromTemp(&whitePoint, whitepointtemp); 129 130 // LAB PCS 131 cmsHPROFILE labProfile = cmsCreateLab4ProfileTHR(NULL, &whitePoint); 132 133 // Display (OCIO sRGB cube -> LAB) 134 cmsHPROFILE DisplayProfile; 135 if(displayicc != "") DisplayProfile = cmsOpenProfileFromFile(displayicc.c_str(), "r"); 136 else DisplayProfile = cmsCreate_sRGBProfileTHR(NULL); 137 138 // Create an empty RGB Profile 139 cmsHPROFILE hProfile = cmsCreateRGBProfileTHR(NULL, &whitePoint, NULL, NULL); 140 141 if(verbose) 142 std::cout << "[OpenColorIO INFO]: Setting up Profile: " << outputfile << "\n"; 143 144 // Added Header fields 145 cmsSetProfileVersion(hProfile, 4.2); 146 cmsSetDeviceClass(hProfile, cmsSigDisplayClass); 147 cmsSetColorSpace(hProfile, cmsSigRgbData); 148 cmsSetPCS(hProfile, cmsSigLabData); 149 cmsSetHeaderRenderingIntent(hProfile, INTENT_PERCEPTUAL); 150 151 // 152 cmsMLU* DescriptionMLU = cmsMLUalloc(NULL, 1); 153 cmsMLU* CopyrightMLU = cmsMLUalloc(NULL, 1); 154 cmsMLUsetASCII(DescriptionMLU, "en", "US", description.c_str()); 155 cmsMLUsetASCII(CopyrightMLU, "en", "US", copyright.c_str()); 156 cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU); 157 cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU); 158 159 // 160 SamplerData data; 161 data.processor = processor; 162 163 // 16Bit 164 data.to_PCS16 = cmsCreateTransform(DisplayProfile, TYPE_RGB_16, labProfile, TYPE_LabV2_16, 165 INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); 166 data.from_PCS16 = cmsCreateTransform(labProfile, TYPE_LabV2_16, DisplayProfile, TYPE_RGB_16, 167 INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); 168 169 // 170 // AToB0Tag - Device to PCS (16-bit) intent of 0 (perceptual) 171 // 172 // cmsSigCurveSetElemType 173 // `- cmsSigCLutElemType 174 // `- cmsSigCurveSetElemType 175 // `- cmsSigMatrixElemType 176 // `- cmsSigCurveSetElemType 177 // 178 179 if(verbose) 180 std::cout << "[OpenColorIO INFO]: Adding AToB0Tag\n"; 181 cmsPipeline* AToB0Tag = cmsPipelineAlloc(NULL, 3, 3); 182 183 Add3GammaCurves(AToB0Tag, 1.f); // cmsSigCurveSetElemType 184 185 // cmsSigCLutElemType 186 cmsStage* AToB0Clut = cmsStageAllocCLut16bit(NULL, cubesize, 3, 3, NULL); 187 188 if(verbose) 189 std::cout << "[OpenColorIO INFO]: Sampling AToB0 CLUT from Display to Lab\n"; 190 cmsStageSampleCLut16bit(AToB0Clut, Display2PCS_Sampler16, &data, 0); 191 cmsPipelineInsertStage(AToB0Tag, cmsAT_END, AToB0Clut); 192 193 Add3GammaCurves(AToB0Tag, 1.f); // cmsSigCurveSetElemType 194 AddIdentityMatrix(AToB0Tag); // cmsSigMatrixElemType 195 Add3GammaCurves(AToB0Tag, 1.f); // cmsSigCurveSetElemType 196 197 // Add AToB0Tag 198 cmsWriteTag(hProfile, cmsSigAToB0Tag, AToB0Tag); 199 cmsPipelineFree(AToB0Tag); 200 201 // 202 // BToA0Tag - PCS to Device space (16-bit) intent of 0 (perceptual) 203 // 204 // cmsSigCurveSetElemType 205 // `- cmsSigMatrixElemType 206 // `- cmsSigCurveSetElemType 207 // `- cmsSigCLutElemType 208 // `- cmsSigCurveSetElemType 209 // 210 if(verbose) 211 std::cout << "[OpenColorIO INFO]: Adding BToA0Tag\n"; 212 cmsPipeline* BToA0Tag = cmsPipelineAlloc(NULL, 3, 3); 213 214 Add3GammaCurves(BToA0Tag, 1.f); // cmsSigCurveSetElemType 215 AddIdentityMatrix(BToA0Tag); // cmsSigMatrixElemType 216 Add3GammaCurves(BToA0Tag, 1.f); // cmsSigCurveSetElemType 217 218 // cmsSigCLutElemType 219 cmsStage* BToA0Clut = cmsStageAllocCLut16bit(NULL, cubesize, 3, 3, NULL); 220 if(verbose) 221 std::cout << "[OpenColorIO INFO]: Sampling BToA0 CLUT from Lab to Display\n"; 222 cmsStageSampleCLut16bit(BToA0Clut, PCS2Display_Sampler16, &data, 0); 223 cmsPipelineInsertStage(BToA0Tag, cmsAT_END, BToA0Clut); 224 225 Add3GammaCurves(BToA0Tag, 1.f); // cmsSigCurveSetElemType 226 227 // Add BToA0Tag 228 cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA0Tag); 229 cmsPipelineFree(BToA0Tag); 230 231 // 232 // D2Bx - Device to PCS (float) (Not Yet Impl) 233 // 234 235 // 236 // B2Dx - PCS to Device (float) (Not Yet Impl) 237 // 238 239 // 240 // Write 241 // 242 if(verbose) 243 std::cout << "[OpenColorIO INFO]: Writing " << outputfile << std::endl; 244 cmsSaveProfileToFile(hProfile, outputfile.c_str()); 245 cmsCloseProfile(hProfile); 246 247 if(verbose) 248 std::cout << "[OpenColorIO INFO]: Finished\n"; 249 } 250 251 } 252 OCIO_NAMESPACE_EXIT 253