Backout bug 449422.
[wine-gecko.git] / modules / lcms / src / cmsxform.c
blob08d892680d88633de9ec83a03a3c9a91027e151e
1 //
2 // Little cms
3 // Copyright (C) 1998-2007 Marti Maria
4 //
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.
24 #include "lcms.h"
27 // Transformations stuff
28 // -----------------------------------------------------------------------
31 // Interface
33 cmsHTRANSFORM LCMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
34 DWORD InputFormat,
35 cmsHPROFILE Output,
36 DWORD OutputFormat,
37 int Intent,
38 DWORD dwFlags);
40 cmsHTRANSFORM LCMSEXPORT cmsCreateProofingTransform(cmsHPROFILE Input,
41 DWORD InputFormat,
42 cmsHPROFILE Output,
43 DWORD OutputFormat,
44 cmsHPROFILE Proofing,
45 int Intent,
46 int ProofingIntent,
47 DWORD dwFlags);
50 void LCMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform);
52 void LCMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
53 LPVOID InputBuffer,
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 // -------------------------------------------------------------------------
64 // Alarm RGB codes
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,
84 icSigPreview1Tag,
85 icSigPreview2Tag,
86 icSigPreview1Tag };
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
100 static
101 void ShaperMatrixToPCS(struct _cmstransform_struct *p,
102 WORD In[3], WORD Out[3])
104 cmsEvalMatShaper(p -> InMatShaper, In, Out);
107 // From LUT to PCS
109 static
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
118 static
119 void NC2toPCS(struct _cmstransform_struct *p,
120 WORD In[], WORD Out[3])
122 int index = In[0];
124 if (index >= p ->NamedColorList-> nColors)
125 cmsSignalError(LCMS_ERRC_WARNING, "Color %d out of range", index);
126 else
127 CopyMemory(Out, p ->NamedColorList->List[index].PCS, 3 * sizeof(WORD));
130 // From PCS to Shaper-Matrix
132 static
133 void PCStoShaperMatrix(struct _cmstransform_struct *p,
134 WORD In[3], WORD Out[3])
136 cmsEvalMatShaper(p -> OutMatShaper, In, Out);
139 // From PCS to LUT
141 static
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
161 static
162 void NullXFORM(_LPcmsTRANSFORM p,
163 LPVOID in,
164 LPVOID out, unsigned int Size)
166 register LPBYTE accum;
167 register LPBYTE output;
168 WORD wIn[MAXCHANNELS];
169 register unsigned int i, n;
172 accum = (LPBYTE) in;
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
187 static
188 void NormalXFORM(_LPcmsTRANSFORM p,
189 LPVOID in,
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];
196 WORD wGamut[1];
197 register unsigned int i, n;
201 accum = (LPBYTE) in;
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);
212 if (p -> Stage1) {
214 p -> Stage1(wStageABC, wPCS, &p->m1, &p->of1);
216 if (wPCS[0] == 0xFFFF &&
217 wPCS[1] == 0xFFFF &&
218 wPCS[2] == 0xFFFF) {
220 // White cutoff
222 output = p -> ToOutput((_LPcmsTRANSFORM) p,
223 _cmsWhiteBySpace(cmsGetColorSpace(p -> OutputProfile)),
224 output);
225 continue;
228 else
229 COPY_3CHANS(wPCS, wStageABC);
232 if (p->Gamut) {
234 // Gamut check, enabled across CLUT
236 cmsEvalLUT(p -> Gamut, wPCS, wGamut);
238 if (wGamut[0] >= 1) {
240 wOut[0] = AlarmR; // Gamut alarm
241 wOut[1] = AlarmG;
242 wOut[2] = AlarmB;
243 wOut[3] = 0;
245 output = p -> ToOutput((_LPcmsTRANSFORM)p, wOut, output);
246 continue;
250 if (p -> Preview)
252 WORD wPreview[3]; // PCS
254 cmsEvalLUT(p -> Preview, wPCS, wPreview);
255 COPY_3CHANS(wPCS, wPreview);
258 if (p -> Stage2) {
260 p -> Stage2(wPCS, wStageLMN, &p->m2, &p->of2);
262 if (wPCS[0] == 0xFFFF &&
263 wPCS[1] == 0xFFFF &&
264 wPCS[2] == 0xFFFF) {
266 // White cutoff
268 output = p -> ToOutput((_LPcmsTRANSFORM)p,
269 _cmsWhiteBySpace(cmsGetColorSpace(p -> OutputProfile)),
270 output);
272 continue;
276 else
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
289 static
290 void PrecalculatedXFORM(_LPcmsTRANSFORM p,
291 LPVOID in,
292 LPVOID out, unsigned int Size)
294 register LPBYTE accum;
295 register LPBYTE output;
296 WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
297 unsigned int i, n;
300 accum = (LPBYTE) in;
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,
314 p ->DeviceLink -> T,
315 &p ->DeviceLink -> CLut16params);
317 else
318 cmsEvalLUT(p -> DeviceLink, wIn, wOut);
321 output = p -> ToOutput(p, wOut, output);
325 // Auxiliar: Handle precalculated gamut check
327 static
328 void TransformOnePixelWithGamutCheck(_LPcmsTRANSFORM p, WORD wIn[], WORD wOut[])
330 WORD wOutOfGamut;
332 cmsEvalLUT(p ->GamutCheck, wIn, &wOutOfGamut);
334 if (wOutOfGamut >= 1) {
336 ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS);
338 wOut[0] = AlarmR;
339 wOut[1] = AlarmG;
340 wOut[2] = AlarmB;
343 else
344 cmsEvalLUT(p -> DeviceLink, wIn, wOut);
349 static
350 void PrecalculatedXFORMGamutCheck(_LPcmsTRANSFORM p,
351 LPVOID in,
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;
360 accum = (LPBYTE) in;
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
378 static
379 void CachedXFORM(_LPcmsTRANSFORM p,
380 LPVOID in,
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];
390 accum = (LPBYTE) in;
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);
414 else {
416 // Try to speedup things on plain devicelinks
418 if (p ->DeviceLink ->wFlags == LUT_HAS3DGRID) {
420 p ->DeviceLink ->CLut16params.Interp3D(wIn, wOut,
421 p ->DeviceLink -> T,
422 &p ->DeviceLink -> CLut16params);
424 else
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
447 static
448 void CachedXFORMGamutCheck(_LPcmsTRANSFORM p,
449 LPVOID in,
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];
459 accum = (LPBYTE) in;
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);
482 else {
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
502 static
503 void MatrixShaperXFORM(_LPcmsTRANSFORM p,
504 LPVOID in,
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;
513 accum = (LPBYTE) in;
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
528 static
529 void NC2deviceXform(_LPcmsTRANSFORM p,
530 LPVOID in,
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;
540 accum = (LPBYTE) in;
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.
561 static
562 void FromLstarToXYZ(LPGAMMATABLE g, LPGAMMATABLE gxyz[3])
564 int i;
565 int nPoints = 4096;
566 cmsCIELab Lab;
567 cmsCIEXYZ XYZ;
568 L16PARAMS L16;
570 // Setup interpolation across origin
571 cmsCalcL16Params(g ->nEntries, &L16);
573 // Allocate curves
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;
586 Lab.a = Lab.b = 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
599 static
600 LPMATSHAPER cmsBuildGrayInputMatrixShaper(cmsHPROFILE hProfile)
602 cmsCIEXYZ Illuminant;
603 LPGAMMATABLE GrayTRC, Shapes[3];
604 LPMATSHAPER MatShaper;
605 MAT3 Scale;
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);
617 else {
618 Shapes[0] = cmsDupGamma(GrayTRC);
619 Shapes[1] = cmsDupGamma(GrayTRC);
620 Shapes[2] = cmsDupGamma(GrayTRC);
623 if (!Shapes[0] || !Shapes[1] || !Shapes[2])
624 return NULL;
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);
637 return MatShaper;
642 // Monochrome as output
644 static
645 LPMATSHAPER cmsBuildGrayOutputMatrixShaper(cmsHPROFILE hProfile)
647 cmsCIEXYZ Illuminant;
648 LPGAMMATABLE GrayTRC, Shapes[3];
649 LPMATSHAPER MatShaper;
650 MAT3 Scale;
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);
672 else {
674 // Normal case
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])
684 return NULL;
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);
695 return MatShaper;
701 // Input matrix, only in XYZ
703 LPMATSHAPER cmsBuildInputMatrixShaper(cmsHPROFILE InputProfile)
705 MAT3 DoubleMat;
706 LPGAMMATABLE Shapes[3];
707 LPMATSHAPER InMatSh;
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))
719 return NULL;
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])
726 return NULL;
728 InMatSh = cmsAllocMatShaper(&DoubleMat, Shapes, MATSHAPER_INPUT);
730 cmsFreeGammaTriple(Shapes);
732 return InMatSh;
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))
754 return NULL;
756 if (MAT3inverse(&DoubleMat, &DoubleInv) < 0)
757 return NULL;
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);
768 return OutMatSh;
773 // This function builds a transform matrix chaining parameters
775 static
776 LCMSBOOL cmsBuildSmeltMatShaper(_LPcmsTRANSFORM p)
778 MAT3 From, To, ToInv, Transfer;
779 LPGAMMATABLE In[3], InverseOut[3];
782 if (!cmsReadICCMatrixRGB2XYZ(&From, p -> InputProfile))
783 return FALSE;
786 if (!cmsReadICCMatrixRGB2XYZ(&To, p -> OutputProfile))
787 return FALSE;
789 // invert dest
791 if (MAT3inverse(&To, &ToInv) < 0)
792 return FALSE;
794 // Multiply
795 MAT3per(&Transfer, &ToInv, &From);
798 // Read gamma curves
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])
805 return FALSE;
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);
814 return FALSE;
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
832 static
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");
844 return XYZRel;
850 static
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)) {
883 DoBPC = TRUE;
887 // Black point compensation does not apply to absolute intent
889 if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC)
890 DoBPC = FALSE;
892 // Black point compensation does not apply to devicelink profiles
894 if (cmsGetDeviceClass(p ->InputProfile) == icSigLinkClass)
895 DoBPC = FALSE;
897 if (cmsGetDeviceClass(p ->OutputProfile) == icSigLinkClass)
898 DoBPC = FALSE;
900 if (DoBPC) {
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)
912 DoBPC = FALSE;
917 cmsChooseCnvrt(p -> Intent == INTENT_ABSOLUTE_COLORIMETRIC,
919 p -> Phase1,
920 &BlackPointIn,
921 &WhitePointIn,
922 &IlluminantIn,
923 &ChromaticAdaptationMatrixIn,
925 p -> Phase3,
926 &BlackPointOut,
927 &WhitePointOut,
928 &IlluminantOut,
929 &ChromaticAdaptationMatrixOut,
931 DoBPC,
932 p ->AdaptationState,
933 &p->Stage1,
934 &p->m1, &p->of1);
937 else // Proofing
941 cmsTakeIluminant(&IlluminantProof, p -> PreviewProfile);
942 cmsTakeMediaWhitePoint(&WhitePointProof, p -> PreviewProfile);
943 cmsTakeMediaBlackPoint(&BlackPointProof, p -> PreviewProfile);
944 cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixProof, p -> PreviewProfile);
946 if (DoBPC) {
948 cmsDetectBlackPoint(&BlackPointProof, p->PreviewProfile, p->Intent, 0);
949 cmsDetectBlackPoint(&BlackPointIn, p->InputProfile, p->Intent, 0);
950 cmsDetectBlackPoint(&BlackPointOut, p->OutputProfile, p->Intent, 0);
952 // Reality check
954 if (BlackPointIn.X == BlackPointProof.X &&
955 BlackPointIn.Y == BlackPointProof.Y &&
956 BlackPointIn.Z == BlackPointProof.Z)
957 DoBPC = FALSE;
964 cmsChooseCnvrt(p -> Intent == INTENT_ABSOLUTE_COLORIMETRIC,
966 p -> Phase1,
967 &BlackPointIn,
968 &WhitePointIn,
969 &IlluminantIn,
970 &ChromaticAdaptationMatrixIn,
972 p -> Phase2,
973 &BlackPointProof,
974 &WhitePointProof,
975 &IlluminantProof,
976 &ChromaticAdaptationMatrixProof,
977 DoBPC,
978 p ->AdaptationState,
979 &p->Stage1,
980 &p->m1, &p->of1);
982 cmsChooseCnvrt(p -> ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC,
984 p -> Phase2,
985 &BlackPointProof,
986 &WhitePointProof,
987 &IlluminantProof,
988 &ChromaticAdaptationMatrixProof,
990 p -> Phase3,
991 &BlackPointOut,
992 &WhitePointOut,
993 &IlluminantOut,
994 &ChromaticAdaptationMatrixOut,
996 0.0,
997 &p->Stage2,
998 &p->m2, &p->of2);
1004 // Check colorspace
1006 static
1007 LCMSBOOL IsProperColorSpace(cmsHPROFILE hProfile, DWORD dwFormat, LCMSBOOL lUsePCS)
1009 int Space = T_COLORSPACE(dwFormat);
1011 if (Space == PT_ANY) return TRUE;
1013 if (lUsePCS)
1014 return (Space == _cmsLCMScolorSpace(cmsGetPCS(hProfile)));
1015 else
1016 return (Space == _cmsLCMScolorSpace(cmsGetColorSpace(hProfile)));
1020 // Auxiliary: allocate transform struct and set to defaults
1022 static
1023 _LPcmsTRANSFORM AllocEmptyTransform(void)
1025 // Allocate needed memory
1027 _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) _cmsMalloc(sizeof(_cmsTRANSFORM));
1028 if (!p) {
1030 cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateTransform: _cmsMalloc() failed");
1031 return NULL;
1034 ZeroMemory(p, sizeof(_cmsTRANSFORM));
1036 // Initialize default methods
1038 p -> xform = NULL;
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;
1046 p -> Gamut = 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);
1058 return p;
1062 // Identify whatever a transform is to be cached
1064 static
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;
1079 else {
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
1094 static
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");
1100 return NULL;
1103 if (!IsProperColorSpace(p->InputProfile, p->OutputFormat, TRUE)) {
1104 cmsSignalError(LCMS_ERRC_ABORTED, "Device link is operating on wrong colorspace on output");
1105 return NULL;
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);
1116 return NULL;
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");
1127 p -> Phase1 = -1;
1128 p -> Phase2 = -1;
1129 p -> Phase3 = -1;
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
1148 static
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;
1177 if (ProofTag) {
1179 p -> Preview = cmsReadICCLut(p ->PreviewProfile, ProofTag);
1180 p -> Phase2 = GetPhase(p ->PreviewProfile);
1183 else
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);
1214 else {
1216 // Nope, cannot be done.
1218 cmsSignalError(LCMS_ERRC_WARNING, "Sorry, the proof profile has not gamut checking capabilities");
1219 p -> Gamut = NULL;
1227 // Choose the adequate transform routine
1229 static
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;
1244 else {
1245 // Can we optimize matrix-shaper only transform?
1247 if ((*FromTagPtr == 0) &&
1248 (*ToTagPtr == 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");
1262 return NULL;
1265 p -> Phase1 = p -> Phase3 = XYZRel;
1266 return p;
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");
1279 return NULL;
1283 else
1285 p -> FromDevice = ShaperMatrixToPCS;
1286 p -> InMatShaper = cmsBuildInputMatrixShaper(p -> InputProfile);
1288 if (!p ->InMatShaper) {
1289 cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for input");
1290 return NULL;
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");
1304 return NULL;
1308 else
1310 p -> ToDevice = PCStoShaperMatrix;
1311 p -> OutMatShaper = cmsBuildOutputMatrixShaper(p->OutputProfile);
1313 if (!p -> OutMatShaper) {
1314 cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for output");
1315 return NULL;
1317 p -> Phase3 = XYZRel;
1322 return p;
1328 // Create a transform.
1330 cmsHTRANSFORM LCMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1331 DWORD InputFormat,
1332 cmsHPROFILE OutputProfile,
1333 DWORD OutputFormat,
1334 cmsHPROFILE ProofingProfile,
1335 int nIntent,
1336 int ProofingIntent,
1337 DWORD dwFlags)
1340 _LPcmsTRANSFORM p;
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");
1348 return NULL;
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);
1385 return NULL;
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);
1399 return NULL;
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);
1431 return NULL;
1435 if (!IsProperColorSpace(OutputProfile, OutputFormat, FALSE)) {
1436 cmsSignalError(LCMS_ERRC_ABORTED, "Output profile is operating on wrong colorspace");
1437 cmsDeleteTransform((cmsHTRANSFORM) p);
1438 return NULL;
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);
1448 return NULL;
1451 p -> Phase1 = GetPhase(InputProfile);
1452 p -> Phase2 = -1;
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);
1502 return NULL;
1506 TakeConversionRoutines(p, dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1508 if (!(p -> dwOriginalFlags & cmsFLAGS_NOTPRECALC)) {
1510 LPLUT DeviceLink;
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);
1525 else {
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;
1562 if (DeviceLink) {
1564 p ->DeviceLink = DeviceLink;
1566 if ((nIntent != INTENT_ABSOLUTE_COLORIMETRIC) &&
1567 !(p -> dwOriginalFlags & cmsFLAGS_NOWHITEONWHITEFIXUP))
1569 _cmsFixWhiteMisalignment(p);
1572 else
1575 cmsSignalError(LCMS_ERRC_ABORTED,
1576 "Cannot precalculate %d->%d channels transform!",
1577 T_CHANNELS(InputFormat), T_CHANNELS(OutputFormat));
1579 cmsDeleteTransform(p);
1580 return NULL;
1584 SetPrecalculatedTransform(p);
1589 // Re-Identify formats
1590 p -> FromInput = _cmsIdentifyInputFormat(p, InputFormat);
1591 p -> ToOutput = _cmsIdentifyOutputFormat(p, OutputFormat);
1594 return p;
1598 // Wrapper por simpler non-proofing transforms.
1600 cmsHTRANSFORM LCMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1601 DWORD InputFormat,
1602 cmsHPROFILE Output,
1603 DWORD OutputFormat,
1604 int Intent,
1605 DWORD dwFlags)
1608 return cmsCreateProofingTransform(Input, InputFormat,
1609 Output, OutputFormat,
1610 NULL,
1611 Intent, INTENT_ABSOLUTE_COLORIMETRIC,
1612 dwFlags);
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);
1626 if (p -> Gamut)
1627 cmsFreeLUT(p -> Gamut);
1628 if (p -> Preview)
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,
1651 LPVOID InputBuffer,
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)) {
1685 case icSigGrayData:
1687 return cmsIsTag(hProfile, icSigGrayTRCTag);
1689 case icSigRgbData:
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));
1698 default:
1700 return FALSE;
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;
1721 default:
1722 cmsSignalError(LCMS_ERRC_ABORTED, "Unexpected direction (%d)", UsedDirection);
1723 return FALSE;
1726 if (cmsIsTag(hProfile, TagTable[Intent])) return TRUE;
1727 return _cmsIsMatrixShaper(hProfile);
1730 return (cmsTakeRenderingIntent(hProfile) == Intent);
1733 // Multiple profile transform.
1734 static
1735 int MultiprofileSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
1737 cmsHTRANSFORM* Transforms = (cmsHTRANSFORM*) Cargo;
1738 int i;
1740 cmsDoTransform(Transforms[0], In, Out, 1);
1742 for (i=1; Transforms[i]; i++)
1743 cmsDoTransform(Transforms[i], Out, Out, 1);
1747 return TRUE;
1751 static
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[],
1767 int nProfiles,
1768 DWORD dwInput,
1769 DWORD dwOutput,
1770 int Intent,
1771 DWORD dwFlags)
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;
1779 LPLUT Grid;
1780 int nGridPoints, ChannelsInput, ChannelsOutput = 3, i;
1781 _LPcmsTRANSFORM p;
1782 int nNamedColor;
1784 if (nProfiles > 255) {
1785 cmsSignalError(LCMS_ERRC_ABORTED, "What are you trying to do with more that 255 profiles?!?, of course aborted");
1786 return NULL;
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?
1814 nNamedColor = 0;
1815 for (i=0; i < nProfiles; i++) {
1816 if (cmsGetDeviceClass(hProfiles[i]) == icSigNamedColorClass)
1817 nNamedColor++;
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
1835 else
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");
1840 return NULL;
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;
1863 // Check colorspace
1865 hProfile = hProfiles[i];
1866 lIsDeviceLink = (cmsGetDeviceClass(hProfile) == icSigLinkClass);
1867 lIsInput = (CurrentColorSpace != icSigXYZData) &&
1868 (CurrentColorSpace != icSigLabData);
1870 if (lIsInput) {
1872 ColorSpaceIn = cmsGetColorSpace(hProfile);
1873 ColorSpaceOut = cmsGetPCS(hProfile);
1876 else {
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,
1896 NULL, FormatOutput,
1897 Intent, dwPrecalcFlags);
1900 else {
1902 if (lIsInput) {
1904 Transforms[i] = cmsCreateTransform(hProfile, FormatInput,
1905 (ColorSpaceOut == icSigLabData ? hLab : hXYZ), FormatOutput,
1906 Intent, dwPrecalcFlags);
1908 else {
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);
1926 else
1927 if (CurrentColorSpace == icSigLabData) {
1929 Transforms[i] = cmsCreateTransform(hLab, FormatInput,
1930 hProfile, FormatOutput,
1931 Intent, dwPrecalcFlags);
1934 else {
1935 cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateMultiprofileTransform: ColorSpace mismatch");
1936 goto ErrorCleanup;
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)) {
1961 cmsFreeLUT(Grid);
1962 goto ErrorCleanup;
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;
1992 ErrorCleanup:
1994 if (hLab) cmsCloseProfile(hLab);
1995 if (hXYZ) cmsCloseProfile(hXYZ);
1996 return NULL;
2001 double LCMSEXPORT cmsSetAdaptationState(double d)
2003 double OldVal = GlobalAdaptationState;
2005 if (d >= 0)
2006 GlobalAdaptationState = d;
2008 return OldVal;