Tizen 2.0 Release
[external/lcms.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     // 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
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 
998 const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
999 {
1000     cmsUInt32Number 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
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 }