Update lcms (#544)
[openjpeg.git] / thirdparty / liblcms2 / src / cmsvirt.c
1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2016 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
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
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.
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.3);
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         // Tries to minimize space. Thanks to Richard Hughes for this nice idea         
183         if (!cmsWriteTag(hICC, cmsSigRedTRCTag,   (void*) TransferFunction[0])) goto Error;
184
185         if (TransferFunction[1] == TransferFunction[0]) {
186
187             if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error;
188
189         } else {
190
191             if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;
192         }
193
194         if (TransferFunction[2] == TransferFunction[0]) {
195
196             if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error;
197
198         } else {
199
200             if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;
201         }
202     }
203
204     if (Primaries) {
205         if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error;
206     }
207
208
209     return hICC;
210
211 Error:
212     if (hICC)
213         cmsCloseProfile(hICC);
214     return NULL;
215 }
216
217 cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint,
218                                           const cmsCIExyYTRIPLE* Primaries,
219                                           cmsToneCurve* const TransferFunction[3])
220 {
221     return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction);
222 }
223
224
225
226 // This function creates a profile based on White point and transfer function.
227 cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID,
228                                            const cmsCIExyY* WhitePoint,
229                                            const cmsToneCurve* TransferFunction)
230 {
231     cmsHPROFILE hICC;
232     cmsCIEXYZ tmp;
233
234     hICC = cmsCreateProfilePlaceholder(ContextID);
235     if (!hICC)                          // can't allocate
236         return NULL;
237
238     cmsSetProfileVersion(hICC, 4.3);
239
240     cmsSetDeviceClass(hICC,      cmsSigDisplayClass);
241     cmsSetColorSpace(hICC,       cmsSigGrayData);
242     cmsSetPCS(hICC,              cmsSigXYZData);
243     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
244
245
246     // Implement profile using following tags:
247     //
248     //  1 cmsSigProfileDescriptionTag
249     //  2 cmsSigMediaWhitePointTag
250     //  3 cmsSigGrayTRCTag
251
252     // This conforms a standard Gray DisplayProfile
253
254     // Fill-in the tags
255
256     if (!SetTextTags(hICC, L"gray built-in")) goto Error;
257
258
259     if (WhitePoint) {
260
261         cmsxyY2XYZ(&tmp, WhitePoint);
262         if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error;
263     }
264
265     if (TransferFunction) {
266
267         if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error;
268     }
269
270     return hICC;
271
272 Error:
273     if (hICC)
274         cmsCloseProfile(hICC);
275     return NULL;
276 }
277
278
279
280 cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint,
281                                                     const cmsToneCurve* TransferFunction)
282 {
283     return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction);
284 }
285
286 // This is a devicelink operating in the target colorspace with as many transfer functions as components
287
288 cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,
289                                                           cmsColorSpaceSignature ColorSpace,
290                                                           cmsToneCurve* const TransferFunctions[])
291 {
292     cmsHPROFILE hICC;
293     cmsPipeline* Pipeline;
294     int nChannels;
295
296     hICC = cmsCreateProfilePlaceholder(ContextID);
297     if (!hICC)
298         return NULL;
299
300     cmsSetProfileVersion(hICC, 4.3);
301
302     cmsSetDeviceClass(hICC,      cmsSigLinkClass);
303     cmsSetColorSpace(hICC,       ColorSpace);
304     cmsSetPCS(hICC,              ColorSpace);
305
306     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
307
308     // Set up channels
309     nChannels = cmsChannelsOf(ColorSpace);
310
311     // Creates a Pipeline with prelinearization step only
312     Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
313     if (Pipeline == NULL) goto Error;
314
315
316     // Copy tables to Pipeline
317     if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))
318         goto Error;
319
320     // Create tags
321     if (!SetTextTags(hICC, L"Linearization built-in")) goto Error;
322     if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error;
323     if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error;
324
325     // Pipeline is already on virtual profile
326     cmsPipelineFree(Pipeline);
327
328     // Ok, done
329     return hICC;
330
331 Error:
332     cmsPipelineFree(Pipeline);
333     if (hICC)
334         cmsCloseProfile(hICC);
335
336
337     return NULL;
338 }
339
340 cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace,
341                                                                  cmsToneCurve* const TransferFunctions[])
342 {
343     return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions);
344 }
345
346 // Ink-limiting algorithm
347 //
348 //  Sum = C + M + Y + K
349 //  If Sum > InkLimit
350 //        Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
351 //        if Ratio <0
352 //              Ratio=0
353 //        endif
354 //     Else
355 //         Ratio=1
356 //     endif
357 //
358 //     C = Ratio * C
359 //     M = Ratio * M
360 //     Y = Ratio * Y
361 //     K: Does not change
362
363 static
364 int InkLimitingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
365 {
366     cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
367     cmsFloat64Number SumCMY, SumCMYK, Ratio;
368
369     InkLimit = (InkLimit * 655.35);
370
371     SumCMY   = In[0]  + In[1] + In[2];
372     SumCMYK  = SumCMY + In[3];
373
374     if (SumCMYK > InkLimit) {
375
376         Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
377         if (Ratio < 0)
378             Ratio = 0;
379     }
380     else Ratio = 1;
381
382     Out[0] = _cmsQuickSaturateWord(In[0] * Ratio);     // C
383     Out[1] = _cmsQuickSaturateWord(In[1] * Ratio);     // M
384     Out[2] = _cmsQuickSaturateWord(In[2] * Ratio);     // Y
385
386     Out[3] = In[3];                                 // K (untouched)
387
388     return TRUE;
389 }
390
391 // This is a devicelink operating in CMYK for ink-limiting
392
393 cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,
394                                                      cmsColorSpaceSignature ColorSpace,
395                                                      cmsFloat64Number Limit)
396 {
397     cmsHPROFILE hICC;
398     cmsPipeline* LUT;
399     cmsStage* CLUT;
400     int nChannels;
401
402     if (ColorSpace != cmsSigCmykData) {
403         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
404         return NULL;
405     }
406
407     if (Limit < 0.0 || Limit > 400) {
408
409         cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400");
410         if (Limit < 0) Limit = 0;
411         if (Limit > 400) Limit = 400;
412
413     }
414
415     hICC = cmsCreateProfilePlaceholder(ContextID);
416     if (!hICC)                          // can't allocate
417         return NULL;
418
419     cmsSetProfileVersion(hICC, 4.3);
420
421     cmsSetDeviceClass(hICC,      cmsSigLinkClass);
422     cmsSetColorSpace(hICC,       ColorSpace);
423     cmsSetPCS(hICC,              ColorSpace);
424
425     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
426
427
428     // Creates a Pipeline with 3D grid only
429     LUT = cmsPipelineAlloc(ContextID, 4, 4);
430     if (LUT == NULL) goto Error;
431
432
433     nChannels = cmsChannelsOf(ColorSpace);
434
435     CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL);
436     if (CLUT == NULL) goto Error;
437
438     if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error;
439
440     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) ||
441         !cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) ||
442         !cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels)))
443         goto Error;
444
445     // Create tags
446     if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error;
447
448     if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT))  goto Error;
449     if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error;
450
451     // cmsPipeline is already on virtual profile
452     cmsPipelineFree(LUT);
453
454     // Ok, done
455     return hICC;
456
457 Error:
458     if (LUT != NULL)
459         cmsPipelineFree(LUT);
460
461     if (hICC != NULL)
462         cmsCloseProfile(hICC);
463
464     return NULL;
465 }
466
467 cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit)
468 {
469     return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit);
470 }
471
472
473 // Creates a fake Lab identity.
474 cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
475 {
476     cmsHPROFILE hProfile;
477     cmsPipeline* LUT = NULL;
478
479     hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
480     if (hProfile == NULL) return NULL;
481
482     cmsSetProfileVersion(hProfile, 2.1);
483
484     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
485     cmsSetColorSpace(hProfile,  cmsSigLabData);
486     cmsSetPCS(hProfile,         cmsSigLabData);
487
488     if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL;
489
490     // An identity LUT is all we need
491     LUT = cmsPipelineAlloc(ContextID, 3, 3);
492     if (LUT == NULL) goto Error;
493
494     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
495         goto Error;
496
497     if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
498     cmsPipelineFree(LUT);
499
500     return hProfile;
501
502 Error:
503
504     if (LUT != NULL)
505         cmsPipelineFree(LUT);
506
507     if (hProfile != NULL)
508         cmsCloseProfile(hProfile);
509
510     return NULL;
511 }
512
513
514 cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint)
515 {
516     return cmsCreateLab2ProfileTHR(NULL, WhitePoint);
517 }
518
519
520 // Creates a fake Lab V4 identity.
521 cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
522 {
523     cmsHPROFILE hProfile;
524     cmsPipeline* LUT = NULL;
525
526     hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
527     if (hProfile == NULL) return NULL;
528
529     cmsSetProfileVersion(hProfile, 4.3);
530
531     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
532     cmsSetColorSpace(hProfile,  cmsSigLabData);
533     cmsSetPCS(hProfile,         cmsSigLabData);
534
535     if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error;
536
537     // An empty LUTs is all we need
538     LUT = cmsPipelineAlloc(ContextID, 3, 3);
539     if (LUT == NULL) goto Error;
540
541     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
542         goto Error;
543
544     if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
545     cmsPipelineFree(LUT);
546
547     return hProfile;
548
549 Error:
550
551     if (LUT != NULL)
552         cmsPipelineFree(LUT);
553
554     if (hProfile != NULL)
555         cmsCloseProfile(hProfile);
556
557     return NULL;
558 }
559
560 cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint)
561 {
562     return cmsCreateLab4ProfileTHR(NULL, WhitePoint);
563 }
564
565
566 // Creates a fake XYZ identity
567 cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID)
568 {
569     cmsHPROFILE hProfile;
570     cmsPipeline* LUT = NULL;
571
572     hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL);
573     if (hProfile == NULL) return NULL;
574
575     cmsSetProfileVersion(hProfile, 4.3);
576
577     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
578     cmsSetColorSpace(hProfile,  cmsSigXYZData);
579     cmsSetPCS(hProfile,         cmsSigXYZData);
580
581     if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error;
582
583     // An identity LUT is all we need
584     LUT = cmsPipelineAlloc(ContextID, 3, 3);
585     if (LUT == NULL) goto Error;
586
587     if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
588         goto Error;
589
590     if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
591     cmsPipelineFree(LUT);
592
593     return hProfile;
594
595 Error:
596
597     if (LUT != NULL)
598         cmsPipelineFree(LUT);
599
600     if (hProfile != NULL)
601         cmsCloseProfile(hProfile);
602
603     return NULL;
604 }
605
606
607 cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void)
608 {
609     return cmsCreateXYZProfileTHR(NULL);
610 }
611
612
613 //sRGB Curves are defined by:
614 //
615 //If  R�sRGB,G�sRGB, B�sRGB < 0.04045
616 //
617 //    R =  R�sRGB / 12.92
618 //    G =  G�sRGB / 12.92
619 //    B =  B�sRGB / 12.92
620 //
621 //
622 //else if  R�sRGB,G�sRGB, B�sRGB >= 0.04045
623 //
624 //    R = ((R�sRGB + 0.055) / 1.055)^2.4
625 //    G = ((G�sRGB + 0.055) / 1.055)^2.4
626 //    B = ((B�sRGB + 0.055) / 1.055)^2.4
627
628 static
629 cmsToneCurve* Build_sRGBGamma(cmsContext ContextID)
630 {
631     cmsFloat64Number Parameters[5];
632
633     Parameters[0] = 2.4;
634     Parameters[1] = 1. / 1.055;
635     Parameters[2] = 0.055 / 1.055;
636     Parameters[3] = 1. / 12.92;
637     Parameters[4] = 0.04045;
638
639     return cmsBuildParametricToneCurve(ContextID, 4, Parameters);
640 }
641
642 // Create the ICC virtual profile for sRGB space
643 cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID)
644 {
645        cmsCIExyY       D65 = { 0.3127, 0.3290, 1.0 };
646        cmsCIExyYTRIPLE Rec709Primaries = {
647                                    {0.6400, 0.3300, 1.0},
648                                    {0.3000, 0.6000, 1.0},
649                                    {0.1500, 0.0600, 1.0}
650                                    };
651        cmsToneCurve* Gamma22[3];
652        cmsHPROFILE  hsRGB;
653
654       // cmsWhitePointFromTemp(&D65, 6504);
655        Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID);
656        if (Gamma22[0] == NULL) return NULL;
657
658        hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22);
659        cmsFreeToneCurve(Gamma22[0]);
660        if (hsRGB == NULL) return NULL;
661
662        if (!SetTextTags(hsRGB, L"sRGB built-in")) {
663            cmsCloseProfile(hsRGB);
664            return NULL;
665        }
666
667        return hsRGB;
668 }
669
670 cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void)
671 {
672     return cmsCreate_sRGBProfileTHR(NULL);
673 }
674
675
676
677 typedef struct {
678                 cmsFloat64Number Brightness;
679                 cmsFloat64Number Contrast;
680                 cmsFloat64Number Hue;
681                 cmsFloat64Number Saturation;
682                 cmsBool          lAdjustWP;
683                 cmsCIEXYZ WPsrc, WPdest;
684
685 } BCHSWADJUSTS, *LPBCHSWADJUSTS;
686
687
688 static
689 int bchswSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
690 {
691     cmsCIELab LabIn, LabOut;
692     cmsCIELCh LChIn, LChOut;
693     cmsCIEXYZ XYZ;
694     LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
695
696
697     cmsLabEncoded2Float(&LabIn, In);
698
699
700     cmsLab2LCh(&LChIn, &LabIn);
701
702     // Do some adjusts on LCh
703
704     LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
705     LChOut.C = LChIn.C + bchsw -> Saturation;
706     LChOut.h = LChIn.h + bchsw -> Hue;
707
708
709     cmsLCh2Lab(&LabOut, &LChOut);
710
711     // Move white point in Lab
712     if (bchsw->lAdjustWP) {
713            cmsLab2XYZ(&bchsw->WPsrc, &XYZ, &LabOut);
714            cmsXYZ2Lab(&bchsw->WPdest, &LabOut, &XYZ);
715     }
716
717     // Back to encoded
718
719     cmsFloat2LabEncoded(Out, &LabOut);
720
721     return TRUE;
722 }
723
724
725 // Creates an abstract profile operating in Lab space for Brightness,
726 // contrast, Saturation and white point displacement
727
728 cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
729     int nLUTPoints,
730     cmsFloat64Number Bright,
731     cmsFloat64Number Contrast,
732     cmsFloat64Number Hue,
733     cmsFloat64Number Saturation,
734     int TempSrc,
735     int TempDest)
736 {
737     cmsHPROFILE hICC;
738     cmsPipeline* Pipeline;
739     BCHSWADJUSTS bchsw;
740     cmsCIExyY WhitePnt;
741     cmsStage* CLUT;
742     cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
743     int i;
744
745     bchsw.Brightness = Bright;
746     bchsw.Contrast   = Contrast;
747     bchsw.Hue        = Hue;
748     bchsw.Saturation = Saturation;
749     if (TempSrc == TempDest) {
750
751            bchsw.lAdjustWP = FALSE;
752     }
753     else {
754            bchsw.lAdjustWP = TRUE;
755            cmsWhitePointFromTemp(&WhitePnt, TempSrc);
756            cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
757            cmsWhitePointFromTemp(&WhitePnt, TempDest);
758            cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
759      
760     }
761
762     hICC = cmsCreateProfilePlaceholder(ContextID);
763     if (!hICC)                          // can't allocate
764         return NULL;
765
766     cmsSetDeviceClass(hICC,      cmsSigAbstractClass);
767     cmsSetColorSpace(hICC,       cmsSigLabData);
768     cmsSetPCS(hICC,              cmsSigLabData);
769
770     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
771
772     // Creates a Pipeline with 3D grid only
773     Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
774     if (Pipeline == NULL) {
775         cmsCloseProfile(hICC);
776         return NULL;
777     }
778
779     for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
780     CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
781     if (CLUT == NULL) return NULL;
782
783
784     if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
785
786         // Shouldn't reach here
787         goto Error;
788     }
789
790     if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
791         goto Error;
792     }
793
794     // Create tags
795     if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
796
797     cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
798
799     cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
800
801     // Pipeline is already on virtual profile
802     cmsPipelineFree(Pipeline);
803
804     // Ok, done
805     return hICC;
806
807 Error:
808     cmsPipelineFree(Pipeline);
809     cmsCloseProfile(hICC);
810     return NULL;
811 }
812
813
814 CMSAPI cmsHPROFILE   CMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints,
815                                                              cmsFloat64Number Bright,
816                                                              cmsFloat64Number Contrast,
817                                                              cmsFloat64Number Hue,
818                                                              cmsFloat64Number Saturation,
819                                                              int TempSrc,
820                                                              int TempDest)
821 {
822     return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest);
823 }
824
825
826 // Creates a fake NULL profile. This profile return 1 channel as always 0.
827 // Is useful only for gamut checking tricks
828 cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)
829 {
830     cmsHPROFILE hProfile;
831     cmsPipeline* LUT = NULL;
832     cmsStage* PostLin;
833     cmsToneCurve* EmptyTab;
834     cmsUInt16Number Zero[2] = { 0, 0 };
835
836     hProfile = cmsCreateProfilePlaceholder(ContextID);
837     if (!hProfile)                          // can't allocate
838         return NULL;
839
840     cmsSetProfileVersion(hProfile, 4.3);
841
842     if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
843
844
845
846     cmsSetDeviceClass(hProfile, cmsSigOutputClass);
847     cmsSetColorSpace(hProfile,  cmsSigGrayData);
848     cmsSetPCS(hProfile,         cmsSigLabData);
849
850     // An empty LUTs is all we need
851     LUT = cmsPipelineAlloc(ContextID, 1, 1);
852     if (LUT == NULL) goto Error;
853
854     EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
855     PostLin = cmsStageAllocToneCurves(ContextID, 1, &EmptyTab);
856     cmsFreeToneCurve(EmptyTab);
857
858     if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
859         goto Error;
860
861     if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
862     if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
863
864     cmsPipelineFree(LUT);
865     return hProfile;
866
867 Error:
868
869     if (LUT != NULL)
870         cmsPipelineFree(LUT);
871
872     if (hProfile != NULL)
873         cmsCloseProfile(hProfile);
874
875     return NULL;
876 }
877
878 cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)
879 {
880     return cmsCreateNULLProfileTHR(NULL);
881 }
882
883
884 static
885 int IsPCS(cmsColorSpaceSignature ColorSpace)
886 {
887     return (ColorSpace == cmsSigXYZData ||
888             ColorSpace == cmsSigLabData);
889 }
890
891
892 static
893 void FixColorSpaces(cmsHPROFILE hProfile,
894                               cmsColorSpaceSignature ColorSpace,
895                               cmsColorSpaceSignature PCS,
896                               cmsUInt32Number dwFlags)
897 {
898     if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
899
900             if (IsPCS(ColorSpace) && IsPCS(PCS)) {
901
902                     cmsSetDeviceClass(hProfile,      cmsSigAbstractClass);
903                     cmsSetColorSpace(hProfile,       ColorSpace);
904                     cmsSetPCS(hProfile,              PCS);
905                     return;
906             }
907
908             if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
909
910                     cmsSetDeviceClass(hProfile, cmsSigOutputClass);
911                     cmsSetPCS(hProfile,         ColorSpace);
912                     cmsSetColorSpace(hProfile,  PCS);
913                     return;
914             }
915
916             if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
917
918                    cmsSetDeviceClass(hProfile,  cmsSigInputClass);
919                    cmsSetColorSpace(hProfile,   ColorSpace);
920                    cmsSetPCS(hProfile,          PCS);
921                    return;
922             }
923     }
924
925     cmsSetDeviceClass(hProfile,      cmsSigLinkClass);
926     cmsSetColorSpace(hProfile,       ColorSpace);
927     cmsSetPCS(hProfile,              PCS);
928 }
929
930
931
932 // This function creates a named color profile dumping all the contents of transform to a single profile
933 // In this way, LittleCMS may be used to "group" several named color databases into a single profile.
934 // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
935 // is the normal PCS for named color profiles.
936 static
937 cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
938 {
939     _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
940     cmsHPROFILE hICC = NULL;
941     int i, nColors;
942     cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
943
944     // Create an empty placeholder
945     hICC = cmsCreateProfilePlaceholder(v->ContextID);
946     if (hICC == NULL) return NULL;
947
948     // Critical information
949     cmsSetDeviceClass(hICC, cmsSigNamedColorClass);
950     cmsSetColorSpace(hICC, v ->ExitColorSpace);
951     cmsSetPCS(hICC, cmsSigLabData);
952
953     // Tag profile with information
954     if (!SetTextTags(hICC, L"Named color devicelink")) goto Error;
955
956     Original = cmsGetNamedColorList(xform);
957     if (Original == NULL) goto Error;
958
959     nColors = cmsNamedColorCount(Original);
960     nc2     = cmsDupNamedColorList(Original);
961     if (nc2 == NULL) goto Error;
962
963     // Colorant count now depends on the output space
964     nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);
965
966     // Make sure we have proper formatters
967     cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
968         FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
969         | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace)));
970
971     // Apply the transfor to colorants.
972     for (i=0; i < nColors; i++) {
973         cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
974     }
975
976     if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
977     cmsFreeNamedColorList(nc2);
978
979     return hICC;
980
981 Error:
982     if (hICC != NULL) cmsCloseProfile(hICC);
983     return NULL;
984 }
985
986
987 // This structure holds information about which MPU can be stored on a profile based on the version
988
989 typedef struct {
990     cmsBool              IsV4;             // Is a V4 tag?
991     cmsTagSignature      RequiredTag;      // Set to 0 for both types
992     cmsTagTypeSignature  LutType;          // The LUT type
993     int                  nTypes;           // Number of types (up to 5)
994     cmsStageSignature    MpeTypes[5];      // 5 is the maximum number
995
996 } cmsAllowedLUT;
997
998 #define cmsSig0 ((cmsTagSignature) 0) 
999
1000 static const cmsAllowedLUT AllowedLUTTypes[] = {
1001
1002     { FALSE, cmsSig0,        cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1003     { FALSE, cmsSig0,        cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1004     { FALSE, cmsSig0,        cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },
1005     { TRUE,  cmsSig0,        cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },
1006     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  3,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
1007     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType   } },
1008     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  5,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1009     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  1,  { cmsSigCurveSetElemType }},
1010     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  3,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1011     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
1012     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  5,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
1013 };
1014
1015 #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
1016
1017 // Check a single entry
1018 static
1019 cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
1020 {
1021     cmsStage* mpe;
1022     int n;
1023
1024     for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
1025
1026         if (n > Tab ->nTypes) return FALSE;
1027         if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
1028     }
1029
1030     return (n == Tab ->nTypes);
1031 }
1032
1033
1034 static
1035 const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1036 {
1037     cmsUInt32Number n;
1038
1039     for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1040
1041         const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1042
1043         if (IsV4 ^ Tab -> IsV4) continue;
1044         if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1045
1046         if (CheckOne(Tab, Lut)) return Tab;
1047     }
1048
1049     return NULL;
1050 }
1051
1052
1053 // Does convert a transform into a device link profile
1054 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1055 {
1056     cmsHPROFILE hProfile = NULL;
1057     cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;
1058     cmsUInt32Number ColorSpaceBitsIn, ColorSpaceBitsOut;
1059     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1060     cmsPipeline* LUT = NULL;
1061     cmsStage* mpe;
1062     cmsContext ContextID = cmsGetTransformContextID(hTransform);
1063     const cmsAllowedLUT* AllowedLUT;
1064     cmsTagSignature DestinationTag;
1065     cmsProfileClassSignature deviceClass; 
1066
1067     _cmsAssert(hTransform != NULL);
1068
1069     // Get the first mpe to check for named color
1070     mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);
1071
1072     // Check if is a named color transform
1073     if (mpe != NULL) {
1074
1075         if (cmsStageType(mpe) == cmsSigNamedColorElemType) {
1076             return CreateNamedColorDevicelink(hTransform);
1077         }
1078     }
1079
1080     // First thing to do is to get a copy of the transformation
1081     LUT = cmsPipelineDup(xform ->Lut);
1082     if (LUT == NULL) return NULL;
1083
1084     // Time to fix the Lab2/Lab4 issue.
1085     if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1086
1087         if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
1088             goto Error;
1089     }
1090
1091     // On the output side too
1092     if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1093
1094         if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
1095             goto Error;
1096     }
1097
1098
1099     hProfile = cmsCreateProfilePlaceholder(ContextID);
1100     if (!hProfile) goto Error;                    // can't allocate
1101
1102     cmsSetProfileVersion(hProfile, Version);
1103
1104     FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);
1105
1106     // Optimize the LUT and precalculate a devicelink
1107
1108     ChansIn  = cmsChannelsOf(xform -> EntryColorSpace);
1109     ChansOut = cmsChannelsOf(xform -> ExitColorSpace);
1110
1111     ColorSpaceBitsIn  = _cmsLCMScolorSpace(xform -> EntryColorSpace);
1112     ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
1113
1114     FrmIn  = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1115     FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1116
1117     deviceClass = cmsGetDeviceClass(hProfile);
1118
1119      if (deviceClass == cmsSigOutputClass)
1120          DestinationTag = cmsSigBToA0Tag;
1121      else
1122          DestinationTag = cmsSigAToB0Tag;
1123
1124     // Check if the profile/version can store the result
1125     if (dwFlags & cmsFLAGS_FORCE_CLUT)
1126         AllowedLUT = NULL;
1127     else
1128         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1129
1130     if (AllowedLUT == NULL) {
1131
1132         // Try to optimize
1133         _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1134         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1135
1136     }
1137
1138     // If no way, then force CLUT that for sure can be written
1139     if (AllowedLUT == NULL) {
1140
1141         dwFlags |= cmsFLAGS_FORCE_CLUT;
1142         _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1143
1144         // Put identity curves if needed
1145         if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType)
1146              if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
1147                  goto Error;
1148
1149         if (cmsPipelineGetPtrToLastStage(LUT) ->Type != cmsSigCurveSetElemType)
1150              if (!cmsPipelineInsertStage(LUT, cmsAT_END,   _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
1151                  goto Error;
1152
1153         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1154     }
1155
1156     // Somethings is wrong...
1157     if (AllowedLUT == NULL) {
1158         goto Error;
1159     }
1160
1161
1162     if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1163                      cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);
1164
1165     // Tag profile with information
1166     if (!SetTextTags(hProfile, L"devicelink")) goto Error;
1167
1168     // Store result
1169     if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
1170
1171
1172     if (xform -> InputColorant != NULL) {
1173            if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
1174     }
1175
1176     if (xform -> OutputColorant != NULL) {
1177            if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
1178     }
1179
1180     if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
1181         if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
1182     }
1183
1184     // Set the white point
1185     if (deviceClass == cmsSigInputClass) {
1186         if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;
1187     }
1188     else {
1189          if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;
1190     }
1191
1192   
1193     // Per 7.2.15 in spec 4.3
1194     cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);
1195
1196     cmsPipelineFree(LUT);
1197     return hProfile;
1198
1199 Error:
1200     if (LUT != NULL) cmsPipelineFree(LUT);
1201     cmsCloseProfile(hProfile);
1202     return NULL;
1203 }