1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * This code is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 only, as
6  * published by the Free Software Foundation.  Oracle designates this
7  * particular file as subject to the "Classpath" exception as provided
8  * by Oracle in the LICENSE file that accompanied this code.
9  *
10  * This code is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * version 2 for more details (a copy is included in the LICENSE file that
14  * accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License version
17  * 2 along with this work; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21  * or visit www.oracle.com if you need additional information or have any
22  * questions.
23  */
24 
25 // This file is available under and governed by the GNU General Public
26 // License version 2 only, as published by the Free Software Foundation.
27 // However, the following notice accompanied the original version of this
28 // file:
29 //
30 //---------------------------------------------------------------------------------
31 //
32 //  Little Color Management System
33 //  Copyright (c) 1998-2020 Marti Maria Saguer
34 //
35 // Permission is hereby granted, free of charge, to any person obtaining
36 // a copy of this software and associated documentation files (the "Software"),
37 // to deal in the Software without restriction, including without limitation
38 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
39 // and/or sell copies of the Software, and to permit persons to whom the Software
40 // is furnished to do so, subject to the following conditions:
41 //
42 // The above copyright notice and this permission notice shall be included in
43 // all copies or substantial portions of the Software.
44 //
45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 //
53 //---------------------------------------------------------------------------------
54 //
55 
56 #include "lcms2_internal.h"
57 
58 // Virtual (built-in) profiles
59 // -----------------------------------------------------------------------------------
60 
61 static
SetTextTags(cmsHPROFILE hProfile,const wchar_t * Description)62 cmsBool SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description)
63 {
64     cmsMLU *DescriptionMLU, *CopyrightMLU;
65     cmsBool  rc = FALSE;
66     cmsContext ContextID = cmsGetProfileContextID(hProfile);
67 
68     DescriptionMLU  = cmsMLUalloc(ContextID, 1);
69     CopyrightMLU    = cmsMLUalloc(ContextID, 1);
70 
71     if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error;
72 
73     if (!cmsMLUsetWide(DescriptionMLU,  "en", "US", Description)) goto Error;
74     if (!cmsMLUsetWide(CopyrightMLU,    "en", "US", L"No copyright, use freely")) goto Error;
75 
76     if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag,  DescriptionMLU)) goto Error;
77     if (!cmsWriteTag(hProfile, cmsSigCopyrightTag,           CopyrightMLU)) goto Error;
78 
79     rc = TRUE;
80 
81 Error:
82 
83     if (DescriptionMLU)
84         cmsMLUfree(DescriptionMLU);
85     if (CopyrightMLU)
86         cmsMLUfree(CopyrightMLU);
87     return rc;
88 }
89 
90 
91 static
SetSeqDescTag(cmsHPROFILE hProfile,const char * Model)92 cmsBool  SetSeqDescTag(cmsHPROFILE hProfile, const char* Model)
93 {
94     cmsBool  rc = FALSE;
95     cmsContext ContextID = cmsGetProfileContextID(hProfile);
96     cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1);
97 
98     if (Seq == NULL) return FALSE;
99 
100     Seq->seq[0].deviceMfg = (cmsSignature) 0;
101     Seq->seq[0].deviceModel = (cmsSignature) 0;
102 
103 #ifdef CMS_DONT_USE_INT64
104     Seq->seq[0].attributes[0] = 0;
105     Seq->seq[0].attributes[1] = 0;
106 #else
107     Seq->seq[0].attributes = 0;
108 #endif
109 
110     Seq->seq[0].technology = (cmsTechnologySignature) 0;
111 
112     cmsMLUsetASCII( Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS");
113     cmsMLUsetASCII( Seq->seq[0].Model,        cmsNoLanguage, cmsNoCountry, Model);
114 
115     if (!_cmsWriteProfileSequence(hProfile, Seq)) goto Error;
116 
117     rc = TRUE;
118 
119 Error:
120     if (Seq)
121         cmsFreeProfileSequenceDescription(Seq);
122 
123     return rc;
124 }
125 
126 
127 
128 // This function creates a profile based on White point, primaries and
129 // transfer functions.
cmsCreateRGBProfileTHR(cmsContext ContextID,const cmsCIExyY * WhitePoint,const cmsCIExyYTRIPLE * Primaries,cmsToneCurve * const TransferFunction[3])130 cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID,
131                                           const cmsCIExyY* WhitePoint,
132                                           const cmsCIExyYTRIPLE* Primaries,
133                                           cmsToneCurve* const TransferFunction[3])
134 {
135     cmsHPROFILE hICC;
136     cmsMAT3 MColorants;
137     cmsCIEXYZTRIPLE Colorants;
138     cmsCIExyY MaxWhite;
139     cmsMAT3 CHAD;
140     cmsCIEXYZ WhitePointXYZ;
141 
142     hICC = cmsCreateProfilePlaceholder(ContextID);
143     if (!hICC)                          // can't allocate
144         return NULL;
145 
146     cmsSetProfileVersion(hICC, 4.3);
147 
148     cmsSetDeviceClass(hICC,      cmsSigDisplayClass);
149     cmsSetColorSpace(hICC,       cmsSigRgbData);
150     cmsSetPCS(hICC,              cmsSigXYZData);
151 
152     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
153 
154 
155     // Implement profile using following tags:
156     //
157     //  1 cmsSigProfileDescriptionTag
158     //  2 cmsSigMediaWhitePointTag
159     //  3 cmsSigRedColorantTag
160     //  4 cmsSigGreenColorantTag
161     //  5 cmsSigBlueColorantTag
162     //  6 cmsSigRedTRCTag
163     //  7 cmsSigGreenTRCTag
164     //  8 cmsSigBlueTRCTag
165     //  9 Chromatic adaptation Tag
166     // This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II)
167     // 10 cmsSigChromaticityTag
168 
169 
170     if (!SetTextTags(hICC, L"RGB built-in")) goto Error;
171 
172     if (WhitePoint) {
173 
174         if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
175 
176         cmsxyY2XYZ(&WhitePointXYZ, WhitePoint);
177         _cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ());
178 
179         // This is a V4 tag, but many CMM does read and understand it no matter which version
180         if (!cmsWriteTag(hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error;
181     }
182 
183     if (WhitePoint && Primaries) {
184 
185         MaxWhite.x =  WhitePoint -> x;
186         MaxWhite.y =  WhitePoint -> y;
187         MaxWhite.Y =  1.0;
188 
189         if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error;
190 
191         Colorants.Red.X   = MColorants.v[0].n[0];
192         Colorants.Red.Y   = MColorants.v[1].n[0];
193         Colorants.Red.Z   = MColorants.v[2].n[0];
194 
195         Colorants.Green.X = MColorants.v[0].n[1];
196         Colorants.Green.Y = MColorants.v[1].n[1];
197         Colorants.Green.Z = MColorants.v[2].n[1];
198 
199         Colorants.Blue.X  = MColorants.v[0].n[2];
200         Colorants.Blue.Y  = MColorants.v[1].n[2];
201         Colorants.Blue.Z  = MColorants.v[2].n[2];
202 
203         if (!cmsWriteTag(hICC, cmsSigRedColorantTag,   (void*) &Colorants.Red)) goto Error;
204         if (!cmsWriteTag(hICC, cmsSigBlueColorantTag,  (void*) &Colorants.Blue)) goto Error;
205         if (!cmsWriteTag(hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error;
206     }
207 
208 
209     if (TransferFunction) {
210 
211         // Tries to minimize space. Thanks to Richard Hughes for this nice idea
212         if (!cmsWriteTag(hICC, cmsSigRedTRCTag,   (void*) TransferFunction[0])) goto Error;
213 
214         if (TransferFunction[1] == TransferFunction[0]) {
215 
216             if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error;
217 
218         } else {
219 
220             if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;
221         }
222 
223         if (TransferFunction[2] == TransferFunction[0]) {
224 
225             if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error;
226 
227         } else {
228 
229             if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;
230         }
231     }
232 
233     if (Primaries) {
234         if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error;
235     }
236 
237 
238     return hICC;
239 
240 Error:
241     if (hICC)
242         cmsCloseProfile(hICC);
243     return NULL;
244 }
245 
cmsCreateRGBProfile(const cmsCIExyY * WhitePoint,const cmsCIExyYTRIPLE * Primaries,cmsToneCurve * const TransferFunction[3])246 cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint,
247                                           const cmsCIExyYTRIPLE* Primaries,
248                                           cmsToneCurve* const TransferFunction[3])
249 {
250     return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction);
251 }
252 
253 
254 
255 // This function creates a profile based on White point and transfer function.
cmsCreateGrayProfileTHR(cmsContext ContextID,const cmsCIExyY * WhitePoint,const cmsToneCurve * TransferFunction)256 cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID,
257                                            const cmsCIExyY* WhitePoint,
258                                            const cmsToneCurve* TransferFunction)
259 {
260     cmsHPROFILE hICC;
261     cmsCIEXYZ tmp;
262 
263     hICC = cmsCreateProfilePlaceholder(ContextID);
264     if (!hICC)                          // can't allocate
265         return NULL;
266 
267     cmsSetProfileVersion(hICC, 4.3);
268 
269     cmsSetDeviceClass(hICC,      cmsSigDisplayClass);
270     cmsSetColorSpace(hICC,       cmsSigGrayData);
271     cmsSetPCS(hICC,              cmsSigXYZData);
272     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
273 
274 
275     // Implement profile using following tags:
276     //
277     //  1 cmsSigProfileDescriptionTag
278     //  2 cmsSigMediaWhitePointTag
279     //  3 cmsSigGrayTRCTag
280 
281     // This conforms a standard Gray DisplayProfile
282 
283     // Fill-in the tags
284 
285     if (!SetTextTags(hICC, L"gray built-in")) goto Error;
286 
287 
288     if (WhitePoint) {
289 
290         cmsxyY2XYZ(&tmp, WhitePoint);
291         if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error;
292     }
293 
294     if (TransferFunction) {
295 
296         if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error;
297     }
298 
299     return hICC;
300 
301 Error:
302     if (hICC)
303         cmsCloseProfile(hICC);
304     return NULL;
305 }
306 
307 
308 
cmsCreateGrayProfile(const cmsCIExyY * WhitePoint,const cmsToneCurve * TransferFunction)309 cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint,
310                                                     const cmsToneCurve* TransferFunction)
311 {
312     return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction);
313 }
314 
315 // This is a devicelink operating in the target colorspace with as many transfer functions as components
316 
cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,cmsColorSpaceSignature ColorSpace,cmsToneCurve * const TransferFunctions[])317 cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,
318                                                           cmsColorSpaceSignature ColorSpace,
319                                                           cmsToneCurve* const TransferFunctions[])
320 {
321     cmsHPROFILE hICC;
322     cmsPipeline* Pipeline;
323     cmsUInt32Number nChannels;
324 
325     hICC = cmsCreateProfilePlaceholder(ContextID);
326     if (!hICC)
327         return NULL;
328 
329     cmsSetProfileVersion(hICC, 4.3);
330 
331     cmsSetDeviceClass(hICC,      cmsSigLinkClass);
332     cmsSetColorSpace(hICC,       ColorSpace);
333     cmsSetPCS(hICC,              ColorSpace);
334 
335     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
336 
337     // Set up channels
338     nChannels = cmsChannelsOf(ColorSpace);
339 
340     // Creates a Pipeline with prelinearization step only
341     Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
342     if (Pipeline == NULL) goto Error;
343 
344 
345     // Copy tables to Pipeline
346     if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))
347         goto Error;
348 
349     // Create tags
350     if (!SetTextTags(hICC, L"Linearization built-in")) goto Error;
351     if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error;
352     if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error;
353 
354     // Pipeline is already on virtual profile
355     cmsPipelineFree(Pipeline);
356 
357     // Ok, done
358     return hICC;
359 
360 Error:
361     cmsPipelineFree(Pipeline);
362     if (hICC)
363         cmsCloseProfile(hICC);
364 
365 
366     return NULL;
367 }
368 
cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace,cmsToneCurve * const TransferFunctions[])369 cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace,
370                                                                  cmsToneCurve* const TransferFunctions[])
371 {
372     return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions);
373 }
374 
375 // Ink-limiting algorithm
376 //
377 //  Sum = C + M + Y + K
378 //  If Sum > InkLimit
379 //        Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
380 //        if Ratio <0
381 //              Ratio=0
382 //        endif
383 //     Else
384 //         Ratio=1
385 //     endif
386 //
387 //     C = Ratio * C
388 //     M = Ratio * M
389 //     Y = Ratio * Y
390 //     K: Does not change
391 
392 static
InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[],CMSREGISTER cmsUInt16Number Out[],CMSREGISTER void * Cargo)393 int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
394 {
395     cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
396     cmsFloat64Number SumCMY, SumCMYK, Ratio;
397 
398     InkLimit = (InkLimit * 655.35);
399 
400     SumCMY   = In[0]  + In[1] + In[2];
401     SumCMYK  = SumCMY + In[3];
402 
403     if (SumCMYK > InkLimit) {
404 
405         Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
406         if (Ratio < 0)
407             Ratio = 0;
408     }
409     else Ratio = 1;
410 
411     Out[0] = _cmsQuickSaturateWord(In[0] * Ratio);     // C
412     Out[1] = _cmsQuickSaturateWord(In[1] * Ratio);     // M
413     Out[2] = _cmsQuickSaturateWord(In[2] * Ratio);     // Y
414 
415     Out[3] = In[3];                                 // K (untouched)
416 
417     return TRUE;
418 }
419 
420 // This is a devicelink operating in CMYK for ink-limiting
421 
cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,cmsColorSpaceSignature ColorSpace,cmsFloat64Number Limit)422 cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,
423                                                      cmsColorSpaceSignature ColorSpace,
424                                                      cmsFloat64Number Limit)
425 {
426     cmsHPROFILE hICC;
427     cmsPipeline* LUT;
428     cmsStage* CLUT;
429     cmsUInt32Number nChannels;
430 
431     if (ColorSpace != cmsSigCmykData) {
432         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
433         return NULL;
434     }
435 
436     if (Limit < 0.0 || Limit > 400) {
437 
438         cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400");
439         if (Limit < 0) Limit = 0;
440         if (Limit > 400) Limit = 400;
441 
442     }
443 
444     hICC = cmsCreateProfilePlaceholder(ContextID);
445     if (!hICC)                          // can't allocate
446         return NULL;
447 
448     cmsSetProfileVersion(hICC, 4.3);
449 
450     cmsSetDeviceClass(hICC,      cmsSigLinkClass);
451     cmsSetColorSpace(hICC,       ColorSpace);
452     cmsSetPCS(hICC,              ColorSpace);
453 
454     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
455 
456 
457     // Creates a Pipeline with 3D grid only
458     LUT = cmsPipelineAlloc(ContextID, 4, 4);
459     if (LUT == NULL) goto Error;
460 
461 
462     nChannels = cmsChannelsOf(ColorSpace);
463 
464     CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL);
465     if (CLUT == NULL) goto Error;
466 
467     if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error;
468 
469     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) ||
470         !cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) ||
471         !cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels)))
472         goto Error;
473 
474     // Create tags
475     if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error;
476 
477     if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT))  goto Error;
478     if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error;
479 
480     // cmsPipeline is already on virtual profile
481     cmsPipelineFree(LUT);
482 
483     // Ok, done
484     return hICC;
485 
486 Error:
487     if (LUT != NULL)
488         cmsPipelineFree(LUT);
489 
490     if (hICC != NULL)
491         cmsCloseProfile(hICC);
492 
493     return NULL;
494 }
495 
cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace,cmsFloat64Number Limit)496 cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit)
497 {
498     return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit);
499 }
500 
501 
502 // Creates a fake Lab identity.
cmsCreateLab2ProfileTHR(cmsContext ContextID,const cmsCIExyY * WhitePoint)503 cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
504 {
505     cmsHPROFILE hProfile;
506     cmsPipeline* LUT = NULL;
507 
508     hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
509     if (hProfile == NULL) return NULL;
510 
511     cmsSetProfileVersion(hProfile, 2.1);
512 
513     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
514     cmsSetColorSpace(hProfile,  cmsSigLabData);
515     cmsSetPCS(hProfile,         cmsSigLabData);
516 
517     if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL;
518 
519     // An identity LUT is all we need
520     LUT = cmsPipelineAlloc(ContextID, 3, 3);
521     if (LUT == NULL) goto Error;
522 
523     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
524         goto Error;
525 
526     if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
527     cmsPipelineFree(LUT);
528 
529     return hProfile;
530 
531 Error:
532 
533     if (LUT != NULL)
534         cmsPipelineFree(LUT);
535 
536     if (hProfile != NULL)
537         cmsCloseProfile(hProfile);
538 
539     return NULL;
540 }
541 
542 
cmsCreateLab2Profile(const cmsCIExyY * WhitePoint)543 cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint)
544 {
545     return cmsCreateLab2ProfileTHR(NULL, WhitePoint);
546 }
547 
548 
549 // Creates a fake Lab V4 identity.
cmsCreateLab4ProfileTHR(cmsContext ContextID,const cmsCIExyY * WhitePoint)550 cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
551 {
552     cmsHPROFILE hProfile;
553     cmsPipeline* LUT = NULL;
554 
555     hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
556     if (hProfile == NULL) return NULL;
557 
558     cmsSetProfileVersion(hProfile, 4.3);
559 
560     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
561     cmsSetColorSpace(hProfile,  cmsSigLabData);
562     cmsSetPCS(hProfile,         cmsSigLabData);
563 
564     if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error;
565 
566     // An empty LUTs is all we need
567     LUT = cmsPipelineAlloc(ContextID, 3, 3);
568     if (LUT == NULL) goto Error;
569 
570     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
571         goto Error;
572 
573     if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
574     cmsPipelineFree(LUT);
575 
576     return hProfile;
577 
578 Error:
579 
580     if (LUT != NULL)
581         cmsPipelineFree(LUT);
582 
583     if (hProfile != NULL)
584         cmsCloseProfile(hProfile);
585 
586     return NULL;
587 }
588 
cmsCreateLab4Profile(const cmsCIExyY * WhitePoint)589 cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint)
590 {
591     return cmsCreateLab4ProfileTHR(NULL, WhitePoint);
592 }
593 
594 
595 // Creates a fake XYZ identity
cmsCreateXYZProfileTHR(cmsContext ContextID)596 cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID)
597 {
598     cmsHPROFILE hProfile;
599     cmsPipeline* LUT = NULL;
600 
601     hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL);
602     if (hProfile == NULL) return NULL;
603 
604     cmsSetProfileVersion(hProfile, 4.3);
605 
606     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
607     cmsSetColorSpace(hProfile,  cmsSigXYZData);
608     cmsSetPCS(hProfile,         cmsSigXYZData);
609 
610     if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error;
611 
612     // An identity LUT is all we need
613     LUT = cmsPipelineAlloc(ContextID, 3, 3);
614     if (LUT == NULL) goto Error;
615 
616     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
617         goto Error;
618 
619     if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
620     cmsPipelineFree(LUT);
621 
622     return hProfile;
623 
624 Error:
625 
626     if (LUT != NULL)
627         cmsPipelineFree(LUT);
628 
629     if (hProfile != NULL)
630         cmsCloseProfile(hProfile);
631 
632     return NULL;
633 }
634 
635 
cmsCreateXYZProfile(void)636 cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void)
637 {
638     return cmsCreateXYZProfileTHR(NULL);
639 }
640 
641 
642 //sRGB Curves are defined by:
643 //
644 //If  R'sRGB,G'sRGB, B'sRGB < 0.04045
645 //
646 //    R =  R'sRGB / 12.92
647 //    G =  G'sRGB / 12.92
648 //    B =  B'sRGB / 12.92
649 //
650 //
651 //else if  R'sRGB,G'sRGB, B'sRGB >= 0.04045
652 //
653 //    R = ((R'sRGB + 0.055) / 1.055)^2.4
654 //    G = ((G'sRGB + 0.055) / 1.055)^2.4
655 //    B = ((B'sRGB + 0.055) / 1.055)^2.4
656 
657 static
Build_sRGBGamma(cmsContext ContextID)658 cmsToneCurve* Build_sRGBGamma(cmsContext ContextID)
659 {
660     cmsFloat64Number Parameters[5];
661 
662     Parameters[0] = 2.4;
663     Parameters[1] = 1. / 1.055;
664     Parameters[2] = 0.055 / 1.055;
665     Parameters[3] = 1. / 12.92;
666     Parameters[4] = 0.04045;
667 
668     return cmsBuildParametricToneCurve(ContextID, 4, Parameters);
669 }
670 
671 // Create the ICC virtual profile for sRGB space
cmsCreate_sRGBProfileTHR(cmsContext ContextID)672 cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID)
673 {
674        cmsCIExyY       D65 = { 0.3127, 0.3290, 1.0 };
675        cmsCIExyYTRIPLE Rec709Primaries = {
676                                    {0.6400, 0.3300, 1.0},
677                                    {0.3000, 0.6000, 1.0},
678                                    {0.1500, 0.0600, 1.0}
679                                    };
680        cmsToneCurve* Gamma22[3];
681        cmsHPROFILE  hsRGB;
682 
683       // cmsWhitePointFromTemp(&D65, 6504);
684        Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID);
685        if (Gamma22[0] == NULL) return NULL;
686 
687        hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22);
688        cmsFreeToneCurve(Gamma22[0]);
689        if (hsRGB == NULL) return NULL;
690 
691        if (!SetTextTags(hsRGB, L"sRGB built-in")) {
692            cmsCloseProfile(hsRGB);
693            return NULL;
694        }
695 
696        return hsRGB;
697 }
698 
cmsCreate_sRGBProfile(void)699 cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void)
700 {
701     return cmsCreate_sRGBProfileTHR(NULL);
702 }
703 
704 
705 
706 typedef struct {
707                 cmsFloat64Number Brightness;
708                 cmsFloat64Number Contrast;
709                 cmsFloat64Number Hue;
710                 cmsFloat64Number Saturation;
711                 cmsBool          lAdjustWP;
712                 cmsCIEXYZ WPsrc, WPdest;
713 
714 } BCHSWADJUSTS, *LPBCHSWADJUSTS;
715 
716 
717 static
bchswSampler(CMSREGISTER const cmsUInt16Number In[],CMSREGISTER cmsUInt16Number Out[],CMSREGISTER void * Cargo)718 int bchswSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
719 {
720     cmsCIELab LabIn, LabOut;
721     cmsCIELCh LChIn, LChOut;
722     cmsCIEXYZ XYZ;
723     LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
724 
725 
726     cmsLabEncoded2Float(&LabIn, In);
727 
728 
729     cmsLab2LCh(&LChIn, &LabIn);
730 
731     // Do some adjusts on LCh
732 
733     LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
734     LChOut.C = LChIn.C + bchsw -> Saturation;
735     LChOut.h = LChIn.h + bchsw -> Hue;
736 
737 
738     cmsLCh2Lab(&LabOut, &LChOut);
739 
740     // Move white point in Lab
741     if (bchsw->lAdjustWP) {
742            cmsLab2XYZ(&bchsw->WPsrc, &XYZ, &LabOut);
743            cmsXYZ2Lab(&bchsw->WPdest, &LabOut, &XYZ);
744     }
745 
746     // Back to encoded
747 
748     cmsFloat2LabEncoded(Out, &LabOut);
749 
750     return TRUE;
751 }
752 
753 
754 // Creates an abstract profile operating in Lab space for Brightness,
755 // contrast, Saturation and white point displacement
756 
cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,cmsUInt32Number nLUTPoints,cmsFloat64Number Bright,cmsFloat64Number Contrast,cmsFloat64Number Hue,cmsFloat64Number Saturation,cmsUInt32Number TempSrc,cmsUInt32Number TempDest)757 cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
758                                                        cmsUInt32Number nLUTPoints,
759                                                        cmsFloat64Number Bright,
760                                                        cmsFloat64Number Contrast,
761                                                        cmsFloat64Number Hue,
762                                                        cmsFloat64Number Saturation,
763                                                        cmsUInt32Number TempSrc,
764                                                        cmsUInt32Number TempDest)
765 {
766     cmsHPROFILE hICC;
767     cmsPipeline* Pipeline;
768     BCHSWADJUSTS bchsw;
769     cmsCIExyY WhitePnt;
770     cmsStage* CLUT;
771     cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
772     cmsUInt32Number i;
773 
774     bchsw.Brightness = Bright;
775     bchsw.Contrast   = Contrast;
776     bchsw.Hue        = Hue;
777     bchsw.Saturation = Saturation;
778     if (TempSrc == TempDest) {
779 
780            bchsw.lAdjustWP = FALSE;
781     }
782     else {
783            bchsw.lAdjustWP = TRUE;
784            cmsWhitePointFromTemp(&WhitePnt, TempSrc);
785            cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
786            cmsWhitePointFromTemp(&WhitePnt, TempDest);
787            cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
788 
789     }
790 
791     hICC = cmsCreateProfilePlaceholder(ContextID);
792     if (!hICC)                          // can't allocate
793         return NULL;
794 
795     cmsSetDeviceClass(hICC,      cmsSigAbstractClass);
796     cmsSetColorSpace(hICC,       cmsSigLabData);
797     cmsSetPCS(hICC,              cmsSigLabData);
798 
799     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
800 
801     // Creates a Pipeline with 3D grid only
802     Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
803     if (Pipeline == NULL) {
804         cmsCloseProfile(hICC);
805         return NULL;
806     }
807 
808     for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
809     CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
810     if (CLUT == NULL) goto Error;
811 
812 
813     if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
814 
815         // Shouldn't reach here
816         goto Error;
817     }
818 
819     if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
820         goto Error;
821     }
822 
823     // Create tags
824     if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
825 
826     cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
827 
828     cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
829 
830     // Pipeline is already on virtual profile
831     cmsPipelineFree(Pipeline);
832 
833     // Ok, done
834     return hICC;
835 
836 Error:
837     cmsPipelineFree(Pipeline);
838     cmsCloseProfile(hICC);
839     return NULL;
840 }
841 
842 
cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints,cmsFloat64Number Bright,cmsFloat64Number Contrast,cmsFloat64Number Hue,cmsFloat64Number Saturation,cmsUInt32Number TempSrc,cmsUInt32Number TempDest)843 CMSAPI cmsHPROFILE   CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints,
844                                                              cmsFloat64Number Bright,
845                                                              cmsFloat64Number Contrast,
846                                                              cmsFloat64Number Hue,
847                                                              cmsFloat64Number Saturation,
848                                                              cmsUInt32Number TempSrc,
849                                                              cmsUInt32Number TempDest)
850 {
851     return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest);
852 }
853 
854 
855 // Creates a fake NULL profile. This profile return 1 channel as always 0.
856 // Is useful only for gamut checking tricks
cmsCreateNULLProfileTHR(cmsContext ContextID)857 cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)
858 {
859     cmsHPROFILE hProfile;
860     cmsPipeline* LUT = NULL;
861     cmsStage* PostLin;
862     cmsStage* OutLin;
863     cmsToneCurve* EmptyTab[3];
864     cmsUInt16Number Zero[2] = { 0, 0 };
865     const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
866 
867     hProfile = cmsCreateProfilePlaceholder(ContextID);
868     if (!hProfile)                          // can't allocate
869         return NULL;
870 
871     cmsSetProfileVersion(hProfile, 4.3);
872 
873     if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
874 
875 
876     cmsSetDeviceClass(hProfile, cmsSigOutputClass);
877     cmsSetColorSpace(hProfile,  cmsSigGrayData);
878     cmsSetPCS(hProfile,         cmsSigLabData);
879 
880     // Create a valid ICC 4 structure
881     LUT = cmsPipelineAlloc(ContextID, 3, 1);
882     if (LUT == NULL) goto Error;
883 
884     EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
885     PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab);
886     OutLin  = cmsStageAllocToneCurves(ContextID, 1, EmptyTab);
887     cmsFreeToneCurve(EmptyTab[0]);
888 
889     if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
890         goto Error;
891 
892     if (!cmsPipelineInsertStage(LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))
893         goto Error;
894 
895     if (!cmsPipelineInsertStage(LUT, cmsAT_END, OutLin))
896         goto Error;
897 
898     if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
899     if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
900 
901     cmsPipelineFree(LUT);
902     return hProfile;
903 
904 Error:
905 
906     if (LUT != NULL)
907         cmsPipelineFree(LUT);
908 
909     if (hProfile != NULL)
910         cmsCloseProfile(hProfile);
911 
912     return NULL;
913 }
914 
cmsCreateNULLProfile(void)915 cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)
916 {
917     return cmsCreateNULLProfileTHR(NULL);
918 }
919 
920 
921 static
IsPCS(cmsColorSpaceSignature ColorSpace)922 int IsPCS(cmsColorSpaceSignature ColorSpace)
923 {
924     return (ColorSpace == cmsSigXYZData ||
925             ColorSpace == cmsSigLabData);
926 }
927 
928 
929 static
FixColorSpaces(cmsHPROFILE hProfile,cmsColorSpaceSignature ColorSpace,cmsColorSpaceSignature PCS,cmsUInt32Number dwFlags)930 void FixColorSpaces(cmsHPROFILE hProfile,
931                               cmsColorSpaceSignature ColorSpace,
932                               cmsColorSpaceSignature PCS,
933                               cmsUInt32Number dwFlags)
934 {
935     if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
936 
937             if (IsPCS(ColorSpace) && IsPCS(PCS)) {
938 
939                     cmsSetDeviceClass(hProfile,      cmsSigAbstractClass);
940                     cmsSetColorSpace(hProfile,       ColorSpace);
941                     cmsSetPCS(hProfile,              PCS);
942                     return;
943             }
944 
945             if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
946 
947                     cmsSetDeviceClass(hProfile, cmsSigOutputClass);
948                     cmsSetPCS(hProfile,         ColorSpace);
949                     cmsSetColorSpace(hProfile,  PCS);
950                     return;
951             }
952 
953             if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
954 
955                    cmsSetDeviceClass(hProfile,  cmsSigInputClass);
956                    cmsSetColorSpace(hProfile,   ColorSpace);
957                    cmsSetPCS(hProfile,          PCS);
958                    return;
959             }
960     }
961 
962     cmsSetDeviceClass(hProfile,      cmsSigLinkClass);
963     cmsSetColorSpace(hProfile,       ColorSpace);
964     cmsSetPCS(hProfile,              PCS);
965 }
966 
967 
968 
969 // This function creates a named color profile dumping all the contents of transform to a single profile
970 // In this way, LittleCMS may be used to "group" several named color databases into a single profile.
971 // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
972 // is the normal PCS for named color profiles.
973 static
CreateNamedColorDevicelink(cmsHTRANSFORM xform)974 cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
975 {
976     _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
977     cmsHPROFILE hICC = NULL;
978     cmsUInt32Number i, nColors;
979     cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
980 
981     // Create an empty placeholder
982     hICC = cmsCreateProfilePlaceholder(v->ContextID);
983     if (hICC == NULL) return NULL;
984 
985     // Critical information
986     cmsSetDeviceClass(hICC, cmsSigNamedColorClass);
987     cmsSetColorSpace(hICC, v ->ExitColorSpace);
988     cmsSetPCS(hICC, cmsSigLabData);
989 
990     // Tag profile with information
991     if (!SetTextTags(hICC, L"Named color devicelink")) goto Error;
992 
993     Original = cmsGetNamedColorList(xform);
994     if (Original == NULL) goto Error;
995 
996     nColors = cmsNamedColorCount(Original);
997     nc2     = cmsDupNamedColorList(Original);
998     if (nc2 == NULL) goto Error;
999 
1000     // Colorant count now depends on the output space
1001     nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);
1002 
1003     // Make sure we have proper formatters
1004     cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
1005         FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
1006         | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace)));
1007 
1008     // Apply the transfor to colorants.
1009     for (i=0; i < nColors; i++) {
1010         cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
1011     }
1012 
1013     if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
1014     cmsFreeNamedColorList(nc2);
1015 
1016     return hICC;
1017 
1018 Error:
1019     if (hICC != NULL) cmsCloseProfile(hICC);
1020     return NULL;
1021 }
1022 
1023 
1024 // This structure holds information about which MPU can be stored on a profile based on the version
1025 
1026 typedef struct {
1027     cmsBool              IsV4;             // Is a V4 tag?
1028     cmsTagSignature      RequiredTag;      // Set to 0 for both types
1029     cmsTagTypeSignature  LutType;          // The LUT type
1030     int                  nTypes;           // Number of types (up to 5)
1031     cmsStageSignature    MpeTypes[5];      // 5 is the maximum number
1032 
1033 } cmsAllowedLUT;
1034 
1035 #define cmsSig0 ((cmsTagSignature) 0)
1036 
1037 static const cmsAllowedLUT AllowedLUTTypes[] = {
1038 
1039     { FALSE, cmsSig0,        cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1040     { FALSE, cmsSig0,        cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1041     { FALSE, cmsSig0,        cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },
1042     { TRUE,  cmsSig0,        cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },
1043     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  3,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
1044     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType   } },
1045     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  5,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1046     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  1,  { cmsSigCurveSetElemType }},
1047     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  3,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1048     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
1049     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  5,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
1050 };
1051 
1052 #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
1053 
1054 // Check a single entry
1055 static
CheckOne(const cmsAllowedLUT * Tab,const cmsPipeline * Lut)1056 cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
1057 {
1058     cmsStage* mpe;
1059     int n;
1060 
1061     for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
1062 
1063         if (n > Tab ->nTypes) return FALSE;
1064         if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
1065     }
1066 
1067     return (n == Tab ->nTypes);
1068 }
1069 
1070 
1071 static
FindCombination(const cmsPipeline * Lut,cmsBool IsV4,cmsTagSignature DestinationTag)1072 const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1073 {
1074     cmsUInt32Number n;
1075 
1076     for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1077 
1078         const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1079 
1080         if (IsV4 ^ Tab -> IsV4) continue;
1081         if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1082 
1083         if (CheckOne(Tab, Lut)) return Tab;
1084     }
1085 
1086     return NULL;
1087 }
1088 
1089 
1090 // Does convert a transform into a device link profile
cmsTransform2DeviceLink(cmsHTRANSFORM hTransform,cmsFloat64Number Version,cmsUInt32Number dwFlags)1091 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1092 {
1093     cmsHPROFILE hProfile = NULL;
1094     cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;
1095     int ColorSpaceBitsIn, ColorSpaceBitsOut;
1096     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1097     cmsPipeline* LUT = NULL;
1098     cmsStage* mpe;
1099     cmsContext ContextID = cmsGetTransformContextID(hTransform);
1100     const cmsAllowedLUT* AllowedLUT;
1101     cmsTagSignature DestinationTag;
1102     cmsProfileClassSignature deviceClass;
1103 
1104     _cmsAssert(hTransform != NULL);
1105 
1106     // Get the first mpe to check for named color
1107     mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);
1108 
1109     // Check if is a named color transform
1110     if (mpe != NULL) {
1111 
1112         if (cmsStageType(mpe) == cmsSigNamedColorElemType) {
1113             return CreateNamedColorDevicelink(hTransform);
1114         }
1115     }
1116 
1117     // First thing to do is to get a copy of the transformation
1118     LUT = cmsPipelineDup(xform ->Lut);
1119     if (LUT == NULL) return NULL;
1120 
1121     // Time to fix the Lab2/Lab4 issue.
1122     if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1123 
1124         if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
1125             goto Error;
1126     }
1127 
1128     // On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments
1129     if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1130 
1131         dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;
1132         if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
1133             goto Error;
1134     }
1135 
1136 
1137     hProfile = cmsCreateProfilePlaceholder(ContextID);
1138     if (!hProfile) goto Error;                    // can't allocate
1139 
1140     cmsSetProfileVersion(hProfile, Version);
1141 
1142     FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);
1143 
1144     // Optimize the LUT and precalculate a devicelink
1145 
1146     ChansIn  = cmsChannelsOf(xform -> EntryColorSpace);
1147     ChansOut = cmsChannelsOf(xform -> ExitColorSpace);
1148 
1149     ColorSpaceBitsIn  = _cmsLCMScolorSpace(xform -> EntryColorSpace);
1150     ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
1151 
1152     FrmIn  = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1153     FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1154 
1155     deviceClass = cmsGetDeviceClass(hProfile);
1156 
1157      if (deviceClass == cmsSigOutputClass)
1158          DestinationTag = cmsSigBToA0Tag;
1159      else
1160          DestinationTag = cmsSigAToB0Tag;
1161 
1162     // Check if the profile/version can store the result
1163     if (dwFlags & cmsFLAGS_FORCE_CLUT)
1164         AllowedLUT = NULL;
1165     else
1166         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1167 
1168     if (AllowedLUT == NULL) {
1169 
1170         // Try to optimize
1171         _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1172         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1173 
1174     }
1175 
1176     // If no way, then force CLUT that for sure can be written
1177     if (AllowedLUT == NULL) {
1178 
1179         cmsStage* FirstStage;
1180         cmsStage* LastStage;
1181 
1182         dwFlags |= cmsFLAGS_FORCE_CLUT;
1183         _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1184 
1185         // Put identity curves if needed
1186         FirstStage = cmsPipelineGetPtrToFirstStage(LUT);
1187         if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType)
1188              if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
1189                  goto Error;
1190 
1191         LastStage = cmsPipelineGetPtrToLastStage(LUT);
1192         if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType)
1193              if (!cmsPipelineInsertStage(LUT, cmsAT_END,   _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
1194                  goto Error;
1195 
1196         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1197     }
1198 
1199     // Somethings is wrong...
1200     if (AllowedLUT == NULL) {
1201         goto Error;
1202     }
1203 
1204 
1205     if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1206                      cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);
1207 
1208     // Tag profile with information
1209     if (!SetTextTags(hProfile, L"devicelink")) goto Error;
1210 
1211     // Store result
1212     if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
1213 
1214 
1215     if (xform -> InputColorant != NULL) {
1216            if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
1217     }
1218 
1219     if (xform -> OutputColorant != NULL) {
1220            if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
1221     }
1222 
1223     if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
1224         if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
1225     }
1226 
1227     // Set the white point
1228     if (deviceClass == cmsSigInputClass) {
1229         if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;
1230     }
1231     else {
1232          if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;
1233     }
1234 
1235 
1236     // Per 7.2.15 in spec 4.3
1237     cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);
1238 
1239     cmsPipelineFree(LUT);
1240     return hProfile;
1241 
1242 Error:
1243     if (LUT != NULL) cmsPipelineFree(LUT);
1244     cmsCloseProfile(hProfile);
1245     return NULL;
1246 }
1247