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