Check for SYS/GL during library init. Reason is that
[AROS.git] / workbench / libs / lcms2 / src / cmsvirt.c
blob953377a85b9f78766e85ab6aaeccd3843e5ddf32
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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
24 //---------------------------------------------------------------------------------
27 #include "lcms2_internal.h"
29 // Virtual (built-in) profiles
30 // -----------------------------------------------------------------------------------
32 static
33 cmsBool SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description)
35 cmsMLU *DescriptionMLU, *CopyrightMLU;
36 cmsBool rc = FALSE;
37 cmsContext ContextID = cmsGetProfileContextID(hProfile);
39 DescriptionMLU = cmsMLUalloc(ContextID, 1);
40 CopyrightMLU = cmsMLUalloc(ContextID, 1);
42 if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error;
44 if (!cmsMLUsetWide(DescriptionMLU, "en", "US", Description)) goto Error;
45 if (!cmsMLUsetWide(CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error;
47 if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error;
48 if (!cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error;
50 rc = TRUE;
52 Error:
54 if (DescriptionMLU)
55 cmsMLUfree(DescriptionMLU);
56 if (CopyrightMLU)
57 cmsMLUfree(CopyrightMLU);
58 return rc;
62 static
63 cmsBool SetSeqDescTag(cmsHPROFILE hProfile, const char* Model)
65 cmsBool rc = FALSE;
66 cmsContext ContextID = cmsGetProfileContextID(hProfile);
67 cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1);
69 if (Seq == NULL) return FALSE;
71 Seq->seq[0].deviceMfg = (cmsSignature) 0;
72 Seq->seq[0].deviceModel = (cmsSignature) 0;
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
81 Seq->seq[0].technology = (cmsTechnologySignature) 0;
83 cmsMLUsetASCII( Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS");
84 cmsMLUsetASCII( Seq->seq[0].Model, cmsNoLanguage, cmsNoCountry, Model);
86 if (!_cmsWriteProfileSequence(hProfile, Seq)) goto Error;
88 rc = TRUE;
90 Error:
91 if (Seq)
92 cmsFreeProfileSequenceDescription(Seq);
94 return rc;
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])
106 cmsHPROFILE hICC;
107 cmsMAT3 MColorants;
108 cmsCIEXYZTRIPLE Colorants;
109 cmsCIExyY MaxWhite;
110 cmsMAT3 CHAD;
111 cmsCIEXYZ WhitePointXYZ;
113 hICC = cmsCreateProfilePlaceholder(ContextID);
114 if (!hICC) // can't allocate
115 return NULL;
117 cmsSetProfileVersion(hICC, 4.3);
119 cmsSetDeviceClass(hICC, cmsSigDisplayClass);
120 cmsSetColorSpace(hICC, cmsSigRgbData);
121 cmsSetPCS(hICC, cmsSigXYZData);
123 cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
126 // Implement profile using following tags:
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
141 if (!SetTextTags(hICC, L"RGB built-in")) goto Error;
143 if (WhitePoint) {
145 if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
147 cmsxyY2XYZ(&WhitePointXYZ, WhitePoint);
148 _cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ());
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;
154 if (WhitePoint && Primaries) {
156 MaxWhite.x = WhitePoint -> x;
157 MaxWhite.y = WhitePoint -> y;
158 MaxWhite.Y = 1.0;
160 if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error;
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];
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];
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];
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;
180 if (TransferFunction) {
182 // Tries to minimize space. Thanks to Richard Hughes for this nice idea
183 if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error;
185 if (TransferFunction[1] == TransferFunction[0]) {
187 if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error;
189 } else {
191 if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;
194 if (TransferFunction[2] == TransferFunction[0]) {
196 if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error;
198 } else {
200 if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;
204 if (Primaries) {
205 if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error;
209 return hICC;
211 Error:
212 if (hICC)
213 cmsCloseProfile(hICC);
214 return NULL;
217 cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint,
218 const cmsCIExyYTRIPLE* Primaries,
219 cmsToneCurve* const TransferFunction[3])
221 return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction);
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)
231 cmsHPROFILE hICC;
232 cmsCIEXYZ tmp;
234 hICC = cmsCreateProfilePlaceholder(ContextID);
235 if (!hICC) // can't allocate
236 return NULL;
238 cmsSetProfileVersion(hICC, 4.3);
240 cmsSetDeviceClass(hICC, cmsSigDisplayClass);
241 cmsSetColorSpace(hICC, cmsSigGrayData);
242 cmsSetPCS(hICC, cmsSigXYZData);
243 cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
246 // Implement profile using following tags:
248 // 1 cmsSigProfileDescriptionTag
249 // 2 cmsSigMediaWhitePointTag
250 // 3 cmsSigGrayTRCTag
252 // This conforms a standard Gray DisplayProfile
254 // Fill-in the tags
256 if (!SetTextTags(hICC, L"gray built-in")) goto Error;
259 if (WhitePoint) {
261 cmsxyY2XYZ(&tmp, WhitePoint);
262 if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error;
265 if (TransferFunction) {
267 if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error;
270 return hICC;
272 Error:
273 if (hICC)
274 cmsCloseProfile(hICC);
275 return NULL;
280 cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint,
281 const cmsToneCurve* TransferFunction)
283 return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction);
286 // This is a devicelink operating in the target colorspace with as many transfer functions as components
288 cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,
289 cmsColorSpaceSignature ColorSpace,
290 cmsToneCurve* const TransferFunctions[])
292 cmsHPROFILE hICC;
293 cmsPipeline* Pipeline;
294 int nChannels;
296 hICC = cmsCreateProfilePlaceholder(ContextID);
297 if (!hICC)
298 return NULL;
300 cmsSetProfileVersion(hICC, 4.3);
302 cmsSetDeviceClass(hICC, cmsSigLinkClass);
303 cmsSetColorSpace(hICC, ColorSpace);
304 cmsSetPCS(hICC, ColorSpace);
306 cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
308 // Set up channels
309 nChannels = cmsChannelsOf(ColorSpace);
311 // Creates a Pipeline with prelinearization step only
312 Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
313 if (Pipeline == NULL) goto Error;
316 // Copy tables to Pipeline
317 if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))
318 goto Error;
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;
325 // Pipeline is already on virtual profile
326 cmsPipelineFree(Pipeline);
328 // Ok, done
329 return hICC;
331 Error:
332 cmsPipelineFree(Pipeline);
333 if (hICC)
334 cmsCloseProfile(hICC);
337 return NULL;
340 cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace,
341 cmsToneCurve* const TransferFunctions[])
343 return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions);
346 // Ink-limiting algorithm
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
358 // C = Ratio * C
359 // M = Ratio * M
360 // Y = Ratio * Y
361 // K: Does not change
363 static
364 int InkLimitingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
366 cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
367 cmsFloat64Number SumCMY, SumCMYK, Ratio;
369 InkLimit = (InkLimit * 655.35);
371 SumCMY = In[0] + In[1] + In[2];
372 SumCMYK = SumCMY + In[3];
374 if (SumCMYK > InkLimit) {
376 Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
377 if (Ratio < 0)
378 Ratio = 0;
380 else Ratio = 1;
382 Out[0] = _cmsQuickSaturateWord(In[0] * Ratio); // C
383 Out[1] = _cmsQuickSaturateWord(In[1] * Ratio); // M
384 Out[2] = _cmsQuickSaturateWord(In[2] * Ratio); // Y
386 Out[3] = In[3]; // K (untouched)
388 return TRUE;
391 // This is a devicelink operating in CMYK for ink-limiting
393 cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,
394 cmsColorSpaceSignature ColorSpace,
395 cmsFloat64Number Limit)
397 cmsHPROFILE hICC;
398 cmsPipeline* LUT;
399 cmsStage* CLUT;
400 int nChannels;
402 if (ColorSpace != cmsSigCmykData) {
403 cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
404 return NULL;
407 if (Limit < 0.0 || Limit > 400) {
409 cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400");
410 if (Limit < 0) Limit = 0;
411 if (Limit > 400) Limit = 400;
415 hICC = cmsCreateProfilePlaceholder(ContextID);
416 if (!hICC) // can't allocate
417 return NULL;
419 cmsSetProfileVersion(hICC, 4.3);
421 cmsSetDeviceClass(hICC, cmsSigLinkClass);
422 cmsSetColorSpace(hICC, ColorSpace);
423 cmsSetPCS(hICC, ColorSpace);
425 cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
428 // Creates a Pipeline with 3D grid only
429 LUT = cmsPipelineAlloc(ContextID, 4, 4);
430 if (LUT == NULL) goto Error;
433 nChannels = cmsChannelsOf(ColorSpace);
435 CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL);
436 if (CLUT == NULL) goto Error;
438 if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error;
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;
445 // Create tags
446 if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error;
448 if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error;
449 if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error;
451 // cmsPipeline is already on virtual profile
452 cmsPipelineFree(LUT);
454 // Ok, done
455 return hICC;
457 Error:
458 if (LUT != NULL)
459 cmsPipelineFree(LUT);
461 if (hICC != NULL)
462 cmsCloseProfile(hICC);
464 return NULL;
467 cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit)
469 return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit);
473 // Creates a fake Lab identity.
474 cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
476 cmsHPROFILE hProfile;
477 cmsPipeline* LUT = NULL;
479 hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
480 if (hProfile == NULL) return NULL;
482 cmsSetProfileVersion(hProfile, 2.1);
484 cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
485 cmsSetColorSpace(hProfile, cmsSigLabData);
486 cmsSetPCS(hProfile, cmsSigLabData);
488 if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL;
490 // An identity LUT is all we need
491 LUT = cmsPipelineAlloc(ContextID, 3, 3);
492 if (LUT == NULL) goto Error;
494 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
495 goto Error;
497 if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
498 cmsPipelineFree(LUT);
500 return hProfile;
502 Error:
504 if (LUT != NULL)
505 cmsPipelineFree(LUT);
507 if (hProfile != NULL)
508 cmsCloseProfile(hProfile);
510 return NULL;
514 cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint)
516 return cmsCreateLab2ProfileTHR(NULL, WhitePoint);
520 // Creates a fake Lab V4 identity.
521 cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
523 cmsHPROFILE hProfile;
524 cmsPipeline* LUT = NULL;
526 hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
527 if (hProfile == NULL) return NULL;
529 cmsSetProfileVersion(hProfile, 4.3);
531 cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
532 cmsSetColorSpace(hProfile, cmsSigLabData);
533 cmsSetPCS(hProfile, cmsSigLabData);
535 if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error;
537 // An empty LUTs is all we need
538 LUT = cmsPipelineAlloc(ContextID, 3, 3);
539 if (LUT == NULL) goto Error;
541 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
542 goto Error;
544 if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
545 cmsPipelineFree(LUT);
547 return hProfile;
549 Error:
551 if (LUT != NULL)
552 cmsPipelineFree(LUT);
554 if (hProfile != NULL)
555 cmsCloseProfile(hProfile);
557 return NULL;
560 cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint)
562 return cmsCreateLab4ProfileTHR(NULL, WhitePoint);
566 // Creates a fake XYZ identity
567 cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID)
569 cmsHPROFILE hProfile;
570 cmsPipeline* LUT = NULL;
572 hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL);
573 if (hProfile == NULL) return NULL;
575 cmsSetProfileVersion(hProfile, 4.3);
577 cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
578 cmsSetColorSpace(hProfile, cmsSigXYZData);
579 cmsSetPCS(hProfile, cmsSigXYZData);
581 if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error;
583 // An identity LUT is all we need
584 LUT = cmsPipelineAlloc(ContextID, 3, 3);
585 if (LUT == NULL) goto Error;
587 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
588 goto Error;
590 if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
591 cmsPipelineFree(LUT);
593 return hProfile;
595 Error:
597 if (LUT != NULL)
598 cmsPipelineFree(LUT);
600 if (hProfile != NULL)
601 cmsCloseProfile(hProfile);
603 return NULL;
607 cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void)
609 return cmsCreateXYZProfileTHR(NULL);
613 //sRGB Curves are defined by:
615 //If R’sRGB,G’sRGB, B’sRGB < 0.04045
617 // R = R’sRGB / 12.92
618 // G = G’sRGB / 12.92
619 // B = B’sRGB / 12.92
622 //else if R’sRGB,G’sRGB, B’sRGB >= 0.04045
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
628 static
629 cmsToneCurve* Build_sRGBGamma(cmsContext ContextID)
631 cmsFloat64Number Parameters[5];
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;
639 return cmsBuildParametricToneCurve(ContextID, 4, Parameters);
642 // Create the ICC virtual profile for sRGB space
643 cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID)
645 cmsCIExyY D65;
646 cmsCIExyYTRIPLE Rec709Primaries = {
647 {0.6400, 0.3300, 1.0},
648 {0.3000, 0.6000, 1.0},
649 {0.1500, 0.0600, 1.0}
651 cmsToneCurve* Gamma22[3];
652 cmsHPROFILE hsRGB;
654 cmsWhitePointFromTemp(&D65, 6504);
655 Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID);
656 if (Gamma22[0] == NULL) return NULL;
658 hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22);
659 cmsFreeToneCurve(Gamma22[0]);
660 if (hsRGB == NULL) return NULL;
662 if (!SetTextTags(hsRGB, L"sRGB built-in")) {
663 cmsCloseProfile(hsRGB);
664 return NULL;
667 return hsRGB;
670 cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void)
672 return cmsCreate_sRGBProfileTHR(NULL);
677 typedef struct {
678 cmsFloat64Number Brightness;
679 cmsFloat64Number Contrast;
680 cmsFloat64Number Hue;
681 cmsFloat64Number Saturation;
682 cmsCIEXYZ WPsrc, WPdest;
684 } BCHSWADJUSTS, *LPBCHSWADJUSTS;
687 static
688 int bchswSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
690 cmsCIELab LabIn, LabOut;
691 cmsCIELCh LChIn, LChOut;
692 cmsCIEXYZ XYZ;
693 LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
696 cmsLabEncoded2Float(&LabIn, In);
699 cmsLab2LCh(&LChIn, &LabIn);
701 // Do some adjusts on LCh
703 LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
704 LChOut.C = LChIn.C + bchsw -> Saturation;
705 LChOut.h = LChIn.h + bchsw -> Hue;
708 cmsLCh2Lab(&LabOut, &LChOut);
710 // Move white point in Lab
712 cmsLab2XYZ(&bchsw ->WPsrc, &XYZ, &LabOut);
713 cmsXYZ2Lab(&bchsw ->WPdest, &LabOut, &XYZ);
715 // Back to encoded
717 cmsFloat2LabEncoded(Out, &LabOut);
719 return TRUE;
723 // Creates an abstract profile operating in Lab space for Brightness,
724 // contrast, Saturation and white point displacement
726 cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
727 int nLUTPoints,
728 cmsFloat64Number Bright,
729 cmsFloat64Number Contrast,
730 cmsFloat64Number Hue,
731 cmsFloat64Number Saturation,
732 int TempSrc,
733 int TempDest)
735 cmsHPROFILE hICC;
736 cmsPipeline* Pipeline;
737 BCHSWADJUSTS bchsw;
738 cmsCIExyY WhitePnt;
739 cmsStage* CLUT;
740 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
741 int i;
743 bchsw.Brightness = Bright;
744 bchsw.Contrast = Contrast;
745 bchsw.Hue = Hue;
746 bchsw.Saturation = Saturation;
748 cmsWhitePointFromTemp(&WhitePnt, TempSrc );
749 cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
751 cmsWhitePointFromTemp(&WhitePnt, TempDest);
752 cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
754 hICC = cmsCreateProfilePlaceholder(ContextID);
755 if (!hICC) // can't allocate
756 return NULL;
759 cmsSetDeviceClass(hICC, cmsSigAbstractClass);
760 cmsSetColorSpace(hICC, cmsSigLabData);
761 cmsSetPCS(hICC, cmsSigLabData);
763 cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
765 // Creates a Pipeline with 3D grid only
766 Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
767 if (Pipeline == NULL) {
768 cmsCloseProfile(hICC);
769 return NULL;
772 for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
773 CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
774 if (CLUT == NULL) return NULL;
777 if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
779 // Shouldn't reach here
780 goto Error;
783 if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
784 goto Error;
787 // Create tags
788 if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
790 cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
792 cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
794 // Pipeline is already on virtual profile
795 cmsPipelineFree(Pipeline);
797 // Ok, done
798 return hICC;
800 Error:
801 cmsPipelineFree(Pipeline);
802 cmsCloseProfile(hICC);
803 return NULL;
807 CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints,
808 cmsFloat64Number Bright,
809 cmsFloat64Number Contrast,
810 cmsFloat64Number Hue,
811 cmsFloat64Number Saturation,
812 int TempSrc,
813 int TempDest)
815 return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest);
819 // Creates a fake NULL profile. This profile return 1 channel as always 0.
820 // Is useful only for gamut checking tricks
821 cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)
823 cmsHPROFILE hProfile;
824 cmsPipeline* LUT = NULL;
825 cmsStage* PostLin;
826 cmsToneCurve* EmptyTab;
827 cmsUInt16Number Zero[2] = { 0, 0 };
829 hProfile = cmsCreateProfilePlaceholder(ContextID);
830 if (!hProfile) // can't allocate
831 return NULL;
833 cmsSetProfileVersion(hProfile, 4.3);
835 if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
839 cmsSetDeviceClass(hProfile, cmsSigOutputClass);
840 cmsSetColorSpace(hProfile, cmsSigGrayData);
841 cmsSetPCS(hProfile, cmsSigLabData);
843 // An empty LUTs is all we need
844 LUT = cmsPipelineAlloc(ContextID, 1, 1);
845 if (LUT == NULL) goto Error;
847 EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
848 PostLin = cmsStageAllocToneCurves(ContextID, 1, &EmptyTab);
849 cmsFreeToneCurve(EmptyTab);
851 if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
852 goto Error;
854 if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
855 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
857 cmsPipelineFree(LUT);
858 return hProfile;
860 Error:
862 if (LUT != NULL)
863 cmsPipelineFree(LUT);
865 if (hProfile != NULL)
866 cmsCloseProfile(hProfile);
868 return NULL;
871 cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)
873 return cmsCreateNULLProfileTHR(NULL);
877 static
878 int IsPCS(cmsColorSpaceSignature ColorSpace)
880 return (ColorSpace == cmsSigXYZData ||
881 ColorSpace == cmsSigLabData);
885 static
886 void FixColorSpaces(cmsHPROFILE hProfile,
887 cmsColorSpaceSignature ColorSpace,
888 cmsColorSpaceSignature PCS,
889 cmsUInt32Number dwFlags)
891 if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
893 if (IsPCS(ColorSpace) && IsPCS(PCS)) {
895 cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
896 cmsSetColorSpace(hProfile, ColorSpace);
897 cmsSetPCS(hProfile, PCS);
898 return;
901 if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
903 cmsSetDeviceClass(hProfile, cmsSigOutputClass);
904 cmsSetPCS(hProfile, ColorSpace);
905 cmsSetColorSpace(hProfile, PCS);
906 return;
909 if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
911 cmsSetDeviceClass(hProfile, cmsSigInputClass);
912 cmsSetColorSpace(hProfile, ColorSpace);
913 cmsSetPCS(hProfile, PCS);
914 return;
918 cmsSetDeviceClass(hProfile, cmsSigLinkClass);
919 cmsSetColorSpace(hProfile, ColorSpace);
920 cmsSetPCS(hProfile, PCS);
925 // This function creates a named color profile dumping all the contents of transform to a single profile
926 // In this way, LittleCMS may be used to "group" several named color databases into a single profile.
927 // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
928 // is the normal PCS for named color profiles.
929 static
930 cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
932 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
933 cmsHPROFILE hICC = NULL;
934 int i, nColors;
935 cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
937 // Create an empty placeholder
938 hICC = cmsCreateProfilePlaceholder(v->ContextID);
939 if (hICC == NULL) return NULL;
941 // Critical information
942 cmsSetDeviceClass(hICC, cmsSigNamedColorClass);
943 cmsSetColorSpace(hICC, v ->ExitColorSpace);
944 cmsSetPCS(hICC, cmsSigLabData);
946 // Tag profile with information
947 if (!SetTextTags(hICC, L"Named color devicelink")) goto Error;
949 Original = cmsGetNamedColorList(xform);
950 if (Original == NULL) goto Error;
952 nColors = cmsNamedColorCount(Original);
953 nc2 = cmsDupNamedColorList(Original);
954 if (nc2 == NULL) goto Error;
956 // Colorant count now depends on the output space
957 nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);
959 // Make sure we have proper formatters
960 cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
961 FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
962 | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace)));
964 // Apply the transfor to colorants.
965 for (i=0; i < nColors; i++) {
966 cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
969 if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
970 cmsFreeNamedColorList(nc2);
972 return hICC;
974 Error:
975 if (hICC != NULL) cmsCloseProfile(hICC);
976 return NULL;
980 // This structure holds information about which MPU can be stored on a profile based on the version
982 typedef struct {
983 cmsBool IsV4; // Is a V4 tag?
984 cmsTagSignature RequiredTag; // Set to 0 for both types
985 cmsTagTypeSignature LutType; // The LUT type
986 int nTypes; // Number of types (up to 5)
987 cmsStageSignature MpeTypes[5]; // 5 is the maximum number
989 } cmsAllowedLUT;
991 static const cmsAllowedLUT AllowedLUTTypes[] = {
993 { FALSE, 0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType}},
994 { FALSE, 0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType}},
995 { FALSE, 0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType}},
996 { TRUE , 0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType }},
997 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
998 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
999 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1000 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }},
1001 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1002 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
1003 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
1006 #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
1008 // Check a single entry
1009 static
1010 cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
1012 cmsStage* mpe;
1013 int n;
1015 for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
1017 if (n > Tab ->nTypes) return FALSE;
1018 if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
1021 return (n == Tab ->nTypes);
1025 static
1026 const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1028 cmsUInt32Number n;
1030 for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1032 const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1034 if (IsV4 ^ Tab -> IsV4) continue;
1035 if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1037 if (CheckOne(Tab, Lut)) return Tab;
1040 return NULL;
1044 // Does convert a transform into a device link profile
1045 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1047 cmsHPROFILE hProfile = NULL;
1048 cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;
1049 cmsUInt32Number ColorSpaceBitsIn, ColorSpaceBitsOut;
1050 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1051 cmsPipeline* LUT = NULL;
1052 cmsStage* mpe;
1053 cmsContext ContextID = cmsGetTransformContextID(hTransform);
1054 const cmsAllowedLUT* AllowedLUT;
1055 cmsTagSignature DestinationTag;
1056 cmsProfileClassSignature deviceClass;
1058 _cmsAssert(hTransform != NULL);
1060 // Get the first mpe to check for named color
1061 mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);
1063 // Check if is a named color transform
1064 if (mpe != NULL) {
1066 if (cmsStageType(mpe) == cmsSigNamedColorElemType) {
1067 return CreateNamedColorDevicelink(hTransform);
1071 // First thing to do is to get a copy of the transformation
1072 LUT = cmsPipelineDup(xform ->Lut);
1073 if (LUT == NULL) return NULL;
1075 // Time to fix the Lab2/Lab4 issue.
1076 if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1078 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
1079 goto Error;
1082 // On the output side too
1083 if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1085 if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
1086 goto Error;
1090 hProfile = cmsCreateProfilePlaceholder(ContextID);
1091 if (!hProfile) goto Error; // can't allocate
1093 cmsSetProfileVersion(hProfile, Version);
1095 FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);
1097 // Optimize the LUT and precalculate a devicelink
1099 ChansIn = cmsChannelsOf(xform -> EntryColorSpace);
1100 ChansOut = cmsChannelsOf(xform -> ExitColorSpace);
1102 ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace);
1103 ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
1105 FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1106 FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1108 deviceClass = cmsGetDeviceClass(hProfile);
1110 if (deviceClass == cmsSigOutputClass)
1111 DestinationTag = cmsSigBToA0Tag;
1112 else
1113 DestinationTag = cmsSigAToB0Tag;
1115 // Check if the profile/version can store the result
1116 if (dwFlags & cmsFLAGS_FORCE_CLUT)
1117 AllowedLUT = NULL;
1118 else
1119 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1121 if (AllowedLUT == NULL) {
1123 // Try to optimize
1124 _cmsOptimizePipeline(&LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1125 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1129 // If no way, then force CLUT that for sure can be written
1130 if (AllowedLUT == NULL) {
1132 dwFlags |= cmsFLAGS_FORCE_CLUT;
1133 _cmsOptimizePipeline(&LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1135 // Put identity curves if needed
1136 if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType)
1137 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
1138 goto Error;
1140 if (cmsPipelineGetPtrToLastStage(LUT) ->Type != cmsSigCurveSetElemType)
1141 if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
1142 goto Error;
1144 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1147 // Somethings is wrong...
1148 if (AllowedLUT == NULL) {
1149 goto Error;
1153 if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1154 cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);
1156 // Tag profile with information
1157 if (!SetTextTags(hProfile, L"devicelink")) goto Error;
1159 // Store result
1160 if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
1163 if (xform -> InputColorant != NULL) {
1164 if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
1167 if (xform -> OutputColorant != NULL) {
1168 if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
1171 if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
1172 if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
1175 // Set the white point
1176 if (deviceClass == cmsSigInputClass) {
1177 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;
1179 else {
1180 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;
1184 // Per 7.2.15 in spec 4.3
1185 cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);
1187 cmsPipelineFree(LUT);
1188 return hProfile;
1190 Error:
1191 if (LUT != NULL) cmsPipelineFree(LUT);
1192 cmsCloseProfile(hProfile);
1193 return NULL;