msdasql: Check rowset pointer before assignment in ICommandText::Execute.
[wine.git] / libs / lcms2 / src / cmsvirt.c
blob951a8ee29de8e38cfbb6556b8b961fb554d01128
1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2023 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.4);
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.4);
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 cmsInt32Number nChannels;
296 hICC = cmsCreateProfilePlaceholder(ContextID);
297 if (!hICC)
298 return NULL;
300 cmsSetProfileVersion(hICC, 4.4);
302 cmsSetDeviceClass(hICC, cmsSigLinkClass);
303 cmsSetColorSpace(hICC, ColorSpace);
304 cmsSetPCS(hICC, ColorSpace);
306 cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
308 // Set up channels
309 nChannels = cmsChannelsOfColorSpace(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(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
366 cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
367 cmsFloat64Number SumCMY, SumCMYK, Ratio;
369 InkLimit = (InkLimit * 655.35);
371 SumCMY = (cmsFloat64Number) 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 cmsInt32Number 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.4);
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.4);
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.4);
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 = { 0.3127, 0.3290, 1.0 };
646 cmsCIExyYTRIPLE Rec709Primaries = {
647 {0.6400, 0.3300, 1.0},
648 {0.3000, 0.6000, 1.0},
649 {0.1500, 0.0600, 1.0}
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 cmsBool lAdjustWP;
683 cmsCIEXYZ WPsrc, WPdest;
685 } BCHSWADJUSTS, *LPBCHSWADJUSTS;
688 static
689 int bchswSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
691 cmsCIELab LabIn, LabOut;
692 cmsCIELCh LChIn, LChOut;
693 cmsCIEXYZ XYZ;
694 LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
697 cmsLabEncoded2Float(&LabIn, In);
700 cmsLab2LCh(&LChIn, &LabIn);
702 // Do some adjusts on LCh
704 LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
705 LChOut.C = LChIn.C + bchsw -> Saturation;
706 LChOut.h = LChIn.h + bchsw -> Hue;
709 cmsLCh2Lab(&LabOut, &LChOut);
711 // Move white point in Lab
712 if (bchsw->lAdjustWP) {
713 cmsLab2XYZ(&bchsw->WPsrc, &XYZ, &LabOut);
714 cmsXYZ2Lab(&bchsw->WPdest, &LabOut, &XYZ);
717 // Back to encoded
719 cmsFloat2LabEncoded(Out, &LabOut);
721 return TRUE;
725 // Creates an abstract profile operating in Lab space for Brightness,
726 // contrast, Saturation and white point displacement
728 cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
729 cmsUInt32Number nLUTPoints,
730 cmsFloat64Number Bright,
731 cmsFloat64Number Contrast,
732 cmsFloat64Number Hue,
733 cmsFloat64Number Saturation,
734 cmsUInt32Number TempSrc,
735 cmsUInt32Number TempDest)
737 cmsHPROFILE hICC;
738 cmsPipeline* Pipeline;
739 BCHSWADJUSTS bchsw;
740 cmsCIExyY WhitePnt;
741 cmsStage* CLUT;
742 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
743 cmsUInt32Number i;
745 bchsw.Brightness = Bright;
746 bchsw.Contrast = Contrast;
747 bchsw.Hue = Hue;
748 bchsw.Saturation = Saturation;
749 if (TempSrc == TempDest) {
751 bchsw.lAdjustWP = FALSE;
753 else {
754 bchsw.lAdjustWP = TRUE;
755 cmsWhitePointFromTemp(&WhitePnt, TempSrc);
756 cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
757 cmsWhitePointFromTemp(&WhitePnt, TempDest);
758 cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
762 hICC = cmsCreateProfilePlaceholder(ContextID);
763 if (!hICC) // can't allocate
764 return NULL;
766 cmsSetDeviceClass(hICC, cmsSigAbstractClass);
767 cmsSetColorSpace(hICC, cmsSigLabData);
768 cmsSetPCS(hICC, cmsSigLabData);
770 cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
772 // Creates a Pipeline with 3D grid only
773 Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
774 if (Pipeline == NULL) {
775 cmsCloseProfile(hICC);
776 return NULL;
779 for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
780 CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
781 if (CLUT == NULL) goto Error;
784 if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
786 // Shouldn't reach here
787 goto Error;
790 if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
791 goto Error;
794 // Create tags
795 if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
797 cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
799 cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
801 // Pipeline is already on virtual profile
802 cmsPipelineFree(Pipeline);
804 // Ok, done
805 return hICC;
807 Error:
808 cmsPipelineFree(Pipeline);
809 cmsCloseProfile(hICC);
810 return NULL;
814 CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints,
815 cmsFloat64Number Bright,
816 cmsFloat64Number Contrast,
817 cmsFloat64Number Hue,
818 cmsFloat64Number Saturation,
819 cmsUInt32Number TempSrc,
820 cmsUInt32Number TempDest)
822 return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest);
826 // Creates a fake NULL profile. This profile return 1 channel as always 0.
827 // Is useful only for gamut checking tricks
828 cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)
830 cmsHPROFILE hProfile;
831 cmsPipeline* LUT = NULL;
832 cmsStage* PostLin;
833 cmsStage* OutLin;
834 cmsToneCurve* EmptyTab[3];
835 cmsUInt16Number Zero[2] = { 0, 0 };
836 const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
838 hProfile = cmsCreateProfilePlaceholder(ContextID);
839 if (!hProfile) // can't allocate
840 return NULL;
842 cmsSetProfileVersion(hProfile, 4.4);
844 if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
847 cmsSetDeviceClass(hProfile, cmsSigOutputClass);
848 cmsSetColorSpace(hProfile, cmsSigGrayData);
849 cmsSetPCS(hProfile, cmsSigLabData);
851 // Create a valid ICC 4 structure
852 LUT = cmsPipelineAlloc(ContextID, 3, 1);
853 if (LUT == NULL) goto Error;
855 EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
856 PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab);
857 OutLin = cmsStageAllocToneCurves(ContextID, 1, EmptyTab);
858 cmsFreeToneCurve(EmptyTab[0]);
860 if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
861 goto Error;
863 if (!cmsPipelineInsertStage(LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))
864 goto Error;
866 if (!cmsPipelineInsertStage(LUT, cmsAT_END, OutLin))
867 goto Error;
869 if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
870 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
872 cmsPipelineFree(LUT);
873 return hProfile;
875 Error:
877 if (LUT != NULL)
878 cmsPipelineFree(LUT);
880 if (hProfile != NULL)
881 cmsCloseProfile(hProfile);
883 return NULL;
886 cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)
888 return cmsCreateNULLProfileTHR(NULL);
892 static
893 int IsPCS(cmsColorSpaceSignature ColorSpace)
895 return (ColorSpace == cmsSigXYZData ||
896 ColorSpace == cmsSigLabData);
900 static
901 void FixColorSpaces(cmsHPROFILE hProfile,
902 cmsColorSpaceSignature ColorSpace,
903 cmsColorSpaceSignature PCS,
904 cmsUInt32Number dwFlags)
906 if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
908 if (IsPCS(ColorSpace) && IsPCS(PCS)) {
910 cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
911 cmsSetColorSpace(hProfile, ColorSpace);
912 cmsSetPCS(hProfile, PCS);
913 return;
916 if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
918 cmsSetDeviceClass(hProfile, cmsSigOutputClass);
919 cmsSetPCS(hProfile, ColorSpace);
920 cmsSetColorSpace(hProfile, PCS);
921 return;
924 if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
926 cmsSetDeviceClass(hProfile, cmsSigInputClass);
927 cmsSetColorSpace(hProfile, ColorSpace);
928 cmsSetPCS(hProfile, PCS);
929 return;
933 cmsSetDeviceClass(hProfile, cmsSigLinkClass);
934 cmsSetColorSpace(hProfile, ColorSpace);
935 cmsSetPCS(hProfile, PCS);
940 // This function creates a named color profile dumping all the contents of transform to a single profile
941 // In this way, LittleCMS may be used to "group" several named color databases into a single profile.
942 // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
943 // is the normal PCS for named color profiles.
944 static
945 cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
947 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
948 cmsHPROFILE hICC = NULL;
949 cmsUInt32Number i, nColors;
950 cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
952 // Create an empty placeholder
953 hICC = cmsCreateProfilePlaceholder(v->ContextID);
954 if (hICC == NULL) return NULL;
956 // Critical information
957 cmsSetDeviceClass(hICC, cmsSigNamedColorClass);
958 cmsSetColorSpace(hICC, v ->ExitColorSpace);
959 cmsSetPCS(hICC, cmsSigLabData);
961 // Tag profile with information
962 if (!SetTextTags(hICC, L"Named color devicelink")) goto Error;
964 Original = cmsGetNamedColorList(xform);
965 if (Original == NULL) goto Error;
967 nColors = cmsNamedColorCount(Original);
968 nc2 = cmsDupNamedColorList(Original);
969 if (nc2 == NULL) goto Error;
971 // Colorant count now depends on the output space
972 nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);
974 // Make sure we have proper formatters
975 cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
976 FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
977 | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOfColorSpace(v ->ExitColorSpace)));
979 // Apply the transfor to colorants.
980 for (i=0; i < nColors; i++) {
981 cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
984 if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
985 cmsFreeNamedColorList(nc2);
987 return hICC;
989 Error:
990 if (hICC != NULL) cmsCloseProfile(hICC);
991 return NULL;
995 // This structure holds information about which MPU can be stored on a profile based on the version
997 typedef struct {
998 cmsBool IsV4; // Is a V4 tag?
999 cmsTagSignature RequiredTag; // Set to 0 for both types
1000 cmsTagTypeSignature LutType; // The LUT type
1001 int nTypes; // Number of types (up to 5)
1002 cmsStageSignature MpeTypes[5]; // 5 is the maximum number
1004 } cmsAllowedLUT;
1006 #define cmsSig0 ((cmsTagSignature) 0)
1008 static const cmsAllowedLUT AllowedLUTTypes[] = {
1010 { FALSE, cmsSig0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1011 { FALSE, cmsSig0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1012 { FALSE, cmsSig0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },
1013 { TRUE, cmsSig0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },
1014 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
1015 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
1016 { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1017 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }},
1018 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
1019 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
1020 { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
1023 #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
1025 // Check a single entry
1026 static
1027 cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
1029 cmsStage* mpe;
1030 int n;
1032 for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
1034 if (n > Tab ->nTypes) return FALSE;
1035 if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
1038 return (n == Tab ->nTypes);
1042 static
1043 const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
1045 cmsUInt32Number n;
1047 for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
1049 const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
1051 if (IsV4 ^ Tab -> IsV4) continue;
1052 if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
1054 if (CheckOne(Tab, Lut)) return Tab;
1057 return NULL;
1061 // Does convert a transform into a device link profile
1062 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
1064 cmsHPROFILE hProfile = NULL;
1065 cmsUInt32Number FrmIn, FrmOut;
1066 cmsInt32Number ChansIn, ChansOut;
1067 int ColorSpaceBitsIn, ColorSpaceBitsOut;
1068 _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1069 cmsPipeline* LUT = NULL;
1070 cmsStage* mpe;
1071 cmsContext ContextID = cmsGetTransformContextID(hTransform);
1072 const cmsAllowedLUT* AllowedLUT;
1073 cmsTagSignature DestinationTag;
1074 cmsProfileClassSignature deviceClass;
1076 _cmsAssert(hTransform != NULL);
1078 // Get the first mpe to check for named color
1079 mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);
1081 // Check if is a named color transform
1082 if (mpe != NULL) {
1084 if (cmsStageType(mpe) == cmsSigNamedColorElemType) {
1085 return CreateNamedColorDevicelink(hTransform);
1089 // First thing to do is to get a copy of the transformation
1090 LUT = cmsPipelineDup(xform ->Lut);
1091 if (LUT == NULL) return NULL;
1093 // Time to fix the Lab2/Lab4 issue.
1094 if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
1096 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
1097 goto Error;
1100 // On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments
1101 if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
1103 dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;
1104 if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
1105 goto Error;
1109 hProfile = cmsCreateProfilePlaceholder(ContextID);
1110 if (!hProfile) goto Error; // can't allocate
1112 cmsSetProfileVersion(hProfile, Version);
1114 FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);
1116 // Optimize the LUT and precalculate a devicelink
1118 ChansIn = cmsChannelsOfColorSpace(xform -> EntryColorSpace);
1119 ChansOut = cmsChannelsOfColorSpace(xform -> ExitColorSpace);
1121 ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace);
1122 ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
1124 FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
1125 FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
1127 deviceClass = cmsGetDeviceClass(hProfile);
1129 if (deviceClass == cmsSigOutputClass)
1130 DestinationTag = cmsSigBToA0Tag;
1131 else
1132 DestinationTag = cmsSigAToB0Tag;
1134 // Check if the profile/version can store the result
1135 if (dwFlags & cmsFLAGS_FORCE_CLUT)
1136 AllowedLUT = NULL;
1137 else
1138 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1140 if (AllowedLUT == NULL) {
1142 // Try to optimize
1143 _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1144 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1148 // If no way, then force CLUT that for sure can be written
1149 if (AllowedLUT == NULL) {
1151 cmsStage* FirstStage;
1152 cmsStage* LastStage;
1154 dwFlags |= cmsFLAGS_FORCE_CLUT;
1155 _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
1157 // Put identity curves if needed
1158 FirstStage = cmsPipelineGetPtrToFirstStage(LUT);
1159 if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType)
1160 if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
1161 goto Error;
1163 LastStage = cmsPipelineGetPtrToLastStage(LUT);
1164 if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType)
1165 if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
1166 goto Error;
1168 AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
1171 // Somethings is wrong...
1172 if (AllowedLUT == NULL) {
1173 goto Error;
1177 if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
1178 cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);
1180 // Tag profile with information
1181 if (!SetTextTags(hProfile, L"devicelink")) goto Error;
1183 // Store result
1184 if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
1187 if (xform -> InputColorant != NULL) {
1188 if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
1191 if (xform -> OutputColorant != NULL) {
1192 if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
1195 if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
1196 if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
1199 // Set the white point
1200 if (deviceClass == cmsSigInputClass) {
1201 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;
1203 else {
1204 if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;
1208 // Per 7.2.15 in spec 4.3
1209 cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);
1211 cmsPipelineFree(LUT);
1212 return hProfile;
1214 Error:
1215 if (LUT != NULL) cmsPipelineFree(LUT);
1216 cmsCloseProfile(hProfile);
1217 return NULL;