Imported Upstream version 2.4
[platform/upstream/lcms2.git] / src / cmsvirt.c
1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2011 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         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
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.
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.3);
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
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
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.3);
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
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
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
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.3);
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
451 cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit)
452 {
453     return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit);
454 }
455
456
457 // Creates a fake Lab identity.
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
497 cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint)
498 {
499     return cmsCreateLab2ProfileTHR(NULL, WhitePoint);
500 }
501
502
503 // Creates a fake Lab V4 identity.
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.3);
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
542 cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint)
543 {
544     return cmsCreateLab4ProfileTHR(NULL, WhitePoint);
545 }
546
547
548 // Creates a fake XYZ identity
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.3);
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
588 cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void)
589 {
590     return cmsCreateXYZProfileTHR(NULL);
591 }
592
593
594 //sRGB Curves are defined by:
595 //
596 //If  R\92sRGB,G\92sRGB, B\92sRGB < 0.04045
597 //
598 //    R =  R\92sRGB / 12.92
599 //    G =  G\92sRGB / 12.92
600 //    B =  B\92sRGB / 12.92
601 //
602 //
603 //else if  R\92sRGB,G\92sRGB, B\92sRGB >= 0.04045
604 //
605 //    R = ((R\92sRGB + 0.055) / 1.055)^2.4
606 //    G = ((G\92sRGB + 0.055) / 1.055)^2.4
607 //    B = ((B\92sRGB + 0.055) / 1.055)^2.4
608
609 static
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
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
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
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
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
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
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.3);
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
849 cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)
850 {
851     return cmsCreateNULLProfileTHR(NULL);
852 }
853
854
855 static
856 int IsPCS(cmsColorSpaceSignature ColorSpace)
857 {
858     return (ColorSpace == cmsSigXYZData ||
859             ColorSpace == cmsSigLabData);
860 }
861
862
863 static
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
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     // Make sure we have proper formatters
938     cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
939         FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
940         | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace)));
941
942     // Apply the transfor to colorants.
943     for (i=0; i < nColors; i++) {
944         cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
945     }
946
947     if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
948     cmsFreeNamedColorList(nc2);
949
950     return hICC;
951
952 Error:
953     if (hICC != NULL) cmsCloseProfile(hICC);
954     return NULL;
955 }
956
957
958 // This structure holds information about which MPU can be stored on a profile based on the version
959
960 typedef struct {
961     cmsBool              IsV4;             // Is a V4 tag?
962     cmsTagSignature      RequiredTag;      // Set to 0 for both types
963     cmsTagTypeSignature  LutType;          // The LUT type
964     int                  nTypes;           // Number of types (up to 5)
965     cmsStageSignature    MpeTypes[5];      // 5 is the maximum number
966
967 } cmsAllowedLUT;
968
969 static const cmsAllowedLUT AllowedLUTTypes[] = {
970
971     { FALSE, 0,              cmsSigLut16Type,    4,  { cmsSigMatrixElemType,  cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType}},
972     { FALSE, 0,              cmsSigLut16Type,    3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType}},
973     { TRUE , 0,              cmsSigLutAtoBType,  1,  { cmsSigCurveSetElemType }},
974     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  3,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
975     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType   } },
976     { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType,  5,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
977     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  1,  { cmsSigCurveSetElemType }},
978     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  3,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
979     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  3,  { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
980     { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType,  5,  { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
981 };
982
983 #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
984
985 // Check a single entry
986 static
987 cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
988 {
989     cmsStage* mpe;
990     int n;
991
992     for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
993
994         if (n > Tab ->nTypes) return FALSE;
995         if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
996     }
997
998     return (n == Tab ->nTypes);
999 }
1000
1001
1002 static
1003 const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1004 {
1005     cmsUInt32Number n;
1006
1007     for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1008
1009         const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1010
1011         if (IsV4 ^ Tab -> IsV4) continue;
1012         if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1013
1014         if (CheckOne(Tab, Lut)) return Tab;
1015     }
1016
1017     return NULL;
1018 }
1019
1020
1021 // Does convert a transform into a device link profile
1022 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1023 {
1024     cmsHPROFILE hProfile = NULL;
1025     cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;
1026     cmsUInt32Number ColorSpaceBitsIn, ColorSpaceBitsOut;
1027     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1028     cmsPipeline* LUT = NULL;
1029     cmsStage* mpe;
1030     cmsContext ContextID = cmsGetTransformContextID(hTransform);
1031     const cmsAllowedLUT* AllowedLUT;
1032     cmsTagSignature DestinationTag;
1033
1034     _cmsAssert(hTransform != NULL);
1035
1036     // Get the first mpe to check for named color
1037     mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);
1038
1039     // Check if is a named color transform
1040     if (mpe != NULL) {
1041
1042         if (cmsStageType(mpe) == cmsSigNamedColorElemType) {
1043             return CreateNamedColorDevicelink(hTransform);
1044         }
1045     }
1046
1047     // First thing to do is to get a copy of the transformation
1048     LUT = cmsPipelineDup(xform ->Lut);
1049     if (LUT == NULL) return NULL;
1050
1051     // Time to fix the Lab2/Lab4 issue.
1052     if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1053
1054         cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID));
1055     }
1056
1057     // On the output side too
1058     if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1059
1060         cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID));
1061     }
1062
1063
1064     hProfile = cmsCreateProfilePlaceholder(ContextID);
1065     if (!hProfile) goto Error;                    // can't allocate
1066
1067     cmsSetProfileVersion(hProfile, Version);
1068
1069     FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);
1070
1071     // Optimize the LUT and precalculate a devicelink
1072
1073     ChansIn  = cmsChannelsOf(xform -> EntryColorSpace);
1074     ChansOut = cmsChannelsOf(xform -> ExitColorSpace);
1075
1076     ColorSpaceBitsIn  = _cmsLCMScolorSpace(xform -> EntryColorSpace);
1077     ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
1078
1079     FrmIn  = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1080     FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1081
1082
1083      if (cmsGetDeviceClass(hProfile) == cmsSigOutputClass)
1084          DestinationTag = cmsSigBToA0Tag;
1085      else
1086          DestinationTag = cmsSigAToB0Tag;
1087
1088     // Check if the profile/version can store the result
1089     if (dwFlags & cmsFLAGS_FORCE_CLUT)
1090         AllowedLUT = NULL;
1091     else
1092         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1093
1094     if (AllowedLUT == NULL) {
1095
1096         // Try to optimize
1097         _cmsOptimizePipeline(&LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1098         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1099
1100     }
1101
1102     // If no way, then force CLUT that for sure can be written
1103     if (AllowedLUT == NULL) {
1104
1105         dwFlags |= cmsFLAGS_FORCE_CLUT;
1106         _cmsOptimizePipeline(&LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1107
1108         // Put identity curves if needed
1109         if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType)
1110              cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn));
1111
1112         if (cmsPipelineGetPtrToLastStage(LUT) ->Type != cmsSigCurveSetElemType)
1113              cmsPipelineInsertStage(LUT, cmsAT_END,   _cmsStageAllocIdentityCurves(ContextID, ChansOut));
1114
1115         AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1116     }
1117
1118     // Somethings is wrong...
1119     if (AllowedLUT == NULL) {
1120         goto Error;
1121     }
1122
1123
1124     if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1125                      cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);
1126
1127     // Tag profile with information
1128     if (!SetTextTags(hProfile, L"devicelink")) goto Error;
1129
1130     // Store result
1131     if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
1132
1133
1134     if (xform -> InputColorant != NULL) {
1135            if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
1136     }
1137
1138     if (xform -> OutputColorant != NULL) {
1139            if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
1140     }
1141
1142     if (xform ->Sequence != NULL) {
1143         if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
1144     }
1145
1146     cmsPipelineFree(LUT);
1147     return hProfile;
1148
1149 Error:
1150     if (LUT != NULL) cmsPipelineFree(LUT);
1151     cmsCloseProfile(hProfile);
1152     return NULL;
1153 }