3 // Copyright (C) 1998-2007 Marti Maria
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the Software
10 // is furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
17 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 // Transformations stuff
28 // -----------------------------------------------------------------------
33 cmsHTRANSFORM LCMSEXPORT
cmsCreateTransform(cmsHPROFILE Input
,
40 cmsHTRANSFORM LCMSEXPORT
cmsCreateProofingTransform(cmsHPROFILE Input
,
50 void LCMSEXPORT
cmsDeleteTransform(cmsHTRANSFORM hTransform
);
52 void LCMSEXPORT
cmsDoTransform(cmsHTRANSFORM Transform
,
54 LPVOID OutputBuffer
, unsigned int Size
);
56 void LCMSEXPORT
cmsGetAlarmCodes(int *r
, int *g
, int *b
);
57 void LCMSEXPORT
cmsSetAlarmCodes(int r
, int g
, int b
);
58 LCMSBOOL LCMSEXPORT
cmsIsIntentSupported(cmsHPROFILE hProfile
,
59 int Intent
, int UsedDirection
);
61 // -------------------------------------------------------------------------
66 static WORD AlarmR
= 0x8fff, AlarmG
= 0x8fff, AlarmB
= 0x8fff;
68 // Tag tables, soted by intents
70 static icTagSignature Device2PCS
[] = {icSigAToB0Tag
, // Perceptual
71 icSigAToB1Tag
, // Relative colorimetric
72 icSigAToB2Tag
, // Saturation
73 icSigAToB1Tag
}; // Absolute colorimetric
74 // (Relative/WhitePoint)
76 static icTagSignature PCS2Device
[] = {icSigBToA0Tag
, // Perceptual
77 icSigBToA1Tag
, // Relative colorimetric
78 icSigBToA2Tag
, // Saturation
79 icSigBToA1Tag
}; // Absolute colorimetric
80 // (Relative/WhitePoint)
83 static icTagSignature Preview
[] = {icSigPreview0Tag
,
90 static volatile double GlobalAdaptationState
= 0;
92 // --------------------------------Stages--------------------------------------
94 // Following routines does implement several kind of steps inside
95 // transform. On building the transform, code chooses adequate.
98 // From Shaper-Matrix to PCS
101 void ShaperMatrixToPCS(struct _cmstransform_struct
*p
,
102 WORD In
[3], WORD Out
[3])
104 cmsEvalMatShaper(p
-> InMatShaper
, In
, Out
);
110 void LUTtoPCS(struct _cmstransform_struct
*p
,
111 WORD In
[], WORD Out
[3])
113 cmsEvalLUT(p
-> Device2PCS
, In
, Out
);
116 // From indexed named color to PCS
119 void NC2toPCS(struct _cmstransform_struct
*p
,
120 WORD In
[], WORD Out
[3])
124 if (index
>= p
->NamedColorList
-> nColors
)
125 cmsSignalError(LCMS_ERRC_WARNING
, "Color %d out of range", index
);
127 CopyMemory(Out
, p
->NamedColorList
->List
[index
].PCS
, 3 * sizeof(WORD
));
130 // From PCS to Shaper-Matrix
133 void PCStoShaperMatrix(struct _cmstransform_struct
*p
,
134 WORD In
[3], WORD Out
[3])
136 cmsEvalMatShaper(p
-> OutMatShaper
, In
, Out
);
142 void PCStoLUT(struct _cmstransform_struct
*p
,
143 WORD In
[3], WORD Out
[])
145 cmsEvalLUT(p
-> PCS2Device
, In
, Out
);
151 // ----------------------- TRANSFORMATIONS --------------------------
154 // Inlining some assignations
156 #define COPY_3CHANS(to, from) { to[0]=from[0]; to[1]=from[1]; to[2]=from[2]; }
159 // Null transformation, only hold channels
162 void NullXFORM(_LPcmsTRANSFORM p
,
164 LPVOID out
, unsigned int Size
)
166 register LPBYTE accum
;
167 register LPBYTE output
;
168 WORD wIn
[MAXCHANNELS
];
169 register unsigned int i
, n
;
173 output
= (LPBYTE
) out
;
174 n
= Size
; // Buffer len
176 for (i
=0; i
< n
; i
++)
178 accum
= p
-> FromInput(p
, wIn
, accum
);
179 output
= p
-> ToOutput(p
, wIn
, output
);
185 // This is the "normal" proofing transform
188 void NormalXFORM(_LPcmsTRANSFORM p
,
190 LPVOID out
, unsigned int Size
)
192 register LPBYTE accum
;
193 register LPBYTE output
;
194 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
195 WORD wStageABC
[3], wPCS
[3], wStageLMN
[MAXCHANNELS
];
197 register unsigned int i
, n
;
202 output
= (LPBYTE
) out
;
203 n
= Size
; // Buffer len
205 for (i
=0; i
< n
; i
++)
208 accum
= p
-> FromInput(p
, wIn
, accum
);
210 p
-> FromDevice(p
, wIn
, wStageABC
);
214 p
-> Stage1(wStageABC
, wPCS
, &p
->m1
, &p
->of1
);
216 if (wPCS
[0] == 0xFFFF &&
222 output
= p
-> ToOutput((_LPcmsTRANSFORM
) p
,
223 _cmsWhiteBySpace(cmsGetColorSpace(p
-> OutputProfile
)),
229 COPY_3CHANS(wPCS
, wStageABC
);
234 // Gamut check, enabled across CLUT
236 cmsEvalLUT(p
-> Gamut
, wPCS
, wGamut
);
238 if (wGamut
[0] >= 1) {
240 wOut
[0] = AlarmR
; // Gamut alarm
245 output
= p
-> ToOutput((_LPcmsTRANSFORM
)p
, wOut
, output
);
252 WORD wPreview
[3]; // PCS
254 cmsEvalLUT(p
-> Preview
, wPCS
, wPreview
);
255 COPY_3CHANS(wPCS
, wPreview
);
260 p
-> Stage2(wPCS
, wStageLMN
, &p
->m2
, &p
->of2
);
262 if (wPCS
[0] == 0xFFFF &&
268 output
= p
-> ToOutput((_LPcmsTRANSFORM
)p
,
269 _cmsWhiteBySpace(cmsGetColorSpace(p
-> OutputProfile
)),
277 COPY_3CHANS(wStageLMN
, wPCS
);
279 // Here wOut may come as MAXCHANNELS channels
281 p
-> ToDevice(p
, wStageLMN
, wOut
);
283 output
= p
-> ToOutput((_LPcmsTRANSFORM
)p
, wOut
, output
);
287 // Using precalculated LUT
290 void PrecalculatedXFORM(_LPcmsTRANSFORM p
,
292 LPVOID out
, unsigned int Size
)
294 register LPBYTE accum
;
295 register LPBYTE output
;
296 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
301 output
= (LPBYTE
) out
;
302 n
= Size
; // Buffer len
305 for (i
=0; i
< n
; i
++) {
307 accum
= p
-> FromInput(p
, wIn
, accum
);
309 // Try to speedup things on plain devicelinks
311 if (p
->DeviceLink
->wFlags
== LUT_HAS3DGRID
) {
313 p
->DeviceLink
->CLut16params
.Interp3D(wIn
, wOut
,
315 &p
->DeviceLink
-> CLut16params
);
318 cmsEvalLUT(p
-> DeviceLink
, wIn
, wOut
);
321 output
= p
-> ToOutput(p
, wOut
, output
);
325 // Auxiliar: Handle precalculated gamut check
328 void TransformOnePixelWithGamutCheck(_LPcmsTRANSFORM p
, WORD wIn
[], WORD wOut
[])
332 cmsEvalLUT(p
->GamutCheck
, wIn
, &wOutOfGamut
);
334 if (wOutOfGamut
>= 1) {
336 ZeroMemory(wOut
, sizeof(WORD
) * MAXCHANNELS
);
344 cmsEvalLUT(p
-> DeviceLink
, wIn
, wOut
);
350 void PrecalculatedXFORMGamutCheck(_LPcmsTRANSFORM p
,
352 LPVOID out
, unsigned int Size
)
354 register LPBYTE accum
;
355 register LPBYTE output
;
356 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
357 register unsigned int i
, n
;
361 output
= (LPBYTE
) out
;
362 n
= Size
; // Buffer len
364 for (i
=0; i
< n
; i
++) {
366 accum
= p
-> FromInput(p
, wIn
, accum
);
368 TransformOnePixelWithGamutCheck(p
, wIn
, wOut
);
370 output
= p
-> ToOutput(p
, wOut
, output
);
376 // Using precalculated LUT + Cache
379 void CachedXFORM(_LPcmsTRANSFORM p
,
381 LPVOID out
, unsigned int Size
)
383 register LPBYTE accum
;
384 register LPBYTE output
;
385 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
386 register unsigned int i
, n
;
387 WORD CacheIn
[MAXCHANNELS
], CacheOut
[MAXCHANNELS
];
391 output
= (LPBYTE
) out
;
392 n
= Size
; // Buffer len
394 // Empty buffers for quick memcmp
396 ZeroMemory(wIn
, sizeof(WORD
) * MAXCHANNELS
);
397 ZeroMemory(wOut
, sizeof(WORD
) * MAXCHANNELS
);
400 LCMS_READ_LOCK(&p
->rwlock
);
401 CopyMemory(CacheIn
, p
->CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
402 CopyMemory(CacheOut
, p
->CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
403 LCMS_UNLOCK(&p
->rwlock
);
405 for (i
=0; i
< n
; i
++) {
407 accum
= p
-> FromInput(p
, wIn
, accum
);
410 if (memcmp(wIn
, CacheIn
, sizeof(WORD
) * MAXCHANNELS
) == 0) {
412 CopyMemory(wOut
, CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
416 // Try to speedup things on plain devicelinks
418 if (p
->DeviceLink
->wFlags
== LUT_HAS3DGRID
) {
420 p
->DeviceLink
->CLut16params
.Interp3D(wIn
, wOut
,
422 &p
->DeviceLink
-> CLut16params
);
425 cmsEvalLUT(p
-> DeviceLink
, wIn
, wOut
);
428 CopyMemory(CacheIn
, wIn
, sizeof(WORD
) * MAXCHANNELS
);
429 CopyMemory(CacheOut
, wOut
, sizeof(WORD
) * MAXCHANNELS
);
432 output
= p
-> ToOutput(p
, wOut
, output
);
436 LCMS_WRITE_LOCK(&p
->rwlock
);
437 CopyMemory(p
->CacheIn
, CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
438 CopyMemory(p
->CacheOut
, CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
439 LCMS_UNLOCK(&p
->rwlock
);
445 // Using precalculated LUT + Cache
448 void CachedXFORMGamutCheck(_LPcmsTRANSFORM p
,
450 LPVOID out
, unsigned int Size
)
452 register LPBYTE accum
;
453 register LPBYTE output
;
454 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
455 register unsigned int i
, n
;
456 WORD CacheIn
[MAXCHANNELS
], CacheOut
[MAXCHANNELS
];
460 output
= (LPBYTE
) out
;
461 n
= Size
; // Buffer len
463 // Empty buffers for quick memcmp
465 ZeroMemory(wIn
, sizeof(WORD
) * MAXCHANNELS
);
466 ZeroMemory(wOut
, sizeof(WORD
) * MAXCHANNELS
);
468 LCMS_READ_LOCK(&p
->rwlock
);
469 CopyMemory(CacheIn
, p
->CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
470 CopyMemory(CacheOut
, p
->CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
471 LCMS_UNLOCK(&p
->rwlock
);
474 for (i
=0; i
< n
; i
++) {
476 accum
= p
-> FromInput(p
, wIn
, accum
);
478 if (memcmp(wIn
, CacheIn
, sizeof(WORD
) * MAXCHANNELS
) == 0) {
480 CopyMemory(wOut
, CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
484 TransformOnePixelWithGamutCheck(p
, wIn
, wOut
);
486 CopyMemory(CacheIn
, wIn
, sizeof(WORD
) * MAXCHANNELS
);
487 CopyMemory(CacheOut
, wOut
, sizeof(WORD
) * MAXCHANNELS
);
490 output
= p
-> ToOutput(p
, wOut
, output
);
493 LCMS_WRITE_LOCK(&p
->rwlock
);
494 CopyMemory(p
->CacheIn
, CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
495 CopyMemory(p
->CacheOut
, CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
496 LCMS_UNLOCK(&p
->rwlock
);
500 // Using smelted Matrix/Shaper
503 void MatrixShaperXFORM(_LPcmsTRANSFORM p
,
505 LPVOID out
, unsigned int Size
)
507 register LPBYTE accum
;
508 register LPBYTE output
;
509 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
510 register unsigned int i
, n
;
514 output
= (LPBYTE
) out
;
515 n
= Size
; // Buffer len
517 for (i
=0; i
< n
; i
++)
519 accum
= p
-> FromInput(p
, wIn
, accum
);
520 cmsEvalMatShaper(p
-> SmeltMatShaper
, wIn
, wOut
);
521 output
= p
-> ToOutput(p
, wOut
, output
);
526 // Using Named color input table
529 void NC2deviceXform(_LPcmsTRANSFORM p
,
531 LPVOID out
, unsigned int Size
)
534 register LPBYTE accum
;
535 register LPBYTE output
;
536 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
537 register unsigned int i
;
541 output
= (LPBYTE
) out
;
543 for (i
=0; i
< Size
; i
++) {
545 accum
= p
-> FromInput(p
, wIn
, accum
);
546 CopyMemory(wOut
, p
->NamedColorList
->List
[wIn
[0]].DeviceColorant
, sizeof(WORD
) * MAXCHANNELS
);
547 output
= p
-> ToOutput(p
, wOut
, output
);
554 // --------------------------------------------------------------------------
555 // Build a LUT based on shape-matrix method.
558 // Some non-conformant gray profiles are using kTCR as L*,
559 // this function converts the curve to XYZ PCS.
562 void FromLstarToXYZ(LPGAMMATABLE g
, LPGAMMATABLE gxyz
[3])
570 // Setup interpolation across origin
571 cmsCalcL16Params(g
->nEntries
, &L16
);
574 gxyz
[0] = cmsAllocGamma(nPoints
);
575 gxyz
[1] = cmsAllocGamma(nPoints
);
576 gxyz
[2] = cmsAllocGamma(nPoints
);
578 // Transport from Lab to XYZ
580 for (i
=0; i
< nPoints
; i
++) {
582 WORD val
= _cmsQuantizeVal(i
, nPoints
);
583 WORD w
= cmsLinearInterpLUT16(val
, g
->GammaTable
, &L16
);
585 Lab
.L
= ((double) 100.0 * w
) / 65535.0;
588 cmsLab2XYZ(NULL
, &XYZ
, &Lab
);
590 // Should be same curve
591 gxyz
[0] ->GammaTable
[i
] = (WORD
) floor((65535.0 * XYZ
.X
) / D50X
+ 0.5);
592 gxyz
[1] ->GammaTable
[i
] = (WORD
) floor((65535.0 * XYZ
.Y
) / D50Y
+ 0.5);
593 gxyz
[2] ->GammaTable
[i
] = (WORD
) floor((65535.0 * XYZ
.Z
) / D50Z
+ 0.5);
597 // Monochrome version
600 LPMATSHAPER
cmsBuildGrayInputMatrixShaper(cmsHPROFILE hProfile
)
602 cmsCIEXYZ Illuminant
;
603 LPGAMMATABLE GrayTRC
, Shapes
[3];
604 LPMATSHAPER MatShaper
;
607 GrayTRC
= cmsReadICCGamma(hProfile
, icSigGrayTRCTag
); // Y
608 if (GrayTRC
== NULL
) return NULL
;
610 cmsTakeIluminant(&Illuminant
, hProfile
);
612 if (cmsGetPCS(hProfile
) == icSigLabData
) {
614 // Fixup for Lab monochrome
615 FromLstarToXYZ(GrayTRC
, Shapes
);
618 Shapes
[0] = cmsDupGamma(GrayTRC
);
619 Shapes
[1] = cmsDupGamma(GrayTRC
);
620 Shapes
[2] = cmsDupGamma(GrayTRC
);
623 if (!Shapes
[0] || !Shapes
[1] || !Shapes
[2])
626 cmsFreeGamma(GrayTRC
);
628 // R=G=B as precondition
630 VEC3init(&Scale
.v
[0], Illuminant
.X
/3, Illuminant
.X
/3, Illuminant
.X
/3);
631 VEC3init(&Scale
.v
[1], Illuminant
.Y
/3, Illuminant
.Y
/3, Illuminant
.Y
/3);
632 VEC3init(&Scale
.v
[2], Illuminant
.Z
/3, Illuminant
.Z
/3, Illuminant
.Z
/3);
635 MatShaper
= cmsAllocMatShaper(&Scale
, Shapes
, MATSHAPER_INPUT
);
636 cmsFreeGammaTriple(Shapes
);
642 // Monochrome as output
645 LPMATSHAPER
cmsBuildGrayOutputMatrixShaper(cmsHPROFILE hProfile
)
647 cmsCIEXYZ Illuminant
;
648 LPGAMMATABLE GrayTRC
, Shapes
[3];
649 LPMATSHAPER MatShaper
;
652 cmsTakeIluminant(&Illuminant
, hProfile
);
654 // That is a special case for non-compliant profiles.
656 if (cmsGetPCS(hProfile
) == icSigLabData
) {
658 LPGAMMATABLE Shapes1
[3];
660 GrayTRC
= cmsReadICCGamma(hProfile
, icSigGrayTRCTag
);
661 FromLstarToXYZ(GrayTRC
, Shapes1
);
663 // Reversing must be done after curve translation
665 Shapes
[0] = cmsReverseGamma(Shapes1
[0]->nEntries
, Shapes1
[0]);
666 Shapes
[1] = cmsReverseGamma(Shapes1
[1]->nEntries
, Shapes1
[1]);
667 Shapes
[2] = cmsReverseGamma(Shapes1
[2]->nEntries
, Shapes1
[2]);
669 cmsFreeGammaTriple(Shapes1
);
676 GrayTRC
= cmsReadICCGammaReversed(hProfile
, icSigGrayTRCTag
); // Y
678 Shapes
[0] = cmsDupGamma(GrayTRC
);
679 Shapes
[1] = cmsDupGamma(GrayTRC
);
680 Shapes
[2] = cmsDupGamma(GrayTRC
);
683 if (!Shapes
[0] || !Shapes
[1] || !Shapes
[2])
686 cmsFreeGamma(GrayTRC
);
688 VEC3init(&Scale
.v
[0], 0, 1.0/Illuminant
.Y
, 0);
689 VEC3init(&Scale
.v
[1], 0, 1.0/Illuminant
.Y
, 0);
690 VEC3init(&Scale
.v
[2], 0, 1.0/Illuminant
.Y
, 0);
693 MatShaper
= cmsAllocMatShaper(&Scale
, Shapes
, MATSHAPER_OUTPUT
);
694 cmsFreeGammaTriple(Shapes
);
701 // Input matrix, only in XYZ
703 LPMATSHAPER
cmsBuildInputMatrixShaper(cmsHPROFILE InputProfile
)
706 LPGAMMATABLE Shapes
[3];
709 // Check if this is a grayscale profile. If so, build
710 // appropiate conversion tables. The tables are the PCS
711 // iluminant, scaled across GrayTRC
713 if (cmsGetColorSpace(InputProfile
) == icSigGrayData
)
715 return cmsBuildGrayInputMatrixShaper(InputProfile
);
718 if (!cmsReadICCMatrixRGB2XYZ(&DoubleMat
, InputProfile
))
721 Shapes
[0] = cmsReadICCGamma(InputProfile
, icSigRedTRCTag
);
722 Shapes
[1] = cmsReadICCGamma(InputProfile
, icSigGreenTRCTag
);
723 Shapes
[2] = cmsReadICCGamma(InputProfile
, icSigBlueTRCTag
);
725 if (!Shapes
[0] || !Shapes
[1] || !Shapes
[2])
728 InMatSh
= cmsAllocMatShaper(&DoubleMat
, Shapes
, MATSHAPER_INPUT
);
730 cmsFreeGammaTriple(Shapes
);
736 // Output style matrix-shaper
739 LPMATSHAPER
cmsBuildOutputMatrixShaper(cmsHPROFILE OutputProfile
)
741 MAT3 DoubleMat
, DoubleInv
;
742 LPGAMMATABLE InverseShapes
[3];
743 LPMATSHAPER OutMatSh
;
747 if (cmsGetColorSpace(OutputProfile
) == icSigGrayData
)
749 return cmsBuildGrayOutputMatrixShaper(OutputProfile
);
753 if (!cmsReadICCMatrixRGB2XYZ(&DoubleMat
, OutputProfile
))
756 if (MAT3inverse(&DoubleMat
, &DoubleInv
) < 0)
760 InverseShapes
[0] = cmsReadICCGammaReversed(OutputProfile
, icSigRedTRCTag
);
761 InverseShapes
[1] = cmsReadICCGammaReversed(OutputProfile
, icSigGreenTRCTag
);
762 InverseShapes
[2] = cmsReadICCGammaReversed(OutputProfile
, icSigBlueTRCTag
);
764 OutMatSh
= cmsAllocMatShaper(&DoubleInv
, InverseShapes
, MATSHAPER_OUTPUT
);
766 cmsFreeGammaTriple(InverseShapes
);
773 // This function builds a transform matrix chaining parameters
776 LCMSBOOL
cmsBuildSmeltMatShaper(_LPcmsTRANSFORM p
)
778 MAT3 From
, To
, ToInv
, Transfer
;
779 LPGAMMATABLE In
[3], InverseOut
[3];
782 if (!cmsReadICCMatrixRGB2XYZ(&From
, p
-> InputProfile
))
786 if (!cmsReadICCMatrixRGB2XYZ(&To
, p
-> OutputProfile
))
791 if (MAT3inverse(&To
, &ToInv
) < 0)
795 MAT3per(&Transfer
, &ToInv
, &From
);
800 In
[0] = cmsReadICCGamma(p
-> InputProfile
, icSigRedTRCTag
);
801 In
[1] = cmsReadICCGamma(p
-> InputProfile
, icSigGreenTRCTag
);
802 In
[2] = cmsReadICCGamma(p
-> InputProfile
, icSigBlueTRCTag
);
804 if (!In
[0] || !In
[1] || !In
[2])
808 InverseOut
[0] = cmsReadICCGammaReversed(p
-> OutputProfile
, icSigRedTRCTag
);
809 InverseOut
[1] = cmsReadICCGammaReversed(p
-> OutputProfile
, icSigGreenTRCTag
);
810 InverseOut
[2] = cmsReadICCGammaReversed(p
-> OutputProfile
, icSigBlueTRCTag
);
812 if (!InverseOut
[0] || !InverseOut
[1] || !InverseOut
[2]) {
813 cmsFreeGammaTriple(In
);
817 p
-> SmeltMatShaper
= cmsAllocMatShaper2(&Transfer
, In
, InverseOut
, MATSHAPER_ALLSMELTED
);
819 cmsFreeGammaTriple(In
);
820 cmsFreeGammaTriple(InverseOut
);
822 return (p
-> SmeltMatShaper
!= NULL
);
828 // Conversion between PCS ------------------------------------------
830 // Identifies intent archieved by LUT
833 int GetPhase(cmsHPROFILE hProfile
)
835 switch (cmsGetPCS(hProfile
)) {
837 case icSigXYZData
: return XYZRel
;
839 case icSigLabData
: return LabRel
;
841 default: cmsSignalError(LCMS_ERRC_ABORTED
, "Invalid PCS");
851 void TakeConversionRoutines(_LPcmsTRANSFORM p
, int DoBPC
)
853 cmsCIEXYZ BlackPointIn
, WhitePointIn
, IlluminantIn
;
854 cmsCIEXYZ BlackPointOut
, WhitePointOut
, IlluminantOut
;
855 cmsCIEXYZ BlackPointProof
, WhitePointProof
, IlluminantProof
;
856 MAT3 ChromaticAdaptationMatrixIn
, ChromaticAdaptationMatrixOut
;
857 MAT3 ChromaticAdaptationMatrixProof
;
860 cmsTakeIluminant(&IlluminantIn
, p
-> InputProfile
);
861 cmsTakeMediaWhitePoint(&WhitePointIn
, p
-> InputProfile
);
862 cmsTakeMediaBlackPoint(&BlackPointIn
, p
-> InputProfile
);
863 cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixIn
, p
-> InputProfile
);
865 cmsTakeIluminant(&IlluminantOut
, p
-> OutputProfile
);
866 cmsTakeMediaWhitePoint(&WhitePointOut
, p
-> OutputProfile
);
867 cmsTakeMediaBlackPoint(&BlackPointOut
, p
-> OutputProfile
);
868 cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixOut
, p
-> OutputProfile
);
871 if (p
-> Preview
== NULL
&& p
->Gamut
== NULL
) // Non-proofing
873 if (p
->Intent
== INTENT_PERCEPTUAL
||
874 p
->Intent
== INTENT_SATURATION
) {
877 // For v4 profiles, Perceptual PCS has a reference black point
878 // which v2 profiles should scale to.
880 if ((cmsGetProfileICCversion(p
->InputProfile
) >= 0x4000000) ||
881 (cmsGetProfileICCversion(p
->OutputProfile
) >= 0x4000000)) {
887 // Black point compensation does not apply to absolute intent
889 if (p
->Intent
== INTENT_ABSOLUTE_COLORIMETRIC
)
892 // Black point compensation does not apply to devicelink profiles
894 if (cmsGetDeviceClass(p
->InputProfile
) == icSigLinkClass
)
897 if (cmsGetDeviceClass(p
->OutputProfile
) == icSigLinkClass
)
902 // Detect Black points
904 cmsDetectBlackPoint(&BlackPointIn
, p
->InputProfile
, p
->Intent
, 0);
905 cmsDetectBlackPoint(&BlackPointOut
, p
->OutputProfile
, p
->Intent
, 0);
907 // If equal black points, then do nothing. This often applies to BP=0
909 if (BlackPointIn
.X
== BlackPointOut
.X
&&
910 BlackPointIn
.Y
== BlackPointOut
.Y
&&
911 BlackPointIn
.Z
== BlackPointOut
.Z
)
917 cmsChooseCnvrt(p
-> Intent
== INTENT_ABSOLUTE_COLORIMETRIC
,
923 &ChromaticAdaptationMatrixIn
,
929 &ChromaticAdaptationMatrixOut
,
941 cmsTakeIluminant(&IlluminantProof
, p
-> PreviewProfile
);
942 cmsTakeMediaWhitePoint(&WhitePointProof
, p
-> PreviewProfile
);
943 cmsTakeMediaBlackPoint(&BlackPointProof
, p
-> PreviewProfile
);
944 cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixProof
, p
-> PreviewProfile
);
948 cmsDetectBlackPoint(&BlackPointProof
, p
->PreviewProfile
, p
->Intent
, 0);
949 cmsDetectBlackPoint(&BlackPointIn
, p
->InputProfile
, p
->Intent
, 0);
950 cmsDetectBlackPoint(&BlackPointOut
, p
->OutputProfile
, p
->Intent
, 0);
954 if (BlackPointIn
.X
== BlackPointProof
.X
&&
955 BlackPointIn
.Y
== BlackPointProof
.Y
&&
956 BlackPointIn
.Z
== BlackPointProof
.Z
)
964 cmsChooseCnvrt(p
-> Intent
== INTENT_ABSOLUTE_COLORIMETRIC
,
970 &ChromaticAdaptationMatrixIn
,
976 &ChromaticAdaptationMatrixProof
,
982 cmsChooseCnvrt(p
-> ProofIntent
== INTENT_ABSOLUTE_COLORIMETRIC
,
988 &ChromaticAdaptationMatrixProof
,
994 &ChromaticAdaptationMatrixOut
,
1007 LCMSBOOL
IsProperColorSpace(cmsHPROFILE hProfile
, DWORD dwFormat
, LCMSBOOL lUsePCS
)
1009 int Space
= T_COLORSPACE(dwFormat
);
1011 if (Space
== PT_ANY
) return TRUE
;
1014 return (Space
== _cmsLCMScolorSpace(cmsGetPCS(hProfile
)));
1016 return (Space
== _cmsLCMScolorSpace(cmsGetColorSpace(hProfile
)));
1020 // Auxiliary: allocate transform struct and set to defaults
1023 _LPcmsTRANSFORM
AllocEmptyTransform(void)
1025 // Allocate needed memory
1027 _LPcmsTRANSFORM p
= (_LPcmsTRANSFORM
) _cmsMalloc(sizeof(_cmsTRANSFORM
));
1030 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsCreateTransform: _cmsMalloc() failed");
1034 ZeroMemory(p
, sizeof(_cmsTRANSFORM
));
1036 // Initialize default methods
1039 p
-> Intent
= INTENT_PERCEPTUAL
;
1040 p
-> ProofIntent
= INTENT_ABSOLUTE_COLORIMETRIC
;
1041 p
-> DoGamutCheck
= FALSE
;
1042 p
-> InputProfile
= NULL
;
1043 p
-> OutputProfile
= NULL
;
1044 p
-> PreviewProfile
= NULL
;
1045 p
-> Preview
= NULL
;
1047 p
-> DeviceLink
= NULL
;
1048 p
-> InMatShaper
= NULL
;
1049 p
-> OutMatShaper
= NULL
;
1050 p
-> SmeltMatShaper
= NULL
;
1051 p
-> NamedColorList
= NULL
;
1052 p
-> EntryColorSpace
= (icColorSpaceSignature
) 0;
1053 p
-> ExitColorSpace
= (icColorSpaceSignature
) 0;
1054 p
-> AdaptationState
= GlobalAdaptationState
;
1056 LCMS_CREATE_LOCK(&p
->rwlock
);
1062 // Identify whatever a transform is to be cached
1065 void SetPrecalculatedTransform(_LPcmsTRANSFORM p
)
1067 if ((p
->dwOriginalFlags
& cmsFLAGS_GAMUTCHECK
) && p
->GamutCheck
!= NULL
) {
1069 p
-> xform
= PrecalculatedXFORMGamutCheck
;
1071 if (!(p
->dwOriginalFlags
& cmsFLAGS_NOTCACHE
)) {
1073 ZeroMemory(p
->CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
1074 TransformOnePixelWithGamutCheck(p
, p
->CacheIn
, p
->CacheOut
);
1075 p
->xform
= CachedXFORMGamutCheck
;
1081 p
-> xform
= PrecalculatedXFORM
;
1083 if (!(p
->dwOriginalFlags
& cmsFLAGS_NOTCACHE
)) {
1085 ZeroMemory(p
->CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
1086 cmsEvalLUT(p
->DeviceLink
, p
->CacheIn
, p
->CacheOut
);
1087 p
->xform
= CachedXFORM
;
1093 // Transform is identified as device-link
1095 cmsHPROFILE
CreateDeviceLinkTransform(_LPcmsTRANSFORM p
)
1098 if (!IsProperColorSpace(p
->InputProfile
, p
->InputFormat
, FALSE
)) {
1099 cmsSignalError(LCMS_ERRC_ABORTED
, "Device link is operating on wrong colorspace on input");
1103 if (!IsProperColorSpace(p
->InputProfile
, p
->OutputFormat
, TRUE
)) {
1104 cmsSignalError(LCMS_ERRC_ABORTED
, "Device link is operating on wrong colorspace on output");
1108 // Device link does only have AToB0Tag (ICC-Spec 1998/09)
1110 p
->DeviceLink
= cmsReadICCLut(p
->InputProfile
, icSigAToB0Tag
);
1112 if (!p
->DeviceLink
) {
1114 cmsSignalError(LCMS_ERRC_ABORTED
, "Noncompliant device-link profile");
1115 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1119 if (p
->PreviewProfile
!= NULL
) {
1120 cmsSignalError(LCMS_ERRC_WARNING
, "Proofing not supported on device link transforms");
1123 if (p
->OutputProfile
!= NULL
) {
1124 cmsSignalError(LCMS_ERRC_WARNING
, "Output profile should be NULL, since this is a device-link transform");
1131 SetPrecalculatedTransform(p
);
1133 p
-> EntryColorSpace
= cmsGetColorSpace(p
-> InputProfile
);
1134 p
-> ExitColorSpace
= cmsGetPCS(p
-> InputProfile
);
1136 if (p
->EntryColorSpace
== icSigRgbData
||
1137 p
->EntryColorSpace
== icSigCmyData
) {
1139 p
->DeviceLink
-> CLut16params
.Interp3D
= cmsTetrahedralInterp16
;
1142 // Precalculated device-link profile is ready
1143 return (cmsHTRANSFORM
) p
;
1147 // Transform that includes proofing
1149 void CreateProof(_LPcmsTRANSFORM p
, icTagSignature
*ToTagPtr
)
1152 icTagSignature ProofTag
;
1154 if (p
-> dwOriginalFlags
& cmsFLAGS_SOFTPROOFING
) {
1156 // Apr-15, 2002 - Too much profiles does have bogus content
1157 // on preview tag, so I do compute it by my own.
1159 p
-> Preview
= _cmsComputeSoftProofLUT(p
->PreviewProfile
, p
->Intent
);
1160 p
-> Phase2
= LabRel
;
1162 // That's a proofing transfor, so use second intent for output.
1164 *ToTagPtr
= PCS2Device
[p
->ProofIntent
];
1166 if (p
-> Preview
== NULL
) {
1168 ProofTag
= Preview
[p
-> Intent
];
1170 if (!cmsIsTag(p
->PreviewProfile
, ProofTag
)) {
1172 ProofTag
= Preview
[0];
1173 if (!cmsIsTag(p
->PreviewProfile
, ProofTag
))
1174 ProofTag
= (icTagSignature
)0;
1179 p
-> Preview
= cmsReadICCLut(p
->PreviewProfile
, ProofTag
);
1180 p
-> Phase2
= GetPhase(p
->PreviewProfile
);
1185 p
-> Preview
= NULL
;
1186 p
->PreviewProfile
= NULL
;
1187 cmsSignalError(LCMS_ERRC_WARNING
, "Sorry, the proof profile has not previewing capabilities");
1194 // Aug-31, 2001 - Too much profiles does have bogus content
1195 // on gamut tag, so I do compute it by my own.
1197 if ((p
-> dwOriginalFlags
& cmsFLAGS_GAMUTCHECK
) && (p
-> dwOriginalFlags
& cmsFLAGS_NOTPRECALC
)) {
1200 p
-> Gamut
= _cmsComputeGamutLUT(p
->PreviewProfile
, p
->Intent
);
1201 p
-> Phase2
= LabRel
;
1203 if (p
-> Gamut
== NULL
) {
1205 // Profile goes only in one direction... try to see
1206 // if profile has the tag, and use it, no matter it
1207 // could be bogus. This is the last chance!
1209 if (cmsIsTag(p
->PreviewProfile
, icSigGamutTag
)) {
1211 p
-> Gamut
= cmsReadICCLut(p
->PreviewProfile
, icSigGamutTag
);
1216 // Nope, cannot be done.
1218 cmsSignalError(LCMS_ERRC_WARNING
, "Sorry, the proof profile has not gamut checking capabilities");
1227 // Choose the adequate transform routine
1230 _LPcmsTRANSFORM
PickTransformRoutine(_LPcmsTRANSFORM p
,
1231 icTagSignature
*FromTagPtr
,
1232 icTagSignature
*ToTagPtr
)
1238 // Is a named color profile?
1239 if (cmsGetDeviceClass(p
->InputProfile
) == icSigNamedColorClass
) {
1241 // Yes, and used as input
1242 p
->FromDevice
= NC2toPCS
;
1245 // Can we optimize matrix-shaper only transform?
1247 if ((*FromTagPtr
== 0) &&
1249 (!p
->PreviewProfile
) &&
1250 (p
-> Intent
!= INTENT_ABSOLUTE_COLORIMETRIC
) &&
1251 (p
-> EntryColorSpace
== icSigRgbData
) &&
1252 (p
-> ExitColorSpace
== icSigRgbData
) &&
1253 !(p
-> dwOriginalFlags
& cmsFLAGS_BLACKPOINTCOMPENSATION
)) {
1255 // Yes... try to smelt matrix-shapers
1256 p
-> xform
= MatrixShaperXFORM
;
1257 p
-> dwOriginalFlags
|= cmsFLAGS_NOTPRECALC
;
1259 if (!cmsBuildSmeltMatShaper(p
))
1261 cmsSignalError(LCMS_ERRC_ABORTED
, "unable to smelt shaper-matrix, required tags missing");
1265 p
-> Phase1
= p
-> Phase3
= XYZRel
;
1270 // No, is a transform involving LUT
1272 if (*FromTagPtr
!= 0) {
1274 p
-> FromDevice
= LUTtoPCS
;
1275 p
-> Device2PCS
= cmsReadICCLut(p
-> InputProfile
, *FromTagPtr
);
1276 if (!p
-> Device2PCS
) {
1278 cmsSignalError(LCMS_ERRC_ABORTED
, "profile is unsuitable for input");
1285 p
-> FromDevice
= ShaperMatrixToPCS
;
1286 p
-> InMatShaper
= cmsBuildInputMatrixShaper(p
-> InputProfile
);
1288 if (!p
->InMatShaper
) {
1289 cmsSignalError(LCMS_ERRC_ABORTED
, "profile is unsuitable for input");
1293 p
-> Phase1
= XYZRel
;
1298 if (*ToTagPtr
!= 0) {
1300 p
-> ToDevice
= PCStoLUT
;
1301 p
-> PCS2Device
= cmsReadICCLut(p
-> OutputProfile
, *ToTagPtr
);
1302 if (!p
-> PCS2Device
) {
1303 cmsSignalError(LCMS_ERRC_ABORTED
, "profile is unsuitable for output");
1310 p
-> ToDevice
= PCStoShaperMatrix
;
1311 p
-> OutMatShaper
= cmsBuildOutputMatrixShaper(p
->OutputProfile
);
1313 if (!p
-> OutMatShaper
) {
1314 cmsSignalError(LCMS_ERRC_ABORTED
, "profile is unsuitable for output");
1317 p
-> Phase3
= XYZRel
;
1328 // Create a transform.
1330 cmsHTRANSFORM LCMSEXPORT
cmsCreateProofingTransform(cmsHPROFILE InputProfile
,
1332 cmsHPROFILE OutputProfile
,
1334 cmsHPROFILE ProofingProfile
,
1341 icTagSignature FromTag
;
1342 icTagSignature ToTag
;
1344 if (nIntent
< 0 || nIntent
> 3 ||
1345 ProofingIntent
< 0 || ProofingIntent
> 3) {
1347 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsCreateTransform: intent mismatch");
1351 p
= AllocEmptyTransform();
1352 if (p
== NULL
) return NULL
;
1354 p
-> xform
= NormalXFORM
;
1355 p
-> Intent
= nIntent
;
1356 p
-> ProofIntent
= ProofingIntent
;
1357 p
-> DoGamutCheck
= FALSE
;
1358 p
-> InputProfile
= InputProfile
;
1359 p
-> OutputProfile
= OutputProfile
;
1360 p
-> PreviewProfile
= ProofingProfile
;
1361 p
-> InputFormat
= InputFormat
;
1362 p
-> OutputFormat
= OutputFormat
;
1363 p
-> dwOriginalFlags
= dwFlags
;
1365 p
-> lInputV4Lab
= p
->lOutputV4Lab
= FALSE
;
1368 p
-> FromInput
= _cmsIdentifyInputFormat(p
, InputFormat
);
1369 p
-> ToOutput
= _cmsIdentifyOutputFormat(p
, OutputFormat
);
1371 // Null transform can be done without profiles
1372 if ((p
->dwOriginalFlags
& cmsFLAGS_NULLTRANSFORM
) ||
1373 ((InputProfile
== NULL
) &&
1374 (OutputProfile
== NULL
))) {
1376 p
-> xform
= NullXFORM
;
1377 return (cmsHTRANSFORM
) p
;
1380 // From here we need at least one input profile
1381 if (InputProfile
== NULL
) {
1383 cmsSignalError(LCMS_ERRC_ABORTED
, "Input profile cannot be NULL!");
1384 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1389 // Device link are means to store precalculated transform grids.
1390 if (cmsGetDeviceClass(InputProfile
) == icSigLinkClass
) {
1392 return CreateDeviceLinkTransform(p
);
1395 if (!IsProperColorSpace(InputProfile
, InputFormat
, FALSE
)) {
1397 cmsSignalError(LCMS_ERRC_ABORTED
, "Input profile is operating on wrong colorspace");
1398 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1402 p
->EntryColorSpace
= cmsGetColorSpace(InputProfile
);
1404 // Oct-21-2002: Added named color transforms
1405 if (cmsGetDeviceClass(InputProfile
) == icSigNamedColorClass
) {
1407 if (p
->NamedColorList
== NULL
)
1408 p
->NamedColorList
= cmsAllocNamedColorList(0);
1410 cmsReadICCnamedColorList(p
, InputProfile
, icSigNamedColor2Tag
);
1412 // Special case. If output profile == NULL, then the transform gives
1413 // device values from named colors.
1415 if (OutputProfile
== NULL
) {
1417 p
->ExitColorSpace
= p
-> EntryColorSpace
;
1418 p
->xform
= NC2deviceXform
;
1419 return (cmsHTRANSFORM
) p
;
1422 // Named color doesn't precalc anything
1423 p
-> dwOriginalFlags
|= cmsFLAGS_NOTPRECALC
;
1427 // From here we need also output profile.
1428 if (OutputProfile
== NULL
) {
1429 cmsSignalError(LCMS_ERRC_ABORTED
, "Output profile cannot be NULL!");
1430 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1435 if (!IsProperColorSpace(OutputProfile
, OutputFormat
, FALSE
)) {
1436 cmsSignalError(LCMS_ERRC_ABORTED
, "Output profile is operating on wrong colorspace");
1437 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1441 p
-> ExitColorSpace
= cmsGetColorSpace(OutputProfile
);
1443 // Named color only on input
1444 if (cmsGetDeviceClass(OutputProfile
) == icSigNamedColorClass
) {
1446 cmsSignalError(LCMS_ERRC_ABORTED
, "Named color profiles are not supported as output");
1447 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1451 p
-> Phase1
= GetPhase(InputProfile
);
1453 p
-> Phase3
= GetPhase(OutputProfile
);
1455 // Try to locate a LUT
1457 FromTag
= Device2PCS
[nIntent
];
1458 ToTag
= PCS2Device
[nIntent
];
1460 if (!cmsIsTag(InputProfile
, FromTag
)) {
1462 FromTag
= Device2PCS
[0];
1464 if (!cmsIsTag(InputProfile
, FromTag
)) {
1465 FromTag
= (icTagSignature
)0;
1469 // If proofing is needed, add required tags/parameters
1470 if (ProofingProfile
)
1471 CreateProof(p
, &ToTag
);
1474 if (!cmsIsTag(OutputProfile
, ToTag
)) {
1476 ToTag
= PCS2Device
[0];
1478 // 12-Dec-2003, Abstract profiles can be placed as output and still using AToB0
1479 if (cmsGetDeviceClass(OutputProfile
) == icSigAbstractClass
) {
1481 if (!cmsIsTag(OutputProfile
, ToTag
)) {
1482 ToTag
= (icTagSignature
) icSigAToB0Tag
;
1486 if (!cmsIsTag(OutputProfile
, ToTag
))
1487 ToTag
= (icTagSignature
)0;
1491 if (p
-> dwOriginalFlags
& cmsFLAGS_MATRIXINPUT
)
1492 FromTag
= (icTagSignature
)0;
1494 if (p
-> dwOriginalFlags
& cmsFLAGS_MATRIXOUTPUT
)
1495 ToTag
= (icTagSignature
)0;
1499 if (PickTransformRoutine(p
, &FromTag
, &ToTag
) == NULL
) {
1501 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1506 TakeConversionRoutines(p
, dwFlags
& cmsFLAGS_BLACKPOINTCOMPENSATION
);
1508 if (!(p
-> dwOriginalFlags
& cmsFLAGS_NOTPRECALC
)) {
1511 LPLUT GamutCheck
= NULL
;
1514 if (p
->EntryColorSpace
== icSigCmykData
&&
1515 p
->ExitColorSpace
== icSigCmykData
&&
1516 (dwFlags
& cmsFLAGS_PRESERVEBLACK
)) {
1518 DeviceLink
= _cmsPrecalculateBlackPreservingDeviceLink((cmsHTRANSFORM
) p
, dwFlags
);
1520 // Cannot be done at all?
1521 if (DeviceLink
== NULL
)
1522 DeviceLink
= _cmsPrecalculateDeviceLink((cmsHTRANSFORM
) p
, dwFlags
);
1527 DeviceLink
= _cmsPrecalculateDeviceLink((cmsHTRANSFORM
) p
, dwFlags
);
1530 // Allow to specify cmsFLAGS_GAMUTCHECK, even if no proofing profile is given
1531 if ((p
->PreviewProfile
!= NULL
) && (p
-> dwOriginalFlags
& cmsFLAGS_GAMUTCHECK
)) {
1533 GamutCheck
= _cmsPrecalculateGamutCheck((cmsHTRANSFORM
) p
);
1536 // If input colorspace is Rgb, Cmy, then use tetrahedral interpolation
1537 // for speed reasons (it only works well on spaces on Luma is diagonal, and
1538 // not if luma is in separate channel)
1539 if (p
->EntryColorSpace
== icSigRgbData
||
1540 p
->EntryColorSpace
== icSigCmyData
) {
1543 cmsCalcCLUT16ParamsEx(DeviceLink
->CLut16params
.nSamples
,
1544 DeviceLink
->CLut16params
.nInputs
,
1545 DeviceLink
->CLut16params
.nOutputs
,
1546 TRUE
, &DeviceLink
->CLut16params
);
1550 // If this is a 8-bit transform, optimize LUT further.
1552 if ((T_BYTES(InputFormat
) == 1) && (T_CHANNELS(InputFormat
) == 3)) {
1554 DeviceLink
= _cmsBlessLUT8(DeviceLink
);
1555 if (DeviceLink
== NULL
) return NULL
;
1560 p
->GamutCheck
= GamutCheck
;
1564 p
->DeviceLink
= DeviceLink
;
1566 if ((nIntent
!= INTENT_ABSOLUTE_COLORIMETRIC
) &&
1567 !(p
-> dwOriginalFlags
& cmsFLAGS_NOWHITEONWHITEFIXUP
))
1569 _cmsFixWhiteMisalignment(p
);
1575 cmsSignalError(LCMS_ERRC_ABORTED
,
1576 "Cannot precalculate %d->%d channels transform!",
1577 T_CHANNELS(InputFormat
), T_CHANNELS(OutputFormat
));
1579 cmsDeleteTransform(p
);
1584 SetPrecalculatedTransform(p
);
1589 // Re-Identify formats
1590 p
-> FromInput
= _cmsIdentifyInputFormat(p
, InputFormat
);
1591 p
-> ToOutput
= _cmsIdentifyOutputFormat(p
, OutputFormat
);
1598 // Wrapper por simpler non-proofing transforms.
1600 cmsHTRANSFORM LCMSEXPORT
cmsCreateTransform(cmsHPROFILE Input
,
1608 return cmsCreateProofingTransform(Input
, InputFormat
,
1609 Output
, OutputFormat
,
1611 Intent
, INTENT_ABSOLUTE_COLORIMETRIC
,
1616 // Profiles are *NOT* closed
1618 void LCMSEXPORT
cmsDeleteTransform(cmsHTRANSFORM hTransform
)
1620 _LPcmsTRANSFORM p
= (_LPcmsTRANSFORM
) (LPSTR
) hTransform
;
1622 if (p
-> Device2PCS
)
1623 cmsFreeLUT(p
-> Device2PCS
);
1624 if (p
-> PCS2Device
)
1625 cmsFreeLUT(p
-> PCS2Device
);
1627 cmsFreeLUT(p
-> Gamut
);
1629 cmsFreeLUT(p
-> Preview
);
1630 if (p
-> DeviceLink
)
1631 cmsFreeLUT(p
-> DeviceLink
);
1632 if (p
-> InMatShaper
)
1633 cmsFreeMatShaper(p
-> InMatShaper
);
1634 if (p
-> OutMatShaper
)
1635 cmsFreeMatShaper(p
-> OutMatShaper
);
1636 if (p
-> SmeltMatShaper
)
1637 cmsFreeMatShaper(p
-> SmeltMatShaper
);
1638 if (p
->NamedColorList
)
1639 cmsFreeNamedColorList(p
->NamedColorList
);
1640 if (p
-> GamutCheck
)
1641 cmsFreeLUT(p
-> GamutCheck
);
1643 LCMS_FREE_LOCK(&p
->rwlock
);
1645 _cmsFree((void *) p
);
1649 // Apply transform code
1650 void LCMSEXPORT
cmsDoTransform(cmsHTRANSFORM Transform
,
1652 LPVOID OutputBuffer
, unsigned int Size
)
1656 _LPcmsTRANSFORM p
= (_LPcmsTRANSFORM
) (LPSTR
) Transform
;
1658 p
-> StrideIn
= p
-> StrideOut
= Size
;
1660 p
-> xform(p
, InputBuffer
, OutputBuffer
, Size
);
1665 void LCMSEXPORT
cmsSetAlarmCodes(int r
, int g
, int b
)
1667 AlarmR
= RGB_8_TO_16(r
);
1668 AlarmG
= RGB_8_TO_16(g
);
1669 AlarmB
= RGB_8_TO_16(b
);
1672 void LCMSEXPORT
cmsGetAlarmCodes(int *r
, int *g
, int *b
)
1674 *r
= RGB_16_TO_8(AlarmR
);
1675 *g
= RGB_16_TO_8(AlarmG
);
1676 *b
= RGB_16_TO_8(AlarmB
);
1679 // Returns TRUE if the profile is implemented as matrix-shaper
1681 LCMSBOOL LCMSEXPORT
_cmsIsMatrixShaper(cmsHPROFILE hProfile
)
1683 switch (cmsGetColorSpace(hProfile
)) {
1687 return cmsIsTag(hProfile
, icSigGrayTRCTag
);
1691 return (cmsIsTag(hProfile
, icSigRedColorantTag
) &&
1692 cmsIsTag(hProfile
, icSigGreenColorantTag
) &&
1693 cmsIsTag(hProfile
, icSigBlueColorantTag
) &&
1694 cmsIsTag(hProfile
, icSigRedTRCTag
) &&
1695 cmsIsTag(hProfile
, icSigGreenTRCTag
) &&
1696 cmsIsTag(hProfile
, icSigBlueTRCTag
));
1705 LCMSBOOL LCMSEXPORT
cmsIsIntentSupported(cmsHPROFILE hProfile
,
1706 int Intent
, int UsedDirection
)
1709 icTagSignature
* TagTable
;
1711 // Device link profiles only implements the intent in header
1713 if (cmsGetDeviceClass(hProfile
) != icSigLinkClass
) {
1715 switch (UsedDirection
) {
1717 case LCMS_USED_AS_INPUT
: TagTable
= Device2PCS
; break;
1718 case LCMS_USED_AS_OUTPUT
:TagTable
= PCS2Device
; break;
1719 case LCMS_USED_AS_PROOF
: TagTable
= Preview
; break;
1722 cmsSignalError(LCMS_ERRC_ABORTED
, "Unexpected direction (%d)", UsedDirection
);
1726 if (cmsIsTag(hProfile
, TagTable
[Intent
])) return TRUE
;
1727 return _cmsIsMatrixShaper(hProfile
);
1730 return (cmsTakeRenderingIntent(hProfile
) == Intent
);
1733 // Multiple profile transform.
1735 int MultiprofileSampler(register WORD In
[], register WORD Out
[], register LPVOID Cargo
)
1737 cmsHTRANSFORM
* Transforms
= (cmsHTRANSFORM
*) Cargo
;
1740 cmsDoTransform(Transforms
[0], In
, Out
, 1);
1742 for (i
=1; Transforms
[i
]; i
++)
1743 cmsDoTransform(Transforms
[i
], Out
, Out
, 1);
1752 int IsAllowedInSingleXform(icProfileClassSignature aClass
)
1754 return (aClass
== icSigInputClass
) ||
1755 (aClass
== icSigDisplayClass
) ||
1756 (aClass
== icSigOutputClass
) ||
1757 (aClass
== icSigColorSpaceClass
);
1761 // A multiprofile transform does chain several profiles into a single
1762 // devicelink. It couls also be used to merge named color profiles into
1763 // a single database.
1766 cmsHTRANSFORM LCMSEXPORT
cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles
[],
1773 cmsHTRANSFORM Transforms
[257];
1774 DWORD dwPrecalcFlags
= (dwFlags
|cmsFLAGS_NOTPRECALC
|cmsFLAGS_NOTCACHE
);
1775 DWORD FormatInput
, FormatOutput
;
1776 cmsHPROFILE hLab
, hXYZ
, hProfile
;
1777 icColorSpaceSignature ColorSpace
, CurrentColorSpace
;
1778 icColorSpaceSignature ColorSpaceIn
, ColorSpaceOut
;
1780 int nGridPoints
, ChannelsInput
, ChannelsOutput
= 3, i
;
1784 if (nProfiles
> 255) {
1785 cmsSignalError(LCMS_ERRC_ABORTED
, "What are you trying to do with more that 255 profiles?!?, of course aborted");
1789 // There is a simple case with just two profiles, try to catch it in order of getting
1790 // black preservation to work on this function, at least with two profiles.
1793 if (nProfiles
== 2) {
1795 icProfileClassSignature Class1
= cmsGetDeviceClass(hProfiles
[0]);
1796 icProfileClassSignature Class2
= cmsGetDeviceClass(hProfiles
[1]);
1798 // Only input, output and display are allowed
1800 if (IsAllowedInSingleXform(Class1
) &&
1801 IsAllowedInSingleXform(Class2
))
1802 return cmsCreateTransform(hProfiles
[0], dwInput
, hProfiles
[1], dwOutput
, Intent
, dwFlags
);
1806 // Creates a phantom transform for latter filling
1807 p
= (_LPcmsTRANSFORM
) cmsCreateTransform(NULL
, dwInput
,
1808 NULL
, dwOutput
, Intent
, cmsFLAGS_NULLTRANSFORM
);
1810 // If user wants null one, give it
1811 if (dwFlags
& cmsFLAGS_NULLTRANSFORM
) return (cmsHPROFILE
) p
;
1813 // Is a bunch of named color profiles?
1815 for (i
=0; i
< nProfiles
; i
++) {
1816 if (cmsGetDeviceClass(hProfiles
[i
]) == icSigNamedColorClass
)
1821 if (nNamedColor
== nProfiles
) {
1823 // Yes, only named color. Create a named color-device
1824 // and append to named color table
1826 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1828 p
= (_LPcmsTRANSFORM
) cmsCreateTransform(hProfiles
[0], dwInput
, NULL
, dwOutput
, Intent
, dwFlags
);
1829 for (i
=1; i
< nProfiles
; i
++) {
1830 cmsReadICCnamedColorList(p
, hProfiles
[i
], icSigNamedColor2Tag
);
1833 return p
; // Ok, done so far
1836 if (nNamedColor
> 0) {
1838 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1839 cmsSignalError(LCMS_ERRC_ABORTED
, "Could not mix named color profiles with other types in multiprofile transform");
1844 // We will need a 3DCLUT for device link
1845 Grid
= cmsAllocLUT();
1846 if (!Grid
) return NULL
;
1848 // This one is our PCS (Always Lab)
1849 hLab
= cmsCreateLabProfile(NULL
);
1850 hXYZ
= cmsCreateXYZProfile();
1852 if (!hLab
|| !hXYZ
) goto ErrorCleanup
;
1854 // Take some info....
1856 p
->EntryColorSpace
= CurrentColorSpace
= cmsGetColorSpace(hProfiles
[0]);
1859 for (i
=0; i
< nProfiles
; i
++) {
1861 int lIsDeviceLink
, lIsInput
;
1865 hProfile
= hProfiles
[i
];
1866 lIsDeviceLink
= (cmsGetDeviceClass(hProfile
) == icSigLinkClass
);
1867 lIsInput
= (CurrentColorSpace
!= icSigXYZData
) &&
1868 (CurrentColorSpace
!= icSigLabData
);
1872 ColorSpaceIn
= cmsGetColorSpace(hProfile
);
1873 ColorSpaceOut
= cmsGetPCS(hProfile
);
1878 ColorSpaceIn
= cmsGetPCS(hProfile
);
1879 ColorSpaceOut
= cmsGetColorSpace(hProfile
);
1882 ChannelsInput
= _cmsChannelsOf(ColorSpaceIn
);
1883 ChannelsOutput
= _cmsChannelsOf(ColorSpaceOut
);
1885 FormatInput
= BYTES_SH(2)|CHANNELS_SH(ChannelsInput
);
1886 FormatOutput
= BYTES_SH(2)|CHANNELS_SH(ChannelsOutput
);
1888 ColorSpace
= ColorSpaceIn
;
1891 if (ColorSpace
== CurrentColorSpace
) {
1893 if (lIsDeviceLink
) {
1895 Transforms
[i
] = cmsCreateTransform(hProfile
, FormatInput
,
1897 Intent
, dwPrecalcFlags
);
1904 Transforms
[i
] = cmsCreateTransform(hProfile
, FormatInput
,
1905 (ColorSpaceOut
== icSigLabData
? hLab
: hXYZ
), FormatOutput
,
1906 Intent
, dwPrecalcFlags
);
1909 Transforms
[i
] = cmsCreateTransform((ColorSpaceIn
== icSigLabData
? hLab
: hXYZ
), FormatInput
,
1910 hProfile
, FormatOutput
,
1911 Intent
, dwPrecalcFlags
);
1918 else // Can come from pcs?
1919 if (CurrentColorSpace
== icSigXYZData
) {
1921 Transforms
[i
] = cmsCreateTransform(hXYZ
, FormatInput
,
1922 hProfile
, FormatOutput
,
1923 Intent
, dwPrecalcFlags
);
1927 if (CurrentColorSpace
== icSigLabData
) {
1929 Transforms
[i
] = cmsCreateTransform(hLab
, FormatInput
,
1930 hProfile
, FormatOutput
,
1931 Intent
, dwPrecalcFlags
);
1935 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsCreateMultiprofileTransform: ColorSpace mismatch");
1939 CurrentColorSpace
= ColorSpaceOut
;
1943 p
->ExitColorSpace
= CurrentColorSpace
;
1944 Transforms
[i
] = NULL
; // End marker
1946 p
->InputProfile
= hProfiles
[0];
1947 p
->OutputProfile
= hProfiles
[nProfiles
- 1];
1949 nGridPoints
= _cmsReasonableGridpointsByColorspace(p
->EntryColorSpace
, dwFlags
);
1951 ChannelsInput
= _cmsChannelsOf(cmsGetColorSpace(p
->InputProfile
));
1953 Grid
= cmsAlloc3DGrid(Grid
, nGridPoints
, ChannelsInput
, ChannelsOutput
);
1955 if (!(dwFlags
& cmsFLAGS_NOPRELINEARIZATION
))
1956 _cmsComputePrelinearizationTablesFromXFORM(Transforms
, nProfiles
, Grid
);
1958 // Compute device link on 16-bit basis
1959 if (!cmsSample3DGrid(Grid
, MultiprofileSampler
, (LPVOID
) Transforms
, Grid
-> wFlags
)) {
1965 // All ok, store the newly created LUT
1966 p
-> DeviceLink
= Grid
;
1968 SetPrecalculatedTransform(p
);
1970 for (i
=nProfiles
-1; i
>= 0; --i
)
1971 cmsDeleteTransform(Transforms
[i
]);
1974 if (hLab
) cmsCloseProfile(hLab
);
1975 if (hXYZ
) cmsCloseProfile(hXYZ
);
1978 if (p
->EntryColorSpace
== icSigRgbData
||
1979 p
->EntryColorSpace
== icSigCmyData
) {
1981 p
->DeviceLink
-> CLut16params
.Interp3D
= cmsTetrahedralInterp16
;
1985 if ((Intent
!= INTENT_ABSOLUTE_COLORIMETRIC
) &&
1986 !(dwFlags
& cmsFLAGS_NOWHITEONWHITEFIXUP
))
1987 _cmsFixWhiteMisalignment(p
);
1989 return (cmsHTRANSFORM
) p
;
1994 if (hLab
) cmsCloseProfile(hLab
);
1995 if (hXYZ
) cmsCloseProfile(hXYZ
);
2001 double LCMSEXPORT
cmsSetAdaptationState(double d
)
2003 double OldVal
= GlobalAdaptationState
;
2006 GlobalAdaptationState
= d
;