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