1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2011 Marti Maria Saguer
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 // -----------------------------------------------------------------------------------
33 cmsBool
SetTextTags(cmsHPROFILE hProfile
, const wchar_t* Description
)
35 cmsMLU
*DescriptionMLU
, *CopyrightMLU
;
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
;
55 cmsMLUfree(DescriptionMLU
);
57 cmsMLUfree(CopyrightMLU
);
63 cmsBool
SetSeqDescTag(cmsHPROFILE hProfile
, const char* Model
)
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;
78 Seq
->seq
[0].attributes
= 0;
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
;
92 cmsFreeProfileSequenceDescription(Seq
);
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])
108 cmsCIEXYZTRIPLE Colorants
;
111 cmsCIEXYZ WhitePointXYZ
;
113 hICC
= cmsCreateProfilePlaceholder(ContextID
);
114 if (!hICC
) // can't allocate
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
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
;
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
;
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
;
191 if (!cmsWriteTag(hICC
, cmsSigGreenTRCTag
, (void*) TransferFunction
[1])) goto Error
;
194 if (TransferFunction
[2] == TransferFunction
[0]) {
196 if (!cmsLinkTag (hICC
, cmsSigBlueTRCTag
, cmsSigRedTRCTag
)) goto Error
;
200 if (!cmsWriteTag(hICC
, cmsSigBlueTRCTag
, (void*) TransferFunction
[2])) goto Error
;
205 if (!cmsWriteTag(hICC
, cmsSigChromaticityTag
, (void*) Primaries
)) goto Error
;
213 cmsCloseProfile(hICC
);
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
)
234 hICC
= cmsCreateProfilePlaceholder(ContextID
);
235 if (!hICC
) // can't allocate
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
256 if (!SetTextTags(hICC
, L
"gray built-in")) goto Error
;
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
;
274 cmsCloseProfile(hICC
);
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
[])
293 cmsPipeline
* Pipeline
;
296 hICC
= cmsCreateProfilePlaceholder(ContextID
);
300 cmsSetProfileVersion(hICC
, 4.3);
302 cmsSetDeviceClass(hICC
, cmsSigLinkClass
);
303 cmsSetColorSpace(hICC
, ColorSpace
);
304 cmsSetPCS(hICC
, ColorSpace
);
306 cmsSetHeaderRenderingIntent(hICC
, INTENT_PERCEPTUAL
);
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
)))
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
);
332 cmsPipelineFree(Pipeline
);
334 cmsCloseProfile(hICC
);
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
350 // Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
361 // K: Does not change
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
);
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)
391 // This is a devicelink operating in CMYK for ink-limiting
393 cmsHPROFILE CMSEXPORT
cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID
,
394 cmsColorSpaceSignature ColorSpace
,
395 cmsFloat64Number Limit
)
402 if (ColorSpace
!= cmsSigCmykData
) {
403 cmsSignalError(ContextID
, cmsERROR_COLORSPACE_CHECK
, "InkLimiting: Only CMYK currently supported");
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
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
)))
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
);
459 cmsPipelineFree(LUT
);
462 cmsCloseProfile(hICC
);
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)))
497 if (!cmsWriteTag(hProfile
, cmsSigAToB0Tag
, LUT
)) goto Error
;
498 cmsPipelineFree(LUT
);
505 cmsPipelineFree(LUT
);
507 if (hProfile
!= NULL
)
508 cmsCloseProfile(hProfile
);
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)))
544 if (!cmsWriteTag(hProfile
, cmsSigAToB0Tag
, LUT
)) goto Error
;
545 cmsPipelineFree(LUT
);
552 cmsPipelineFree(LUT
);
554 if (hProfile
!= NULL
)
555 cmsCloseProfile(hProfile
);
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)))
590 if (!cmsWriteTag(hProfile
, cmsSigAToB0Tag
, LUT
)) goto Error
;
591 cmsPipelineFree(LUT
);
598 cmsPipelineFree(LUT
);
600 if (hProfile
!= NULL
)
601 cmsCloseProfile(hProfile
);
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
629 cmsToneCurve
* Build_sRGBGamma(cmsContext ContextID
)
631 cmsFloat64Number Parameters
[5];
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
)
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];
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
);
670 cmsHPROFILE CMSEXPORT
cmsCreate_sRGBProfile(void)
672 return cmsCreate_sRGBProfileTHR(NULL
);
678 cmsFloat64Number Brightness
;
679 cmsFloat64Number Contrast
;
680 cmsFloat64Number Hue
;
681 cmsFloat64Number Saturation
;
682 cmsCIEXYZ WPsrc
, WPdest
;
684 } BCHSWADJUSTS
, *LPBCHSWADJUSTS
;
688 int bchswSampler(register const cmsUInt16Number In
[], register cmsUInt16Number Out
[], register void* Cargo
)
690 cmsCIELab LabIn
, LabOut
;
691 cmsCIELCh LChIn
, LChOut
;
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
);
717 cmsFloat2LabEncoded(Out
, &LabOut
);
723 // Creates an abstract profile operating in Lab space for Brightness,
724 // contrast, Saturation and white point displacement
726 cmsHPROFILE CMSEXPORT
cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID
,
728 cmsFloat64Number Bright
,
729 cmsFloat64Number Contrast
,
730 cmsFloat64Number Hue
,
731 cmsFloat64Number Saturation
,
736 cmsPipeline
* Pipeline
;
740 cmsUInt32Number Dimensions
[MAX_INPUT_DIMENSIONS
];
743 bchsw
.Brightness
= Bright
;
744 bchsw
.Contrast
= Contrast
;
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
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
);
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
783 if (!cmsPipelineInsertStage(Pipeline
, cmsAT_END
, CLUT
)) {
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
);
801 cmsPipelineFree(Pipeline
);
802 cmsCloseProfile(hICC
);
807 CMSAPI cmsHPROFILE CMSEXPORT
cmsCreateBCHSWabstractProfile(int nLUTPoints
,
808 cmsFloat64Number Bright
,
809 cmsFloat64Number Contrast
,
810 cmsFloat64Number Hue
,
811 cmsFloat64Number Saturation
,
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
;
826 cmsToneCurve
* EmptyTab
;
827 cmsUInt16Number Zero
[2] = { 0, 0 };
829 hProfile
= cmsCreateProfilePlaceholder(ContextID
);
830 if (!hProfile
) // can't allocate
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
))
854 if (!cmsWriteTag(hProfile
, cmsSigBToA0Tag
, (void*) LUT
)) goto Error
;
855 if (!cmsWriteTag(hProfile
, cmsSigMediaWhitePointTag
, cmsD50_XYZ())) goto Error
;
857 cmsPipelineFree(LUT
);
863 cmsPipelineFree(LUT
);
865 if (hProfile
!= NULL
)
866 cmsCloseProfile(hProfile
);
871 cmsHPROFILE CMSEXPORT
cmsCreateNULLProfile(void)
873 return cmsCreateNULLProfileTHR(NULL
);
878 int IsPCS(cmsColorSpaceSignature ColorSpace
)
880 return (ColorSpace
== cmsSigXYZData
||
881 ColorSpace
== cmsSigLabData
);
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
);
901 if (IsPCS(ColorSpace
) && !IsPCS(PCS
)) {
903 cmsSetDeviceClass(hProfile
, cmsSigOutputClass
);
904 cmsSetPCS(hProfile
, ColorSpace
);
905 cmsSetColorSpace(hProfile
, PCS
);
909 if (IsPCS(PCS
) && !IsPCS(ColorSpace
)) {
911 cmsSetDeviceClass(hProfile
, cmsSigInputClass
);
912 cmsSetColorSpace(hProfile
, ColorSpace
);
913 cmsSetPCS(hProfile
, PCS
);
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.
930 cmsHPROFILE
CreateNamedColorDevicelink(cmsHTRANSFORM xform
)
932 _cmsTRANSFORM
* v
= (_cmsTRANSFORM
*) xform
;
933 cmsHPROFILE hICC
= NULL
;
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
);
975 if (hICC
!= NULL
) cmsCloseProfile(hICC
);
980 // This structure holds information about which MPU can be stored on a profile based on the version
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
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
1010 cmsBool
CheckOne(const cmsAllowedLUT
* Tab
, const cmsPipeline
* Lut
)
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
);
1026 const cmsAllowedLUT
* FindCombination(const cmsPipeline
* Lut
, cmsBool IsV4
, cmsTagSignature DestinationTag
)
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
;
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
;
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
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
)))
1082 // On the output side too
1083 if ((xform
->ExitColorSpace
) == cmsSigLabData
&& (Version
< 4.0)) {
1085 if (!cmsPipelineInsertStage(LUT
, cmsAT_END
, _cmsStageAllocLabV4ToV2(ContextID
)))
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
;
1113 DestinationTag
= cmsSigAToB0Tag
;
1115 // Check if the profile/version can store the result
1116 if (dwFlags
& cmsFLAGS_FORCE_CLUT
)
1119 AllowedLUT
= FindCombination(LUT
, Version
>= 4.0, DestinationTag
);
1121 if (AllowedLUT
== NULL
) {
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
)))
1140 if (cmsPipelineGetPtrToLastStage(LUT
) ->Type
!= cmsSigCurveSetElemType
)
1141 if (!cmsPipelineInsertStage(LUT
, cmsAT_END
, _cmsStageAllocIdentityCurves(ContextID
, ChansOut
)))
1144 AllowedLUT
= FindCombination(LUT
, Version
>= 4.0, DestinationTag
);
1147 // Somethings is wrong...
1148 if (AllowedLUT
== NULL
) {
1153 if (dwFlags
& cmsFLAGS_8BITS_DEVICELINK
)
1154 cmsPipelineSetSaveAs8bitsFlag(LUT
, TRUE
);
1156 // Tag profile with information
1157 if (!SetTextTags(hProfile
, L
"devicelink")) goto Error
;
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
;
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
);
1191 if (LUT
!= NULL
) cmsPipelineFree(LUT
);
1192 cmsCloseProfile(hProfile
);