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 // Determine if we can build with SSE2 (this was partly copied from jmorecfg.h in
63 // -------------------------------------------------------------------------
64 #if defined(_M_IX86) && !defined(__GNUC__)
66 /* Get us a CPUID function. Avoid clobbering EBX because sometimes it's the PIC
67 register - I'm not sure if that ever happens on windows, but cpuid isn't
68 on the critical path so we just preserve the register to be safe and to be
69 consistent with the non-windows version. */
70 LCMS_INLINE
void LCMSCPUID(DWORD fxn
, LPDWORD a
, LPDWORD b
, LPDWORD c
, LPDWORD d
) {
89 #define HAVE_MMX_INTEL_MNEMONICS
91 /* SSE2 code appears broken for some cpus (bug 247437) */
92 #define HAVE_SSE2_INTEL_MNEMONICS
93 #define HAVE_SSE2_INTRINSICS
96 #if defined(__GNUC__) && defined(__i386__)
98 /* Get us a CPUID function. We can't use ebx because it's the PIC register on
99 some platforms, so we use ESI instead and save ebx to avoid clobbering it. */
100 LCMS_INLINE
void LCMSCPUID(DWORD fxn
, LPDWORD a
, LPDWORD b
, LPDWORD c
, LPDWORD d
) {
102 DWORD a_
, b_
, c_
, d_
;
103 __asm__
__volatile__ ("xchgl %%ebx, %%esi; cpuid; xchgl %%ebx, %%esi;"
104 : "=a" (a_
), "=S" (b_
), "=c" (c_
), "=d" (d_
) : "a" (fxn
));
111 #define HAVE_SSE2_INTRINSICS
112 /* XXX - the below wasn't in jpeg/jmorecfg.h - why? */
113 #define HAVE_SSE2_INTEL_MNEMONICS
114 #endif /* ! GNUC && i386 */
120 static WORD AlarmR
= 0x8fff, AlarmG
= 0x8fff, AlarmB
= 0x8fff;
122 // Tag tables, soted by intents
124 static icTagSignature Device2PCS
[] = {icSigAToB0Tag
, // Perceptual
125 icSigAToB1Tag
, // Relative colorimetric
126 icSigAToB2Tag
, // Saturation
127 icSigAToB1Tag
}; // Absolute colorimetric
128 // (Relative/WhitePoint)
130 static icTagSignature PCS2Device
[] = {icSigBToA0Tag
, // Perceptual
131 icSigBToA1Tag
, // Relative colorimetric
132 icSigBToA2Tag
, // Saturation
133 icSigBToA1Tag
}; // Absolute colorimetric
134 // (Relative/WhitePoint)
137 static icTagSignature Preview
[] = {icSigPreview0Tag
,
144 static volatile double GlobalAdaptationState
= 0;
146 // -------------------------Runtime SSE2 Detection-----------------------------
148 #define SSE2_EDX_MASK (1UL << 26)
149 static LCMSBOOL
SSE2Available() {
151 static int isAvailable
= -1;
153 DWORD function
= 0x00000001;
155 if (isAvailable
== -1) {
157 // If we don't have compile-time support, we don't have runtime support
158 #ifndef HAVE_SSE2_INTEL_MNEMONICS
161 /* We have CPUID macros defined if we have sse2 mnemonics. */
162 LCMSCPUID(function
, &a
, &b
, &c
, &d
);
163 if (d
& SSE2_EDX_MASK
)
170 return (isAvailable
) ? TRUE
: FALSE
;
173 // --------------------------------Stages--------------------------------------
175 // Following routines does implement several kind of steps inside
176 // transform. On building the transform, code chooses adequate.
179 // From Shaper-Matrix to PCS
182 void ShaperMatrixToPCS(struct _cmstransform_struct
*p
,
183 WORD In
[3], WORD Out
[3])
185 cmsEvalMatShaper(p
-> InMatShaper
, In
, Out
);
191 void LUTtoPCS(struct _cmstransform_struct
*p
,
192 WORD In
[], WORD Out
[3])
194 cmsEvalLUT(p
-> Device2PCS
, In
, Out
);
197 // From indexed named color to PCS
200 void NC2toPCS(struct _cmstransform_struct
*p
,
201 WORD In
[], WORD Out
[3])
205 if (index
>= p
->NamedColorList
-> nColors
)
206 cmsSignalError(LCMS_ERRC_WARNING
, "Color %d out of range", index
);
208 CopyMemory(Out
, p
->NamedColorList
->List
[index
].PCS
, 3 * sizeof(WORD
));
211 // From PCS to Shaper-Matrix
214 void PCStoShaperMatrix(struct _cmstransform_struct
*p
,
215 WORD In
[3], WORD Out
[3])
217 cmsEvalMatShaper(p
-> OutMatShaper
, In
, Out
);
223 void PCStoLUT(struct _cmstransform_struct
*p
,
224 WORD In
[3], WORD Out
[])
226 cmsEvalLUT(p
-> PCS2Device
, In
, Out
);
232 // ----------------------- TRANSFORMATIONS --------------------------
235 // Inlining some assignations
237 #define COPY_3CHANS(to, from) { to[0]=from[0]; to[1]=from[1]; to[2]=from[2]; }
240 // Null transformation, only hold channels
243 void NullXFORM(_LPcmsTRANSFORM p
,
245 LPVOID out
, unsigned int Size
)
247 register LPBYTE accum
;
248 register LPBYTE output
;
249 WORD wIn
[MAXCHANNELS
];
250 register unsigned int i
, n
;
254 output
= (LPBYTE
) out
;
255 n
= Size
; // Buffer len
257 // If the input and output formats are the same,
258 // we don't need to pack and unpack pixels
259 if (p
-> InputFormat
== p
-> OutputFormat
) {
261 // Only copy bytes if the buffers aren't the same
264 // Copy in a nondestructive manner in case
265 // the buffers overlap for some reason
267 Size
* T_BYTES(p
-> InputFormat
) * (T_CHANNELS(p
-> InputFormat
) + T_EXTRA(p
-> InputFormat
)));
273 for (i
=0; i
< n
; i
++)
275 accum
= p
-> FromInput(p
, wIn
, accum
);
276 output
= p
-> ToOutput(p
, wIn
, output
);
282 // This is the "normal" proofing transform
285 void NormalXFORM(_LPcmsTRANSFORM p
,
287 LPVOID out
, unsigned int Size
)
289 register LPBYTE accum
;
290 register LPBYTE output
;
291 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
292 WORD wStageABC
[3], wPCS
[3], wStageLMN
[MAXCHANNELS
];
294 register unsigned int i
, n
;
299 output
= (LPBYTE
) out
;
300 n
= Size
; // Buffer len
302 for (i
=0; i
< n
; i
++)
305 accum
= p
-> FromInput(p
, wIn
, accum
);
307 p
-> FromDevice(p
, wIn
, wStageABC
);
311 p
-> Stage1(wStageABC
, wPCS
, &p
->m1
, &p
->of1
);
313 if (wPCS
[0] == 0xFFFF &&
319 output
= p
-> ToOutput((_LPcmsTRANSFORM
) p
,
320 _cmsWhiteBySpace(cmsGetColorSpace(p
-> OutputProfile
)),
326 COPY_3CHANS(wPCS
, wStageABC
);
331 // Gamut check, enabled across CLUT
333 cmsEvalLUT(p
-> Gamut
, wPCS
, wGamut
);
335 if (wGamut
[0] >= 1) {
337 wOut
[0] = AlarmR
; // Gamut alarm
342 output
= p
-> ToOutput((_LPcmsTRANSFORM
)p
, wOut
, output
);
349 WORD wPreview
[3]; // PCS
351 cmsEvalLUT(p
-> Preview
, wPCS
, wPreview
);
352 COPY_3CHANS(wPCS
, wPreview
);
357 p
-> Stage2(wPCS
, wStageLMN
, &p
->m2
, &p
->of2
);
359 if (wPCS
[0] == 0xFFFF &&
365 output
= p
-> ToOutput((_LPcmsTRANSFORM
)p
,
366 _cmsWhiteBySpace(cmsGetColorSpace(p
-> OutputProfile
)),
374 COPY_3CHANS(wStageLMN
, wPCS
);
376 // Here wOut may come as MAXCHANNELS channels
378 p
-> ToDevice(p
, wStageLMN
, wOut
);
380 output
= p
-> ToOutput((_LPcmsTRANSFORM
)p
, wOut
, output
);
384 // Using precalculated LUT
387 void PrecalculatedXFORM(_LPcmsTRANSFORM p
,
389 LPVOID out
, unsigned int Size
)
391 register LPBYTE accum
;
392 register LPBYTE output
;
393 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
398 output
= (LPBYTE
) out
;
399 n
= Size
; // Buffer len
402 for (i
=0; i
< n
; i
++) {
404 accum
= p
-> FromInput(p
, wIn
, accum
);
406 // Try to speedup things on plain devicelinks
408 if (p
->DeviceLink
->wFlags
== LUT_HAS3DGRID
) {
410 p
->DeviceLink
->CLut16params
.Interp3D(wIn
, wOut
,
412 &p
->DeviceLink
-> CLut16params
);
415 cmsEvalLUT(p
-> DeviceLink
, wIn
, wOut
);
418 output
= p
-> ToOutput(p
, wOut
, output
);
422 // Auxiliar: Handle precalculated gamut check
425 void TransformOnePixelWithGamutCheck(_LPcmsTRANSFORM p
, WORD wIn
[], WORD wOut
[])
429 cmsEvalLUT(p
->GamutCheck
, wIn
, &wOutOfGamut
);
431 if (wOutOfGamut
>= 1) {
433 ZeroMemory(wOut
, sizeof(WORD
) * MAXCHANNELS
);
441 cmsEvalLUT(p
-> DeviceLink
, wIn
, wOut
);
447 void PrecalculatedXFORMGamutCheck(_LPcmsTRANSFORM p
,
449 LPVOID out
, unsigned int Size
)
451 register LPBYTE accum
;
452 register LPBYTE output
;
453 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
454 register unsigned int i
, n
;
458 output
= (LPBYTE
) out
;
459 n
= Size
; // Buffer len
461 for (i
=0; i
< n
; i
++) {
463 accum
= p
-> FromInput(p
, wIn
, accum
);
465 TransformOnePixelWithGamutCheck(p
, wIn
, wOut
);
467 output
= p
-> ToOutput(p
, wOut
, output
);
473 // Using precalculated LUT + Cache
476 void CachedXFORM(_LPcmsTRANSFORM p
,
478 LPVOID out
, unsigned int Size
)
480 register LPBYTE accum
;
481 register LPBYTE output
;
482 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
483 register unsigned int i
, n
;
484 WORD CacheIn
[MAXCHANNELS
], CacheOut
[MAXCHANNELS
];
488 output
= (LPBYTE
) out
;
489 n
= Size
; // Buffer len
491 // Empty buffers for quick memcmp
493 ZeroMemory(wIn
, sizeof(WORD
) * MAXCHANNELS
);
494 ZeroMemory(wOut
, sizeof(WORD
) * MAXCHANNELS
);
497 LCMS_READ_LOCK(&p
->rwlock
);
498 CopyMemory(CacheIn
, p
->CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
499 CopyMemory(CacheOut
, p
->CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
500 LCMS_UNLOCK(&p
->rwlock
);
502 for (i
=0; i
< n
; i
++) {
504 accum
= p
-> FromInput(p
, wIn
, accum
);
507 if (memcmp(wIn
, CacheIn
, sizeof(WORD
) * MAXCHANNELS
) == 0) {
509 CopyMemory(wOut
, CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
513 // Try to speedup things on plain devicelinks
515 if (p
->DeviceLink
->wFlags
== LUT_HAS3DGRID
) {
517 p
->DeviceLink
->CLut16params
.Interp3D(wIn
, wOut
,
519 &p
->DeviceLink
-> CLut16params
);
522 cmsEvalLUT(p
-> DeviceLink
, wIn
, wOut
);
525 CopyMemory(CacheIn
, wIn
, sizeof(WORD
) * MAXCHANNELS
);
526 CopyMemory(CacheOut
, wOut
, sizeof(WORD
) * MAXCHANNELS
);
529 output
= p
-> ToOutput(p
, wOut
, output
);
533 LCMS_WRITE_LOCK(&p
->rwlock
);
534 CopyMemory(p
->CacheIn
, CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
535 CopyMemory(p
->CacheOut
, CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
536 LCMS_UNLOCK(&p
->rwlock
);
542 // Using precalculated LUT + Cache
545 void CachedXFORMGamutCheck(_LPcmsTRANSFORM p
,
547 LPVOID out
, unsigned int Size
)
549 register LPBYTE accum
;
550 register LPBYTE output
;
551 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
552 register unsigned int i
, n
;
553 WORD CacheIn
[MAXCHANNELS
], CacheOut
[MAXCHANNELS
];
557 output
= (LPBYTE
) out
;
558 n
= Size
; // Buffer len
560 // Empty buffers for quick memcmp
562 ZeroMemory(wIn
, sizeof(WORD
) * MAXCHANNELS
);
563 ZeroMemory(wOut
, sizeof(WORD
) * MAXCHANNELS
);
565 LCMS_READ_LOCK(&p
->rwlock
);
566 CopyMemory(CacheIn
, p
->CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
567 CopyMemory(CacheOut
, p
->CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
568 LCMS_UNLOCK(&p
->rwlock
);
571 for (i
=0; i
< n
; i
++) {
573 accum
= p
-> FromInput(p
, wIn
, accum
);
575 if (memcmp(wIn
, CacheIn
, sizeof(WORD
) * MAXCHANNELS
) == 0) {
577 CopyMemory(wOut
, CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
581 TransformOnePixelWithGamutCheck(p
, wIn
, wOut
);
583 CopyMemory(CacheIn
, wIn
, sizeof(WORD
) * MAXCHANNELS
);
584 CopyMemory(CacheOut
, wOut
, sizeof(WORD
) * MAXCHANNELS
);
587 output
= p
-> ToOutput(p
, wOut
, output
);
590 LCMS_WRITE_LOCK(&p
->rwlock
);
591 CopyMemory(p
->CacheIn
, CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
592 CopyMemory(p
->CacheOut
, CacheOut
, sizeof(WORD
) * MAXCHANNELS
);
593 LCMS_UNLOCK(&p
->rwlock
);
597 // Using smelted Matrix/Shaper
600 void MatrixShaperXFORM(_LPcmsTRANSFORM p
,
602 LPVOID out
, unsigned int Size
)
604 register LPBYTE accum
;
605 register LPBYTE output
;
606 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
607 register unsigned int i
, n
;
611 output
= (LPBYTE
) out
;
612 n
= Size
; // Buffer len
614 for (i
=0; i
< n
; i
++)
616 accum
= p
-> FromInput(p
, wIn
, accum
);
617 cmsEvalMatShaper(p
-> SmeltMatShaper
, wIn
, wOut
);
618 output
= p
-> ToOutput(p
, wOut
, output
);
622 static const FLOAT floatScale
= 65536.0f
;
623 static const FLOAT
* const floatScaleAddr
= &floatScale
; // Win32 ASM doesn't know how to take addressOf inline
625 #ifdef HAVE_SSE2_INTEL_MNEMONICS
627 void MatrixShaperXFORMFloat(_LPcmsTRANSFORM p
,
629 LPVOID out
, unsigned int Size
)
631 register LPBYTE In
, Out
;
632 register unsigned int i
;
633 LPMATSHAPER MatShaper
;
636 // We support a very limited number of configurations for the floatpath
637 CMSASSERT(p
->InputFormat
== p
->OutputFormat
);
638 CMSASSERT((p
->InputFormat
== TYPE_RGB_8
) || (p
->InputFormat
== TYPE_RGBA_8
));
640 Increment
= (p
->InputFormat
== TYPE_RGB_8
) ? 3 : 4;
643 MatShaper
= p
-> SmeltMatShaper
;
645 for (i
=0; i
< Size
; i
++)
648 LPFVEC3 FloatVals
= &MatShaper
-> Matrix
.FA
.F
->v
[3]; // Access our secret aligned temp buffer
649 LPFVEC3 MatPtr
= MatShaper
-> Matrix
.FA
.F
->v
; // Matrix
650 LPFLOAT clampMax
= &MatShaper
-> clampMax
;
651 LPDWORD tmp
= (LPDWORD
) FloatVals
;
653 if (MatShaper
-> dwFlags
& MATSHAPER_HASINPSHAPER
)
655 if (MatShaper
->L2_Precache
!= NULL
)
657 FloatVals
->n
[VX
] = MatShaper
->L2_Precache
->Impl
.LI8F_FORWARD
.Cache
[0][In
[0]];
658 FloatVals
->n
[VY
] = MatShaper
->L2_Precache
->Impl
.LI8F_FORWARD
.Cache
[1][In
[1]];
659 FloatVals
->n
[VZ
] = MatShaper
->L2_Precache
->Impl
.LI8F_FORWARD
.Cache
[2][In
[2]];
663 FloatVals
->n
[VX
] = ToFloatDomain(cmsLinearInterpLUT16(RGB_8_TO_16(In
[0]), MatShaper
-> L2
[0], &MatShaper
-> p2_16
));
664 FloatVals
->n
[VY
] = ToFloatDomain(cmsLinearInterpLUT16(RGB_8_TO_16(In
[1]), MatShaper
-> L2
[1], &MatShaper
-> p2_16
));
665 FloatVals
->n
[VZ
] = ToFloatDomain(cmsLinearInterpLUT16(RGB_8_TO_16(In
[2]), MatShaper
-> L2
[2], &MatShaper
-> p2_16
));
670 FloatVals
->n
[VX
] = ToFloatDomain(RGB_8_TO_16(In
[0]));
671 FloatVals
->n
[VY
] = ToFloatDomain(RGB_8_TO_16(In
[1]));
672 FloatVals
->n
[VZ
] = ToFloatDomain(RGB_8_TO_16(In
[2]));
675 if (MatShaper
-> dwFlags
& MATSHAPER_HASMATRIX
)
679 "movaps (%0), %%xmm1;\n\t" // Move the first matrix column to xmm1
680 "movaps 16(%0), %%xmm2;\n\t" // Move the second matrix column to xmm2
681 "movaps 32(%0), %%xmm3;\n\t" // move the third matrix column to xmm3
682 "movaps 48(%0), %%xmm0;\n\t" // Move the vector to xmm0
684 // Note - We have to copy and then shuffle because of the weird
685 // semantics of shufps
687 "movaps %%xmm0, %%xmm4;\n\t" // Copy the vector to xmm4
688 "shufps $0, %%xmm4, %%xmm4;\n\t" // Shuffle to repeat the first vector element repeated 4 times
689 "mulps %%xmm4, %%xmm1;\n\t" // Multiply the first vector element by the first matrix column
690 "movaps %%xmm0, %%xmm5; \n\t" // Copy the vector to xmm5
691 "shufps $0x55, %%xmm5, %%xmm5;\n\t" // Shuffle to repeat the second vector element repeated 4 times
692 "mulps %%xmm5, %%xmm2;\n\t" // Multiply the second vector element by the seccond matrix column
693 "movaps %%xmm0, %%xmm6;\n\t" // Copy the vector to xmm6
694 "shufps $0xAA, %%xmm6, %%xmm6;\n\t" // Shuffle to repeat the third vector element repeated 4 times
695 "mulps %%xmm6, %%xmm3;\n\t" // Multiply the third vector element by the third matrix column
697 "addps %%xmm3, %%xmm2;\n\t" // Sum (second + third) columns
698 "addps %%xmm2, %%xmm1;\n\t" // Sum ((second + third) + first) columns
700 "movss (%1), %%xmm7;\n\t" // load the floating point representation of 65535/65536
701 "shufps $0, %%xmm7, %%xmm7;\n\t" // move it into all of the four slots
702 "minps %%xmm7, %%xmm1;\n\t" // clamp the vector to 1.0 max
703 "xorps %%xmm6, %%xmm6;\n\t" // get us cleared bitpatern, which is 0.0f
704 "maxps %%xmm6, %%xmm1;\n\t" // clamp the vector to 0.0 min
705 "movss (%2), %%xmm5;\n\t" // load the floating point scale factor
706 "shufps $0, %%xmm5, %%xmm5;\n\t" // put it in all four slots
707 "mulps %%xmm5, %%xmm1;\n\t" // multiply by the scale factor
708 "cvtps2dq %%xmm1, %%xmm1;\n\t" // convert to integers
709 "movdqa %%xmm1, 48(%0);\n\t" // store
712 : "r" (MatPtr
), "r" (clampMax
), "r" (&floatScale
)
719 mov edx
, floatScaleAddr
722 movaps xmm2
, [eax
+ 16]
723 movaps xmm3
, [eax
+ 32]
724 movaps xmm0
, [eax
+ 48]
730 shufps xmm5
, xmm5
, 0x55
733 shufps xmm6
, xmm6
, 0xAA
748 movdqa
[eax
+ 48], xmm1
755 tmp
[0] = _cmsClampWord(FromFloatDomain(FloatVals
->n
[VX
]));
756 tmp
[1] = _cmsClampWord(FromFloatDomain(FloatVals
->n
[VY
]));
757 tmp
[2] = _cmsClampWord(FromFloatDomain(FloatVals
->n
[VZ
]));
760 if (MatShaper
-> dwFlags
& MATSHAPER_HASSHAPER
)
762 if (MatShaper
->L_Precache
!= NULL
)
764 Out
[0] = MatShaper
->L_Precache
->Impl
.LI168_REVERSE
.Cache
[0][tmp
[0]];
765 Out
[1] = MatShaper
->L_Precache
->Impl
.LI168_REVERSE
.Cache
[1][tmp
[1]];
766 Out
[2] = MatShaper
->L_Precache
->Impl
.LI168_REVERSE
.Cache
[2][tmp
[2]];
770 Out
[0] = RGB_16_TO_8(cmsLinearInterpLUT16((WORD
)tmp
[0], MatShaper
-> L
[0], &MatShaper
-> p16
));
771 Out
[1] = RGB_16_TO_8(cmsLinearInterpLUT16((WORD
)tmp
[1], MatShaper
-> L
[1], &MatShaper
-> p16
));
772 Out
[2] = RGB_16_TO_8(cmsLinearInterpLUT16((WORD
)tmp
[2], MatShaper
-> L
[2], &MatShaper
-> p16
));
777 Out
[0] = RGB_16_TO_8((WORD
)tmp
[0]);
778 Out
[1] = RGB_16_TO_8((WORD
)tmp
[1]);
779 Out
[2] = RGB_16_TO_8((WORD
)tmp
[2]);
789 // Using Named color input table
792 void NC2deviceXform(_LPcmsTRANSFORM p
,
794 LPVOID out
, unsigned int Size
)
797 register LPBYTE accum
;
798 register LPBYTE output
;
799 WORD wIn
[MAXCHANNELS
], wOut
[MAXCHANNELS
];
800 register unsigned int i
;
804 output
= (LPBYTE
) out
;
806 for (i
=0; i
< Size
; i
++) {
808 accum
= p
-> FromInput(p
, wIn
, accum
);
809 CopyMemory(wOut
, p
->NamedColorList
->List
[wIn
[0]].DeviceColorant
, sizeof(WORD
) * MAXCHANNELS
);
810 output
= p
-> ToOutput(p
, wOut
, output
);
817 // --------------------------------------------------------------------------
818 // Build a LUT based on shape-matrix method.
821 // Some non-conformant gray profiles are using kTCR as L*,
822 // this function converts the curve to XYZ PCS.
825 void FromLstarToXYZ(LPGAMMATABLE g
, LPGAMMATABLE gxyz
[3])
833 // Setup interpolation across origin
834 cmsCalcL16Params(g
->nEntries
, &L16
);
837 gxyz
[0] = cmsAllocGamma(nPoints
);
838 gxyz
[1] = cmsAllocGamma(nPoints
);
839 gxyz
[2] = cmsAllocGamma(nPoints
);
841 // Transport from Lab to XYZ
843 for (i
=0; i
< nPoints
; i
++) {
845 WORD val
= _cmsQuantizeVal(i
, nPoints
);
846 WORD w
= cmsLinearInterpLUT16(val
, g
->GammaTable
, &L16
);
848 Lab
.L
= ((double) 100.0 * w
) / 65535.0;
851 cmsLab2XYZ(NULL
, &XYZ
, &Lab
);
853 // Should be same curve
854 gxyz
[0] ->GammaTable
[i
] = (WORD
) floor((65535.0 * XYZ
.X
) / D50X
+ 0.5);
855 gxyz
[1] ->GammaTable
[i
] = (WORD
) floor((65535.0 * XYZ
.Y
) / D50Y
+ 0.5);
856 gxyz
[2] ->GammaTable
[i
] = (WORD
) floor((65535.0 * XYZ
.Z
) / D50Z
+ 0.5);
860 // Monochrome version
863 LPMATSHAPER
cmsBuildGrayInputMatrixShaper(cmsHPROFILE hProfile
)
865 cmsCIEXYZ Illuminant
;
866 LPGAMMATABLE GrayTRC
, Shapes
[3];
867 LPMATSHAPER MatShaper
;
870 GrayTRC
= cmsReadICCGamma(hProfile
, icSigGrayTRCTag
); // Y
871 if (GrayTRC
== NULL
) return NULL
;
873 cmsTakeIluminant(&Illuminant
, hProfile
);
875 if (cmsGetPCS(hProfile
) == icSigLabData
) {
877 // Fixup for Lab monochrome
878 FromLstarToXYZ(GrayTRC
, Shapes
);
881 Shapes
[0] = cmsDupGamma(GrayTRC
);
882 Shapes
[1] = cmsDupGamma(GrayTRC
);
883 Shapes
[2] = cmsDupGamma(GrayTRC
);
886 if (!Shapes
[0] || !Shapes
[1] || !Shapes
[2])
889 cmsFreeGamma(GrayTRC
);
891 // R=G=B as precondition
893 VEC3init(&Scale
.v
[0], Illuminant
.X
/3, Illuminant
.X
/3, Illuminant
.X
/3);
894 VEC3init(&Scale
.v
[1], Illuminant
.Y
/3, Illuminant
.Y
/3, Illuminant
.Y
/3);
895 VEC3init(&Scale
.v
[2], Illuminant
.Z
/3, Illuminant
.Z
/3, Illuminant
.Z
/3);
898 MatShaper
= cmsAllocMatShaper(&Scale
, Shapes
, MATSHAPER_INPUT
);
899 cmsFreeGammaTriple(Shapes
);
905 // Monochrome as output
908 LPMATSHAPER
cmsBuildGrayOutputMatrixShaper(cmsHPROFILE hProfile
)
910 cmsCIEXYZ Illuminant
;
911 LPGAMMATABLE GrayTRC
, Shapes
[3];
912 LPMATSHAPER MatShaper
;
915 cmsTakeIluminant(&Illuminant
, hProfile
);
917 // That is a special case for non-compliant profiles.
919 if (cmsGetPCS(hProfile
) == icSigLabData
) {
921 LPGAMMATABLE Shapes1
[3];
923 GrayTRC
= cmsReadICCGamma(hProfile
, icSigGrayTRCTag
);
924 FromLstarToXYZ(GrayTRC
, Shapes1
);
926 // Reversing must be done after curve translation
928 Shapes
[0] = cmsReverseGamma(Shapes1
[0]->nEntries
, Shapes1
[0]);
929 Shapes
[1] = cmsReverseGamma(Shapes1
[1]->nEntries
, Shapes1
[1]);
930 Shapes
[2] = cmsReverseGamma(Shapes1
[2]->nEntries
, Shapes1
[2]);
932 cmsFreeGammaTriple(Shapes1
);
939 GrayTRC
= cmsReadICCGammaReversed(hProfile
, icSigGrayTRCTag
); // Y
941 Shapes
[0] = cmsDupGamma(GrayTRC
);
942 Shapes
[1] = cmsDupGamma(GrayTRC
);
943 Shapes
[2] = cmsDupGamma(GrayTRC
);
946 if (!Shapes
[0] || !Shapes
[1] || !Shapes
[2])
949 cmsFreeGamma(GrayTRC
);
951 VEC3init(&Scale
.v
[0], 0, 1.0/Illuminant
.Y
, 0);
952 VEC3init(&Scale
.v
[1], 0, 1.0/Illuminant
.Y
, 0);
953 VEC3init(&Scale
.v
[2], 0, 1.0/Illuminant
.Y
, 0);
956 MatShaper
= cmsAllocMatShaper(&Scale
, Shapes
, MATSHAPER_OUTPUT
);
957 cmsFreeGammaTriple(Shapes
);
964 // Input matrix, only in XYZ
966 LPMATSHAPER
cmsBuildInputMatrixShaper(cmsHPROFILE InputProfile
)
969 LPGAMMATABLE Shapes
[3];
972 // Check if this is a grayscale profile. If so, build
973 // appropiate conversion tables. The tables are the PCS
974 // iluminant, scaled across GrayTRC
976 if (cmsGetColorSpace(InputProfile
) == icSigGrayData
)
978 return cmsBuildGrayInputMatrixShaper(InputProfile
);
981 if (!cmsReadICCMatrixRGB2XYZ(&DoubleMat
, InputProfile
))
984 Shapes
[0] = cmsReadICCGamma(InputProfile
, icSigRedTRCTag
);
985 Shapes
[1] = cmsReadICCGamma(InputProfile
, icSigGreenTRCTag
);
986 Shapes
[2] = cmsReadICCGamma(InputProfile
, icSigBlueTRCTag
);
988 if (!Shapes
[0] || !Shapes
[1] || !Shapes
[2])
991 InMatSh
= cmsAllocMatShaper(&DoubleMat
, Shapes
, MATSHAPER_INPUT
);
993 cmsFreeGammaTriple(Shapes
);
999 // Output style matrix-shaper
1002 LPMATSHAPER
cmsBuildOutputMatrixShaper(cmsHPROFILE OutputProfile
)
1004 MAT3 DoubleMat
, DoubleInv
;
1005 LPGAMMATABLE InverseShapes
[3];
1006 LPMATSHAPER OutMatSh
;
1010 if (cmsGetColorSpace(OutputProfile
) == icSigGrayData
)
1012 return cmsBuildGrayOutputMatrixShaper(OutputProfile
);
1016 if (!cmsReadICCMatrixRGB2XYZ(&DoubleMat
, OutputProfile
))
1019 if (MAT3inverse(&DoubleMat
, &DoubleInv
) < 0)
1023 InverseShapes
[0] = cmsReadICCGammaReversed(OutputProfile
, icSigRedTRCTag
);
1024 InverseShapes
[1] = cmsReadICCGammaReversed(OutputProfile
, icSigGreenTRCTag
);
1025 InverseShapes
[2] = cmsReadICCGammaReversed(OutputProfile
, icSigBlueTRCTag
);
1027 if (InverseShapes
[0] &&
1030 OutMatSh
= cmsAllocMatShaper(&DoubleInv
, InverseShapes
, MATSHAPER_OUTPUT
);
1035 cmsFreeGammaTriple(InverseShapes
);
1042 // This function builds a transform matrix chaining parameters
1045 LCMSBOOL
cmsBuildSmeltMatShaper(_LPcmsTRANSFORM p
)
1047 MAT3 From
, To
, ToInv
, Transfer
;
1048 LPGAMMATABLE In
[3], InverseOut
[3];
1049 LPLCMSPRECACHE InPrecache
, OutPrecache
;
1052 if (!cmsReadICCMatrixRGB2XYZ(&From
, p
-> InputProfile
))
1056 if (!cmsReadICCMatrixRGB2XYZ(&To
, p
-> OutputProfile
))
1061 if (MAT3inverse(&To
, &ToInv
) < 0)
1065 MAT3per(&Transfer
, &ToInv
, &From
);
1067 // Check for the relevant precaches
1068 if (p
-> dwOriginalFlags
& cmsFLAGS_FLOATSHAPER
) {
1069 InPrecache
= ((LPLCMSICCPROFILE
)p
->InputProfile
)->Precache
[CMS_PRECACHE_LI8F_FORWARD
];
1070 OutPrecache
= ((LPLCMSICCPROFILE
)p
->OutputProfile
)->Precache
[CMS_PRECACHE_LI168_REVERSE
];
1073 InPrecache
= ((LPLCMSICCPROFILE
)p
->InputProfile
)->Precache
[CMS_PRECACHE_LI16W_FORWARD
];
1074 OutPrecache
= ((LPLCMSICCPROFILE
)p
->OutputProfile
)->Precache
[CMS_PRECACHE_LI1616_REVERSE
];
1077 // If the input interpolations aren't already cached, read gamma curves
1078 if (InPrecache
== NULL
) {
1079 In
[0] = cmsReadICCGamma(p
-> InputProfile
, icSigRedTRCTag
);
1080 In
[1] = cmsReadICCGamma(p
-> InputProfile
, icSigGreenTRCTag
);
1081 In
[2] = cmsReadICCGamma(p
-> InputProfile
, icSigBlueTRCTag
);
1083 if (!In
[0] || !In
[1] || !In
[2])
1087 In
[0] = In
[1] = In
[2] = NULL
;
1090 // If the output interpolations aren't already cached, read reverse
1092 if (OutPrecache
== NULL
) {
1093 InverseOut
[0] = cmsReadICCGammaReversed(p
-> OutputProfile
, icSigRedTRCTag
);
1094 InverseOut
[1] = cmsReadICCGammaReversed(p
-> OutputProfile
, icSigGreenTRCTag
);
1095 InverseOut
[2] = cmsReadICCGammaReversed(p
-> OutputProfile
, icSigBlueTRCTag
);
1097 if (!InverseOut
[0] || !InverseOut
[1] || !InverseOut
[2]) {
1098 cmsFreeGammaTriple(In
);
1103 InverseOut
[0] = InverseOut
[1] = InverseOut
[2] = NULL
;
1105 p
-> SmeltMatShaper
= cmsAllocMatShaper2(&Transfer
, In
, InPrecache
,
1106 InverseOut
, OutPrecache
,
1107 MATSHAPER_ALLSMELTED
|
1108 ((p
-> dwOriginalFlags
& cmsFLAGS_FLOATSHAPER
)
1109 ? MATSHAPER_FLOATMAT
: 0));
1111 // These don't free if the pointers were already NULL
1112 cmsFreeGammaTriple(In
);
1113 cmsFreeGammaTriple(InverseOut
);
1115 return (p
-> SmeltMatShaper
!= NULL
);
1121 // Conversion between PCS ------------------------------------------
1123 // Identifies intent archieved by LUT
1126 int GetPhase(cmsHPROFILE hProfile
)
1128 switch (cmsGetPCS(hProfile
)) {
1130 case icSigXYZData
: return XYZRel
;
1132 case icSigLabData
: return LabRel
;
1134 default: cmsSignalError(LCMS_ERRC_ABORTED
, "Invalid PCS");
1144 void TakeConversionRoutines(_LPcmsTRANSFORM p
, int DoBPC
)
1146 cmsCIEXYZ BlackPointIn
, WhitePointIn
, IlluminantIn
;
1147 cmsCIEXYZ BlackPointOut
, WhitePointOut
, IlluminantOut
;
1148 cmsCIEXYZ BlackPointProof
, WhitePointProof
, IlluminantProof
;
1149 MAT3 ChromaticAdaptationMatrixIn
, ChromaticAdaptationMatrixOut
;
1150 MAT3 ChromaticAdaptationMatrixProof
;
1153 cmsTakeIluminant(&IlluminantIn
, p
-> InputProfile
);
1154 cmsTakeMediaWhitePoint(&WhitePointIn
, p
-> InputProfile
);
1155 cmsTakeMediaBlackPoint(&BlackPointIn
, p
-> InputProfile
);
1156 cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixIn
, p
-> InputProfile
);
1158 cmsTakeIluminant(&IlluminantOut
, p
-> OutputProfile
);
1159 cmsTakeMediaWhitePoint(&WhitePointOut
, p
-> OutputProfile
);
1160 cmsTakeMediaBlackPoint(&BlackPointOut
, p
-> OutputProfile
);
1161 cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixOut
, p
-> OutputProfile
);
1164 if (p
-> Preview
== NULL
&& p
->Gamut
== NULL
) // Non-proofing
1166 if (p
->Intent
== INTENT_PERCEPTUAL
||
1167 p
->Intent
== INTENT_SATURATION
) {
1170 // For v4 profiles, Perceptual PCS has a reference black point
1171 // which v2 profiles should scale to.
1173 if ((cmsGetProfileICCversion(p
->InputProfile
) >= 0x4000000) ||
1174 (cmsGetProfileICCversion(p
->OutputProfile
) >= 0x4000000)) {
1180 // Black point compensation does not apply to absolute intent
1182 if (p
->Intent
== INTENT_ABSOLUTE_COLORIMETRIC
)
1185 // Black point compensation does not apply to devicelink profiles
1187 if (cmsGetDeviceClass(p
->InputProfile
) == icSigLinkClass
)
1190 if (cmsGetDeviceClass(p
->OutputProfile
) == icSigLinkClass
)
1195 // Detect Black points
1197 cmsDetectBlackPoint(&BlackPointIn
, p
->InputProfile
, p
->Intent
, 0);
1198 cmsDetectBlackPoint(&BlackPointOut
, p
->OutputProfile
, p
->Intent
, 0);
1200 // If equal black points, then do nothing. This often applies to BP=0
1202 if (BlackPointIn
.X
== BlackPointOut
.X
&&
1203 BlackPointIn
.Y
== BlackPointOut
.Y
&&
1204 BlackPointIn
.Z
== BlackPointOut
.Z
)
1210 cmsChooseCnvrt(p
-> Intent
== INTENT_ABSOLUTE_COLORIMETRIC
,
1216 &ChromaticAdaptationMatrixIn
,
1222 &ChromaticAdaptationMatrixOut
,
1225 p
->AdaptationState
,
1234 cmsTakeIluminant(&IlluminantProof
, p
-> PreviewProfile
);
1235 cmsTakeMediaWhitePoint(&WhitePointProof
, p
-> PreviewProfile
);
1236 cmsTakeMediaBlackPoint(&BlackPointProof
, p
-> PreviewProfile
);
1237 cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixProof
, p
-> PreviewProfile
);
1241 cmsDetectBlackPoint(&BlackPointProof
, p
->PreviewProfile
, p
->Intent
, 0);
1242 cmsDetectBlackPoint(&BlackPointIn
, p
->InputProfile
, p
->Intent
, 0);
1243 cmsDetectBlackPoint(&BlackPointOut
, p
->OutputProfile
, p
->Intent
, 0);
1247 if (BlackPointIn
.X
== BlackPointProof
.X
&&
1248 BlackPointIn
.Y
== BlackPointProof
.Y
&&
1249 BlackPointIn
.Z
== BlackPointProof
.Z
)
1257 cmsChooseCnvrt(p
-> Intent
== INTENT_ABSOLUTE_COLORIMETRIC
,
1263 &ChromaticAdaptationMatrixIn
,
1269 &ChromaticAdaptationMatrixProof
,
1271 p
->AdaptationState
,
1275 cmsChooseCnvrt(p
-> ProofIntent
== INTENT_ABSOLUTE_COLORIMETRIC
,
1281 &ChromaticAdaptationMatrixProof
,
1287 &ChromaticAdaptationMatrixOut
,
1300 LCMSBOOL
IsProperColorSpace(cmsHPROFILE hProfile
, DWORD dwFormat
, LCMSBOOL lUsePCS
)
1302 int Space
= T_COLORSPACE(dwFormat
);
1304 if (Space
== PT_ANY
) return TRUE
;
1307 return (Space
== _cmsLCMScolorSpace(cmsGetPCS(hProfile
)));
1309 return (Space
== _cmsLCMScolorSpace(cmsGetColorSpace(hProfile
)));
1313 // Auxiliary: allocate transform struct and set to defaults
1316 _LPcmsTRANSFORM
AllocEmptyTransform(void)
1318 // Allocate needed memory
1320 _LPcmsTRANSFORM p
= (_LPcmsTRANSFORM
) _cmsMalloc(sizeof(_cmsTRANSFORM
));
1323 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsCreateTransform: _cmsMalloc() failed");
1327 ZeroMemory(p
, sizeof(_cmsTRANSFORM
));
1329 // Initialize default methods
1332 p
-> Intent
= INTENT_PERCEPTUAL
;
1333 p
-> ProofIntent
= INTENT_ABSOLUTE_COLORIMETRIC
;
1334 p
-> DoGamutCheck
= FALSE
;
1335 p
-> InputProfile
= NULL
;
1336 p
-> OutputProfile
= NULL
;
1337 p
-> PreviewProfile
= NULL
;
1338 p
-> Preview
= NULL
;
1340 p
-> DeviceLink
= NULL
;
1341 p
-> InMatShaper
= NULL
;
1342 p
-> OutMatShaper
= NULL
;
1343 p
-> SmeltMatShaper
= NULL
;
1344 p
-> NamedColorList
= NULL
;
1345 p
-> EntryColorSpace
= (icColorSpaceSignature
) 0;
1346 p
-> ExitColorSpace
= (icColorSpaceSignature
) 0;
1347 p
-> AdaptationState
= GlobalAdaptationState
;
1349 LCMS_CREATE_LOCK(&p
->rwlock
);
1355 // Identify whatever a transform is to be cached
1358 void SetPrecalculatedTransform(_LPcmsTRANSFORM p
)
1360 if ((p
->dwOriginalFlags
& cmsFLAGS_GAMUTCHECK
) && p
->GamutCheck
!= NULL
) {
1362 p
-> xform
= PrecalculatedXFORMGamutCheck
;
1364 if (!(p
->dwOriginalFlags
& cmsFLAGS_NOTCACHE
)) {
1366 ZeroMemory(p
->CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
1367 TransformOnePixelWithGamutCheck(p
, p
->CacheIn
, p
->CacheOut
);
1368 p
->xform
= CachedXFORMGamutCheck
;
1374 p
-> xform
= PrecalculatedXFORM
;
1376 if (!(p
->dwOriginalFlags
& cmsFLAGS_NOTCACHE
)) {
1378 ZeroMemory(p
->CacheIn
, sizeof(WORD
) * MAXCHANNELS
);
1379 cmsEvalLUT(p
->DeviceLink
, p
->CacheIn
, p
->CacheOut
);
1380 p
->xform
= CachedXFORM
;
1386 // Transform is identified as device-link
1388 cmsHPROFILE
CreateDeviceLinkTransform(_LPcmsTRANSFORM p
)
1391 if (!IsProperColorSpace(p
->InputProfile
, p
->InputFormat
, FALSE
)) {
1392 cmsSignalError(LCMS_ERRC_ABORTED
, "Device link is operating on wrong colorspace on input");
1396 if (!IsProperColorSpace(p
->InputProfile
, p
->OutputFormat
, TRUE
)) {
1397 cmsSignalError(LCMS_ERRC_ABORTED
, "Device link is operating on wrong colorspace on output");
1401 // Device link does only have AToB0Tag (ICC-Spec 1998/09)
1403 p
->DeviceLink
= cmsReadICCLut(p
->InputProfile
, icSigAToB0Tag
);
1405 if (!p
->DeviceLink
) {
1407 cmsSignalError(LCMS_ERRC_ABORTED
, "Noncompliant device-link profile");
1408 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1412 if (p
->PreviewProfile
!= NULL
) {
1413 cmsSignalError(LCMS_ERRC_WARNING
, "Proofing not supported on device link transforms");
1416 if (p
->OutputProfile
!= NULL
) {
1417 cmsSignalError(LCMS_ERRC_WARNING
, "Output profile should be NULL, since this is a device-link transform");
1424 SetPrecalculatedTransform(p
);
1426 p
-> EntryColorSpace
= cmsGetColorSpace(p
-> InputProfile
);
1427 p
-> ExitColorSpace
= cmsGetPCS(p
-> InputProfile
);
1429 if (p
->EntryColorSpace
== icSigRgbData
||
1430 p
->EntryColorSpace
== icSigCmyData
) {
1432 p
->DeviceLink
-> CLut16params
.Interp3D
= cmsTetrahedralInterp16
;
1435 // Precalculated device-link profile is ready
1436 return (cmsHTRANSFORM
) p
;
1440 // Transform that includes proofing
1442 void CreateProof(_LPcmsTRANSFORM p
, icTagSignature
*ToTagPtr
)
1445 icTagSignature ProofTag
;
1447 if (p
-> dwOriginalFlags
& cmsFLAGS_SOFTPROOFING
) {
1449 // Apr-15, 2002 - Too much profiles does have bogus content
1450 // on preview tag, so I do compute it by my own.
1452 p
-> Preview
= _cmsComputeSoftProofLUT(p
->PreviewProfile
, p
->Intent
);
1453 p
-> Phase2
= LabRel
;
1455 // That's a proofing transfor, so use second intent for output.
1457 *ToTagPtr
= PCS2Device
[p
->ProofIntent
];
1459 if (p
-> Preview
== NULL
) {
1461 ProofTag
= Preview
[p
-> Intent
];
1463 if (!cmsIsTag(p
->PreviewProfile
, ProofTag
)) {
1465 ProofTag
= Preview
[0];
1466 if (!cmsIsTag(p
->PreviewProfile
, ProofTag
))
1467 ProofTag
= (icTagSignature
)0;
1472 p
-> Preview
= cmsReadICCLut(p
->PreviewProfile
, ProofTag
);
1473 p
-> Phase2
= GetPhase(p
->PreviewProfile
);
1478 p
-> Preview
= NULL
;
1479 p
->PreviewProfile
= NULL
;
1480 cmsSignalError(LCMS_ERRC_WARNING
, "Sorry, the proof profile has not previewing capabilities");
1487 // Aug-31, 2001 - Too much profiles does have bogus content
1488 // on gamut tag, so I do compute it by my own.
1490 if ((p
-> dwOriginalFlags
& cmsFLAGS_GAMUTCHECK
) && (p
-> dwOriginalFlags
& cmsFLAGS_NOTPRECALC
)) {
1493 p
-> Gamut
= _cmsComputeGamutLUT(p
->PreviewProfile
, p
->Intent
);
1494 p
-> Phase2
= LabRel
;
1496 if (p
-> Gamut
== NULL
) {
1498 // Profile goes only in one direction... try to see
1499 // if profile has the tag, and use it, no matter it
1500 // could be bogus. This is the last chance!
1502 if (cmsIsTag(p
->PreviewProfile
, icSigGamutTag
)) {
1504 p
-> Gamut
= cmsReadICCLut(p
->PreviewProfile
, icSigGamutTag
);
1509 // Nope, cannot be done.
1511 cmsSignalError(LCMS_ERRC_WARNING
, "Sorry, the proof profile has not gamut checking capabilities");
1520 // Choose the adequate transform routine
1523 _LPcmsTRANSFORM
PickTransformRoutine(_LPcmsTRANSFORM p
,
1524 icTagSignature
*FromTagPtr
,
1525 icTagSignature
*ToTagPtr
)
1531 // Is a named color profile?
1532 if (cmsGetDeviceClass(p
->InputProfile
) == icSigNamedColorClass
) {
1534 // Yes, and used as input
1535 p
->FromDevice
= NC2toPCS
;
1538 // Can we optimize matrix-shaper only transform?
1540 if ((*FromTagPtr
== 0) &&
1542 (!p
->PreviewProfile
) &&
1543 (p
-> Intent
!= INTENT_ABSOLUTE_COLORIMETRIC
) &&
1544 (p
-> EntryColorSpace
== icSigRgbData
) &&
1545 (p
-> ExitColorSpace
== icSigRgbData
) &&
1546 !(p
-> dwOriginalFlags
& cmsFLAGS_BLACKPOINTCOMPENSATION
)) {
1548 // We've found our type of transform - don't override it later with a precalculated transform
1549 p
-> dwOriginalFlags
|= cmsFLAGS_NOTPRECALC
;
1552 // If the input profile pointer-matches with the output profile,
1553 // optimize the transformation away into a null xform
1554 if (p
-> InputProfile
== p
-> OutputProfile
) {
1555 p
-> xform
= NullXFORM
;
1559 // If the floating point path is requested, see if we support it
1560 if (p
-> dwOriginalFlags
& cmsFLAGS_FLOATSHAPER
)
1563 #ifndef HAVE_SSE2_INTEL_MNEMONICS
1564 // Turn it off if we can't compile it
1565 p
-> dwOriginalFlags
&= ~cmsFLAGS_FLOATSHAPER
;
1567 // Turn it off if we don't have it at runtime
1568 if (!SSE2Available())
1569 p
-> dwOriginalFlags
&= ~cmsFLAGS_FLOATSHAPER
;
1573 // Yes... try to smelt matrix-shapers
1575 #ifndef HAVE_SSE2_INTEL_MNEMONICS
1576 p
-> xform
= MatrixShaperXFORM
;
1578 p
-> xform
= (p
-> dwOriginalFlags
& cmsFLAGS_FLOATSHAPER
) ? MatrixShaperXFORMFloat
: MatrixShaperXFORM
;
1581 if (!cmsBuildSmeltMatShaper(p
))
1583 cmsSignalError(LCMS_ERRC_ABORTED
, "unable to smelt shaper-matrix, required tags missing");
1587 p
-> Phase1
= p
-> Phase3
= XYZRel
;
1592 // No, is a transform involving LUT
1594 if (*FromTagPtr
!= 0) {
1596 p
-> FromDevice
= LUTtoPCS
;
1597 p
-> Device2PCS
= cmsReadICCLut(p
-> InputProfile
, *FromTagPtr
);
1598 if (!p
-> Device2PCS
) {
1600 cmsSignalError(LCMS_ERRC_ABORTED
, "profile is unsuitable for input");
1607 p
-> FromDevice
= ShaperMatrixToPCS
;
1608 p
-> InMatShaper
= cmsBuildInputMatrixShaper(p
-> InputProfile
);
1610 if (!p
->InMatShaper
) {
1611 cmsSignalError(LCMS_ERRC_ABORTED
, "profile is unsuitable for input");
1615 p
-> Phase1
= XYZRel
;
1620 if (*ToTagPtr
!= 0) {
1622 p
-> ToDevice
= PCStoLUT
;
1623 p
-> PCS2Device
= cmsReadICCLut(p
-> OutputProfile
, *ToTagPtr
);
1624 if (!p
-> PCS2Device
) {
1625 cmsSignalError(LCMS_ERRC_ABORTED
, "profile is unsuitable for output");
1632 p
-> ToDevice
= PCStoShaperMatrix
;
1633 p
-> OutMatShaper
= cmsBuildOutputMatrixShaper(p
->OutputProfile
);
1635 if (!p
-> OutMatShaper
) {
1636 cmsSignalError(LCMS_ERRC_ABORTED
, "profile is unsuitable for output");
1639 p
-> Phase3
= XYZRel
;
1650 // Create a transform.
1652 cmsHTRANSFORM LCMSEXPORT
cmsCreateProofingTransform(cmsHPROFILE InputProfile
,
1654 cmsHPROFILE OutputProfile
,
1656 cmsHPROFILE ProofingProfile
,
1663 icTagSignature FromTag
;
1664 icTagSignature ToTag
;
1666 if (nIntent
< 0 || nIntent
> 3 ||
1667 ProofingIntent
< 0 || ProofingIntent
> 3) {
1669 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsCreateTransform: intent mismatch");
1673 p
= AllocEmptyTransform();
1674 if (p
== NULL
) return NULL
;
1676 p
-> xform
= NormalXFORM
;
1677 p
-> Intent
= nIntent
;
1678 p
-> ProofIntent
= ProofingIntent
;
1679 p
-> DoGamutCheck
= FALSE
;
1680 p
-> InputProfile
= InputProfile
;
1681 p
-> OutputProfile
= OutputProfile
;
1682 p
-> PreviewProfile
= ProofingProfile
;
1683 p
-> InputFormat
= InputFormat
;
1684 p
-> OutputFormat
= OutputFormat
;
1685 p
-> dwOriginalFlags
= dwFlags
;
1687 p
-> lInputV4Lab
= p
->lOutputV4Lab
= FALSE
;
1689 p
-> FromInput
= _cmsIdentifyInputFormat(p
, InputFormat
);
1690 p
-> ToOutput
= _cmsIdentifyOutputFormat(p
, OutputFormat
);
1692 // Null transform can be done without profiles
1693 if ((p
->dwOriginalFlags
& cmsFLAGS_NULLTRANSFORM
) ||
1694 ((InputProfile
== NULL
) &&
1695 (OutputProfile
== NULL
))) {
1697 p
-> xform
= NullXFORM
;
1698 return (cmsHTRANSFORM
) p
;
1701 // From here we need at least one input profile
1702 if (InputProfile
== NULL
) {
1704 cmsSignalError(LCMS_ERRC_ABORTED
, "Input profile cannot be NULL!");
1705 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1710 // Device link are means to store precalculated transform grids.
1711 if (cmsGetDeviceClass(InputProfile
) == icSigLinkClass
) {
1713 return CreateDeviceLinkTransform(p
);
1716 if (!IsProperColorSpace(InputProfile
, InputFormat
, FALSE
)) {
1718 cmsSignalError(LCMS_ERRC_ABORTED
, "Input profile is operating on wrong colorspace");
1719 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1723 p
->EntryColorSpace
= cmsGetColorSpace(InputProfile
);
1725 // Oct-21-2002: Added named color transforms
1726 if (cmsGetDeviceClass(InputProfile
) == icSigNamedColorClass
) {
1728 if (p
->NamedColorList
== NULL
)
1729 p
->NamedColorList
= cmsAllocNamedColorList(0);
1731 cmsReadICCnamedColorList(p
, InputProfile
, icSigNamedColor2Tag
);
1733 // Special case. If output profile == NULL, then the transform gives
1734 // device values from named colors.
1736 if (OutputProfile
== NULL
) {
1738 p
->ExitColorSpace
= p
-> EntryColorSpace
;
1739 p
->xform
= NC2deviceXform
;
1740 return (cmsHTRANSFORM
) p
;
1743 // Named color doesn't precalc anything
1744 p
-> dwOriginalFlags
|= cmsFLAGS_NOTPRECALC
;
1748 // From here we need also output profile.
1749 if (OutputProfile
== NULL
) {
1750 cmsSignalError(LCMS_ERRC_ABORTED
, "Output profile cannot be NULL!");
1751 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1756 if (!IsProperColorSpace(OutputProfile
, OutputFormat
, FALSE
)) {
1757 cmsSignalError(LCMS_ERRC_ABORTED
, "Output profile is operating on wrong colorspace");
1758 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1762 p
-> ExitColorSpace
= cmsGetColorSpace(OutputProfile
);
1764 // Named color only on input
1765 if (cmsGetDeviceClass(OutputProfile
) == icSigNamedColorClass
) {
1767 cmsSignalError(LCMS_ERRC_ABORTED
, "Named color profiles are not supported as output");
1768 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1772 p
-> Phase1
= GetPhase(InputProfile
);
1774 p
-> Phase3
= GetPhase(OutputProfile
);
1776 // Try to locate a LUT
1778 FromTag
= Device2PCS
[nIntent
];
1779 ToTag
= PCS2Device
[nIntent
];
1781 if (!cmsIsTag(InputProfile
, FromTag
)) {
1783 FromTag
= Device2PCS
[0];
1785 if (!cmsIsTag(InputProfile
, FromTag
)) {
1786 FromTag
= (icTagSignature
)0;
1790 // If proofing is needed, add required tags/parameters
1791 if (ProofingProfile
)
1792 CreateProof(p
, &ToTag
);
1795 if (!cmsIsTag(OutputProfile
, ToTag
)) {
1797 ToTag
= PCS2Device
[0];
1799 // 12-Dec-2003, Abstract profiles can be placed as output and still using AToB0
1800 if (cmsGetDeviceClass(OutputProfile
) == icSigAbstractClass
) {
1802 if (!cmsIsTag(OutputProfile
, ToTag
)) {
1803 ToTag
= (icTagSignature
) icSigAToB0Tag
;
1807 if (!cmsIsTag(OutputProfile
, ToTag
))
1808 ToTag
= (icTagSignature
)0;
1812 if (p
-> dwOriginalFlags
& cmsFLAGS_MATRIXINPUT
)
1813 FromTag
= (icTagSignature
)0;
1815 if (p
-> dwOriginalFlags
& cmsFLAGS_MATRIXOUTPUT
)
1816 ToTag
= (icTagSignature
)0;
1820 if (PickTransformRoutine(p
, &FromTag
, &ToTag
) == NULL
) {
1822 cmsDeleteTransform((cmsHTRANSFORM
) p
);
1827 TakeConversionRoutines(p
, dwFlags
& cmsFLAGS_BLACKPOINTCOMPENSATION
);
1829 if (!(p
-> dwOriginalFlags
& cmsFLAGS_NOTPRECALC
)) {
1832 LPLUT GamutCheck
= NULL
;
1835 if (p
->EntryColorSpace
== icSigCmykData
&&
1836 p
->ExitColorSpace
== icSigCmykData
&&
1837 (dwFlags
& cmsFLAGS_PRESERVEBLACK
)) {
1839 DeviceLink
= _cmsPrecalculateBlackPreservingDeviceLink((cmsHTRANSFORM
) p
, dwFlags
);
1841 // Cannot be done at all?
1842 if (DeviceLink
== NULL
)
1843 DeviceLink
= _cmsPrecalculateDeviceLink((cmsHTRANSFORM
) p
, dwFlags
);
1848 DeviceLink
= _cmsPrecalculateDeviceLink((cmsHTRANSFORM
) p
, dwFlags
);
1851 // Allow to specify cmsFLAGS_GAMUTCHECK, even if no proofing profile is given
1852 if ((p
->PreviewProfile
!= NULL
) && (p
-> dwOriginalFlags
& cmsFLAGS_GAMUTCHECK
)) {
1854 GamutCheck
= _cmsPrecalculateGamutCheck((cmsHTRANSFORM
) p
);
1857 // If input colorspace is Rgb, Cmy, then use tetrahedral interpolation
1858 // for speed reasons (it only works well on spaces on Luma is diagonal, and
1859 // not if luma is in separate channel)
1860 if (p
->EntryColorSpace
== icSigRgbData
||
1861 p
->EntryColorSpace
== icSigCmyData
) {
1864 cmsCalcCLUT16ParamsEx(DeviceLink
->CLut16params
.nSamples
,
1865 DeviceLink
->CLut16params
.nInputs
,
1866 DeviceLink
->CLut16params
.nOutputs
,
1867 TRUE
, &DeviceLink
->CLut16params
);
1871 // If this is a 8-bit transform, optimize LUT further.
1873 if ((T_BYTES(InputFormat
) == 1) && (T_CHANNELS(InputFormat
) == 3)) {
1875 DeviceLink
= _cmsBlessLUT8(DeviceLink
);
1876 if (DeviceLink
== NULL
) return NULL
;
1881 p
->GamutCheck
= GamutCheck
;
1885 p
->DeviceLink
= DeviceLink
;
1887 if ((nIntent
!= INTENT_ABSOLUTE_COLORIMETRIC
) &&
1888 !(p
-> dwOriginalFlags
& cmsFLAGS_NOWHITEONWHITEFIXUP
))
1890 _cmsFixWhiteMisalignment(p
);
1896 cmsSignalError(LCMS_ERRC_ABORTED
,
1897 "Cannot precalculate %d->%d channels transform!",
1898 T_CHANNELS(InputFormat
), T_CHANNELS(OutputFormat
));
1900 cmsDeleteTransform(p
);
1905 SetPrecalculatedTransform(p
);
1910 // Re-Identify formats
1911 p
-> FromInput
= _cmsIdentifyInputFormat(p
, InputFormat
);
1912 p
-> ToOutput
= _cmsIdentifyOutputFormat(p
, OutputFormat
);
1919 // Wrapper por simpler non-proofing transforms.
1921 cmsHTRANSFORM LCMSEXPORT
cmsCreateTransform(cmsHPROFILE Input
,
1929 return cmsCreateProofingTransform(Input
, InputFormat
,
1930 Output
, OutputFormat
,
1932 Intent
, INTENT_ABSOLUTE_COLORIMETRIC
,
1937 // Profiles are *NOT* closed
1939 void LCMSEXPORT
cmsDeleteTransform(cmsHTRANSFORM hTransform
)
1941 _LPcmsTRANSFORM p
= (_LPcmsTRANSFORM
) (LPSTR
) hTransform
;
1943 if (p
-> Device2PCS
)
1944 cmsFreeLUT(p
-> Device2PCS
);
1945 if (p
-> PCS2Device
)
1946 cmsFreeLUT(p
-> PCS2Device
);
1948 cmsFreeLUT(p
-> Gamut
);
1950 cmsFreeLUT(p
-> Preview
);
1951 if (p
-> DeviceLink
)
1952 cmsFreeLUT(p
-> DeviceLink
);
1953 if (p
-> InMatShaper
)
1954 cmsFreeMatShaper(p
-> InMatShaper
);
1955 if (p
-> OutMatShaper
)
1956 cmsFreeMatShaper(p
-> OutMatShaper
);
1957 if (p
-> SmeltMatShaper
)
1958 cmsFreeMatShaper(p
-> SmeltMatShaper
);
1959 if (p
->NamedColorList
)
1960 cmsFreeNamedColorList(p
->NamedColorList
);
1961 if (p
-> GamutCheck
)
1962 cmsFreeLUT(p
-> GamutCheck
);
1964 LCMS_FREE_LOCK(&p
->rwlock
);
1966 _cmsFree((void *) p
);
1970 // Apply transform code
1971 void LCMSEXPORT
cmsDoTransform(cmsHTRANSFORM Transform
,
1973 LPVOID OutputBuffer
, unsigned int Size
)
1977 _LPcmsTRANSFORM p
= (_LPcmsTRANSFORM
) (LPSTR
) Transform
;
1979 p
-> StrideIn
= p
-> StrideOut
= Size
;
1981 p
-> xform(p
, InputBuffer
, OutputBuffer
, Size
);
1986 void LCMSEXPORT
cmsSetAlarmCodes(int r
, int g
, int b
)
1988 AlarmR
= RGB_8_TO_16(r
);
1989 AlarmG
= RGB_8_TO_16(g
);
1990 AlarmB
= RGB_8_TO_16(b
);
1993 void LCMSEXPORT
cmsGetAlarmCodes(int *r
, int *g
, int *b
)
1995 *r
= RGB_16_TO_8(AlarmR
);
1996 *g
= RGB_16_TO_8(AlarmG
);
1997 *b
= RGB_16_TO_8(AlarmB
);
2000 // Returns TRUE if the profile is implemented as matrix-shaper
2002 LCMSBOOL LCMSEXPORT
_cmsIsMatrixShaper(cmsHPROFILE hProfile
)
2004 switch (cmsGetColorSpace(hProfile
)) {
2008 return cmsIsTag(hProfile
, icSigGrayTRCTag
);
2012 return (cmsIsTag(hProfile
, icSigRedColorantTag
) &&
2013 cmsIsTag(hProfile
, icSigGreenColorantTag
) &&
2014 cmsIsTag(hProfile
, icSigBlueColorantTag
) &&
2015 cmsIsTag(hProfile
, icSigRedTRCTag
) &&
2016 cmsIsTag(hProfile
, icSigGreenTRCTag
) &&
2017 cmsIsTag(hProfile
, icSigBlueTRCTag
));
2026 LCMSBOOL LCMSEXPORT
cmsIsIntentSupported(cmsHPROFILE hProfile
,
2027 int Intent
, int UsedDirection
)
2030 icTagSignature
* TagTable
;
2032 // Device link profiles only implements the intent in header
2034 if (cmsGetDeviceClass(hProfile
) != icSigLinkClass
) {
2036 switch (UsedDirection
) {
2038 case LCMS_USED_AS_INPUT
: TagTable
= Device2PCS
; break;
2039 case LCMS_USED_AS_OUTPUT
:TagTable
= PCS2Device
; break;
2040 case LCMS_USED_AS_PROOF
: TagTable
= Preview
; break;
2043 cmsSignalError(LCMS_ERRC_ABORTED
, "Unexpected direction (%d)", UsedDirection
);
2047 if (cmsIsTag(hProfile
, TagTable
[Intent
])) return TRUE
;
2048 return _cmsIsMatrixShaper(hProfile
);
2051 return (cmsTakeRenderingIntent(hProfile
) == Intent
);
2054 // Multiple profile transform.
2056 int MultiprofileSampler(register WORD In
[], register WORD Out
[], register LPVOID Cargo
)
2058 cmsHTRANSFORM
* Transforms
= (cmsHTRANSFORM
*) Cargo
;
2061 cmsDoTransform(Transforms
[0], In
, Out
, 1);
2063 for (i
=1; Transforms
[i
]; i
++)
2064 cmsDoTransform(Transforms
[i
], Out
, Out
, 1);
2073 int IsAllowedInSingleXform(icProfileClassSignature aClass
)
2075 return (aClass
== icSigInputClass
) ||
2076 (aClass
== icSigDisplayClass
) ||
2077 (aClass
== icSigOutputClass
) ||
2078 (aClass
== icSigColorSpaceClass
);
2082 // A multiprofile transform does chain several profiles into a single
2083 // devicelink. It couls also be used to merge named color profiles into
2084 // a single database.
2087 cmsHTRANSFORM LCMSEXPORT
cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles
[],
2094 cmsHTRANSFORM Transforms
[257];
2095 DWORD dwPrecalcFlags
= (dwFlags
|cmsFLAGS_NOTPRECALC
|cmsFLAGS_NOTCACHE
);
2096 DWORD FormatInput
, FormatOutput
;
2097 cmsHPROFILE hLab
, hXYZ
, hProfile
;
2098 icColorSpaceSignature ColorSpace
, CurrentColorSpace
;
2099 icColorSpaceSignature ColorSpaceIn
, ColorSpaceOut
;
2101 int nGridPoints
, ChannelsInput
, ChannelsOutput
= 3, i
;
2105 if (nProfiles
> 255) {
2106 cmsSignalError(LCMS_ERRC_ABORTED
, "What are you trying to do with more that 255 profiles?!?, of course aborted");
2110 // There is a simple case with just two profiles, try to catch it in order of getting
2111 // black preservation to work on this function, at least with two profiles.
2114 if (nProfiles
== 2) {
2116 icProfileClassSignature Class1
= cmsGetDeviceClass(hProfiles
[0]);
2117 icProfileClassSignature Class2
= cmsGetDeviceClass(hProfiles
[1]);
2119 // Only input, output and display are allowed
2121 if (IsAllowedInSingleXform(Class1
) &&
2122 IsAllowedInSingleXform(Class2
))
2123 return cmsCreateTransform(hProfiles
[0], dwInput
, hProfiles
[1], dwOutput
, Intent
, dwFlags
);
2127 // Creates a phantom transform for latter filling
2128 p
= (_LPcmsTRANSFORM
) cmsCreateTransform(NULL
, dwInput
,
2129 NULL
, dwOutput
, Intent
, cmsFLAGS_NULLTRANSFORM
);
2131 // If user wants null one, give it
2132 if (dwFlags
& cmsFLAGS_NULLTRANSFORM
) return (cmsHPROFILE
) p
;
2134 // Is a bunch of named color profiles?
2136 for (i
=0; i
< nProfiles
; i
++) {
2137 if (cmsGetDeviceClass(hProfiles
[i
]) == icSigNamedColorClass
)
2142 if (nNamedColor
== nProfiles
) {
2144 // Yes, only named color. Create a named color-device
2145 // and append to named color table
2147 cmsDeleteTransform((cmsHTRANSFORM
) p
);
2149 p
= (_LPcmsTRANSFORM
) cmsCreateTransform(hProfiles
[0], dwInput
, NULL
, dwOutput
, Intent
, dwFlags
);
2150 for (i
=1; i
< nProfiles
; i
++) {
2151 cmsReadICCnamedColorList(p
, hProfiles
[i
], icSigNamedColor2Tag
);
2154 return p
; // Ok, done so far
2157 if (nNamedColor
> 0) {
2159 cmsDeleteTransform((cmsHTRANSFORM
) p
);
2160 cmsSignalError(LCMS_ERRC_ABORTED
, "Could not mix named color profiles with other types in multiprofile transform");
2165 // We will need a 3DCLUT for device link
2166 Grid
= cmsAllocLUT();
2167 if (!Grid
) return NULL
;
2169 // This one is our PCS (Always Lab)
2170 hLab
= cmsCreateLabProfile(NULL
);
2171 hXYZ
= cmsCreateXYZProfile();
2173 if (!hLab
|| !hXYZ
) goto ErrorCleanup
;
2175 // Take some info....
2177 p
->EntryColorSpace
= CurrentColorSpace
= cmsGetColorSpace(hProfiles
[0]);
2180 for (i
=0; i
< nProfiles
; i
++) {
2182 int lIsDeviceLink
, lIsInput
;
2186 hProfile
= hProfiles
[i
];
2187 lIsDeviceLink
= (cmsGetDeviceClass(hProfile
) == icSigLinkClass
);
2188 lIsInput
= (CurrentColorSpace
!= icSigXYZData
) &&
2189 (CurrentColorSpace
!= icSigLabData
);
2193 ColorSpaceIn
= cmsGetColorSpace(hProfile
);
2194 ColorSpaceOut
= cmsGetPCS(hProfile
);
2199 ColorSpaceIn
= cmsGetPCS(hProfile
);
2200 ColorSpaceOut
= cmsGetColorSpace(hProfile
);
2203 ChannelsInput
= _cmsChannelsOf(ColorSpaceIn
);
2204 ChannelsOutput
= _cmsChannelsOf(ColorSpaceOut
);
2206 FormatInput
= BYTES_SH(2)|CHANNELS_SH(ChannelsInput
);
2207 FormatOutput
= BYTES_SH(2)|CHANNELS_SH(ChannelsOutput
);
2209 ColorSpace
= ColorSpaceIn
;
2212 if (ColorSpace
== CurrentColorSpace
) {
2214 if (lIsDeviceLink
) {
2216 Transforms
[i
] = cmsCreateTransform(hProfile
, FormatInput
,
2218 Intent
, dwPrecalcFlags
);
2225 Transforms
[i
] = cmsCreateTransform(hProfile
, FormatInput
,
2226 (ColorSpaceOut
== icSigLabData
? hLab
: hXYZ
), FormatOutput
,
2227 Intent
, dwPrecalcFlags
);
2230 Transforms
[i
] = cmsCreateTransform((ColorSpaceIn
== icSigLabData
? hLab
: hXYZ
), FormatInput
,
2231 hProfile
, FormatOutput
,
2232 Intent
, dwPrecalcFlags
);
2239 else // Can come from pcs?
2240 if (CurrentColorSpace
== icSigXYZData
) {
2242 Transforms
[i
] = cmsCreateTransform(hXYZ
, FormatInput
,
2243 hProfile
, FormatOutput
,
2244 Intent
, dwPrecalcFlags
);
2248 if (CurrentColorSpace
== icSigLabData
) {
2250 Transforms
[i
] = cmsCreateTransform(hLab
, FormatInput
,
2251 hProfile
, FormatOutput
,
2252 Intent
, dwPrecalcFlags
);
2256 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsCreateMultiprofileTransform: ColorSpace mismatch");
2260 CurrentColorSpace
= ColorSpaceOut
;
2264 p
->ExitColorSpace
= CurrentColorSpace
;
2265 Transforms
[i
] = NULL
; // End marker
2267 p
->InputProfile
= hProfiles
[0];
2268 p
->OutputProfile
= hProfiles
[nProfiles
- 1];
2270 nGridPoints
= _cmsReasonableGridpointsByColorspace(p
->EntryColorSpace
, dwFlags
);
2272 ChannelsInput
= _cmsChannelsOf(cmsGetColorSpace(p
->InputProfile
));
2274 Grid
= cmsAlloc3DGrid(Grid
, nGridPoints
, ChannelsInput
, ChannelsOutput
);
2276 if (!(dwFlags
& cmsFLAGS_NOPRELINEARIZATION
))
2277 _cmsComputePrelinearizationTablesFromXFORM(Transforms
, nProfiles
, Grid
);
2279 // Compute device link on 16-bit basis
2280 if (!cmsSample3DGrid(Grid
, MultiprofileSampler
, (LPVOID
) Transforms
, Grid
-> wFlags
)) {
2286 // All ok, store the newly created LUT
2287 p
-> DeviceLink
= Grid
;
2289 SetPrecalculatedTransform(p
);
2291 for (i
=nProfiles
-1; i
>= 0; --i
)
2292 cmsDeleteTransform(Transforms
[i
]);
2295 if (hLab
) cmsCloseProfile(hLab
);
2296 if (hXYZ
) cmsCloseProfile(hXYZ
);
2299 if (p
->EntryColorSpace
== icSigRgbData
||
2300 p
->EntryColorSpace
== icSigCmyData
) {
2302 p
->DeviceLink
-> CLut16params
.Interp3D
= cmsTetrahedralInterp16
;
2306 if ((Intent
!= INTENT_ABSOLUTE_COLORIMETRIC
) &&
2307 !(dwFlags
& cmsFLAGS_NOWHITEONWHITEFIXUP
))
2308 _cmsFixWhiteMisalignment(p
);
2310 return (cmsHTRANSFORM
) p
;
2315 if (hLab
) cmsCloseProfile(hLab
);
2316 if (hXYZ
) cmsCloseProfile(hXYZ
);
2322 double LCMSEXPORT
cmsSetAdaptationState(double d
)
2324 double OldVal
= GlobalAdaptationState
;
2327 GlobalAdaptationState
= d
;