Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / modules / lcms / src / cmsxform.c
blob4399aa0f02c27136802af4d732094dc002dcbb79
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 // Determine if we can build with SSE2 (this was partly copied from jmorecfg.h in
62 // mozilla/jpeg)
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) {
71 DWORD a_, b_, c_, d_;
73 ASM {
74 xchg ebx, esi
75 mov eax, fxn
76 cpuid
77 mov a_, eax
78 mov b_, ebx
79 mov c_, ecx
80 mov d_, edx
81 xchg ebx, esi
83 *a = a_;
84 *b = b_;
85 *c = c_;
86 *d = 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
94 #endif
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));
105 *a = a_;
106 *b = b_;
107 *c = c_;
108 *d = d_;
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 */
118 // Alarm RGB codes
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,
138 icSigPreview1Tag,
139 icSigPreview2Tag,
140 icSigPreview1Tag };
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;
152 DWORD a, b, c, d;
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
159 isAvailable = 0;
160 #else
161 /* We have CPUID macros defined if we have sse2 mnemonics. */
162 LCMSCPUID(function, &a, &b, &c, &d);
163 if (d & SSE2_EDX_MASK)
164 isAvailable = 1;
165 else
166 isAvailable = 0;
167 #endif
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
181 static
182 void ShaperMatrixToPCS(struct _cmstransform_struct *p,
183 WORD In[3], WORD Out[3])
185 cmsEvalMatShaper(p -> InMatShaper, In, Out);
188 // From LUT to PCS
190 static
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
199 static
200 void NC2toPCS(struct _cmstransform_struct *p,
201 WORD In[], WORD Out[3])
203 int index = In[0];
205 if (index >= p ->NamedColorList-> nColors)
206 cmsSignalError(LCMS_ERRC_WARNING, "Color %d out of range", index);
207 else
208 CopyMemory(Out, p ->NamedColorList->List[index].PCS, 3 * sizeof(WORD));
211 // From PCS to Shaper-Matrix
213 static
214 void PCStoShaperMatrix(struct _cmstransform_struct *p,
215 WORD In[3], WORD Out[3])
217 cmsEvalMatShaper(p -> OutMatShaper, In, Out);
220 // From PCS to LUT
222 static
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
242 static
243 void NullXFORM(_LPcmsTRANSFORM p,
244 LPVOID in,
245 LPVOID out, unsigned int Size)
247 register LPBYTE accum;
248 register LPBYTE output;
249 WORD wIn[MAXCHANNELS];
250 register unsigned int i, n;
253 accum = (LPBYTE) in;
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
262 if (in != out) {
264 // Copy in a nondestructive manner in case
265 // the buffers overlap for some reason
266 memmove(out, in,
267 Size * T_BYTES(p -> InputFormat) * (T_CHANNELS(p -> InputFormat) + T_EXTRA(p -> InputFormat)));
270 return;
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
284 static
285 void NormalXFORM(_LPcmsTRANSFORM p,
286 LPVOID in,
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];
293 WORD wGamut[1];
294 register unsigned int i, n;
298 accum = (LPBYTE) in;
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);
309 if (p -> Stage1) {
311 p -> Stage1(wStageABC, wPCS, &p->m1, &p->of1);
313 if (wPCS[0] == 0xFFFF &&
314 wPCS[1] == 0xFFFF &&
315 wPCS[2] == 0xFFFF) {
317 // White cutoff
319 output = p -> ToOutput((_LPcmsTRANSFORM) p,
320 _cmsWhiteBySpace(cmsGetColorSpace(p -> OutputProfile)),
321 output);
322 continue;
325 else
326 COPY_3CHANS(wPCS, wStageABC);
329 if (p->Gamut) {
331 // Gamut check, enabled across CLUT
333 cmsEvalLUT(p -> Gamut, wPCS, wGamut);
335 if (wGamut[0] >= 1) {
337 wOut[0] = AlarmR; // Gamut alarm
338 wOut[1] = AlarmG;
339 wOut[2] = AlarmB;
340 wOut[3] = 0;
342 output = p -> ToOutput((_LPcmsTRANSFORM)p, wOut, output);
343 continue;
347 if (p -> Preview)
349 WORD wPreview[3]; // PCS
351 cmsEvalLUT(p -> Preview, wPCS, wPreview);
352 COPY_3CHANS(wPCS, wPreview);
355 if (p -> Stage2) {
357 p -> Stage2(wPCS, wStageLMN, &p->m2, &p->of2);
359 if (wPCS[0] == 0xFFFF &&
360 wPCS[1] == 0xFFFF &&
361 wPCS[2] == 0xFFFF) {
363 // White cutoff
365 output = p -> ToOutput((_LPcmsTRANSFORM)p,
366 _cmsWhiteBySpace(cmsGetColorSpace(p -> OutputProfile)),
367 output);
369 continue;
373 else
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
386 static
387 void PrecalculatedXFORM(_LPcmsTRANSFORM p,
388 LPVOID in,
389 LPVOID out, unsigned int Size)
391 register LPBYTE accum;
392 register LPBYTE output;
393 WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
394 unsigned int i, n;
397 accum = (LPBYTE) in;
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,
411 p ->DeviceLink -> T,
412 &p ->DeviceLink -> CLut16params);
414 else
415 cmsEvalLUT(p -> DeviceLink, wIn, wOut);
418 output = p -> ToOutput(p, wOut, output);
422 // Auxiliar: Handle precalculated gamut check
424 static
425 void TransformOnePixelWithGamutCheck(_LPcmsTRANSFORM p, WORD wIn[], WORD wOut[])
427 WORD wOutOfGamut;
429 cmsEvalLUT(p ->GamutCheck, wIn, &wOutOfGamut);
431 if (wOutOfGamut >= 1) {
433 ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS);
435 wOut[0] = AlarmR;
436 wOut[1] = AlarmG;
437 wOut[2] = AlarmB;
440 else
441 cmsEvalLUT(p -> DeviceLink, wIn, wOut);
446 static
447 void PrecalculatedXFORMGamutCheck(_LPcmsTRANSFORM p,
448 LPVOID in,
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;
457 accum = (LPBYTE) in;
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
475 static
476 void CachedXFORM(_LPcmsTRANSFORM p,
477 LPVOID in,
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];
487 accum = (LPBYTE) in;
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);
511 else {
513 // Try to speedup things on plain devicelinks
515 if (p ->DeviceLink ->wFlags == LUT_HAS3DGRID) {
517 p ->DeviceLink ->CLut16params.Interp3D(wIn, wOut,
518 p ->DeviceLink -> T,
519 &p ->DeviceLink -> CLut16params);
521 else
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
544 static
545 void CachedXFORMGamutCheck(_LPcmsTRANSFORM p,
546 LPVOID in,
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];
556 accum = (LPBYTE) in;
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);
579 else {
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
599 static
600 void MatrixShaperXFORM(_LPcmsTRANSFORM p,
601 LPVOID in,
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;
610 accum = (LPBYTE) in;
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
626 static
627 void MatrixShaperXFORMFloat(_LPcmsTRANSFORM p,
628 LPVOID in,
629 LPVOID out, unsigned int Size)
631 register LPBYTE In, Out;
632 register unsigned int i;
633 LPMATSHAPER MatShaper;
634 unsigned Increment;
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;
641 In = (LPBYTE) in;
642 Out = (LPBYTE) out;
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]];
661 else
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));
668 else
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)
677 #ifdef __GNUC__
678 __asm(
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)
713 : "memory"
715 #else
716 ASM {
717 mov eax, MatPtr
718 mov ecx, clampMax
719 mov edx, floatScaleAddr
721 movaps xmm1, [eax]
722 movaps xmm2, [eax + 16]
723 movaps xmm3, [eax + 32]
724 movaps xmm0, [eax + 48]
726 movaps xmm4, xmm0
727 shufps xmm4, xmm4, 0
728 mulps xmm1, xmm4
729 movaps xmm5, xmm0
730 shufps xmm5, xmm5, 0x55
731 mulps xmm2, xmm5
732 movaps xmm6, xmm0
733 shufps xmm6, xmm6, 0xAA
734 mulps xmm3, xmm6
736 addps xmm2, xmm3
737 addps xmm1, xmm2
739 movss xmm7, [ecx]
740 shufps xmm7, xmm7, 0
741 minps xmm1, xmm7
742 xorps xmm6, xmm6
743 maxps xmm1, xmm6
744 movss xmm5, [edx]
745 shufps xmm5, xmm5, 0
746 mulps xmm1, xmm5
747 cvtps2dq xmm1, xmm1
748 movdqa [eax + 48], xmm1
750 #endif
753 else
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]];
768 else
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));
775 else
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]);
782 In += Increment;
783 Out += Increment;
786 #endif
789 // Using Named color input table
791 static
792 void NC2deviceXform(_LPcmsTRANSFORM p,
793 LPVOID in,
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;
803 accum = (LPBYTE) in;
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.
824 static
825 void FromLstarToXYZ(LPGAMMATABLE g, LPGAMMATABLE gxyz[3])
827 int i;
828 int nPoints = 4096;
829 cmsCIELab Lab;
830 cmsCIEXYZ XYZ;
831 L16PARAMS L16;
833 // Setup interpolation across origin
834 cmsCalcL16Params(g ->nEntries, &L16);
836 // Allocate curves
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;
849 Lab.a = Lab.b = 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
862 static
863 LPMATSHAPER cmsBuildGrayInputMatrixShaper(cmsHPROFILE hProfile)
865 cmsCIEXYZ Illuminant;
866 LPGAMMATABLE GrayTRC, Shapes[3];
867 LPMATSHAPER MatShaper;
868 MAT3 Scale;
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);
880 else {
881 Shapes[0] = cmsDupGamma(GrayTRC);
882 Shapes[1] = cmsDupGamma(GrayTRC);
883 Shapes[2] = cmsDupGamma(GrayTRC);
886 if (!Shapes[0] || !Shapes[1] || !Shapes[2])
887 return NULL;
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);
900 return MatShaper;
905 // Monochrome as output
907 static
908 LPMATSHAPER cmsBuildGrayOutputMatrixShaper(cmsHPROFILE hProfile)
910 cmsCIEXYZ Illuminant;
911 LPGAMMATABLE GrayTRC, Shapes[3];
912 LPMATSHAPER MatShaper;
913 MAT3 Scale;
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);
935 else {
937 // Normal case
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])
947 return NULL;
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);
958 return MatShaper;
964 // Input matrix, only in XYZ
966 LPMATSHAPER cmsBuildInputMatrixShaper(cmsHPROFILE InputProfile)
968 MAT3 DoubleMat;
969 LPGAMMATABLE Shapes[3];
970 LPMATSHAPER InMatSh;
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))
982 return NULL;
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])
989 return NULL;
991 InMatSh = cmsAllocMatShaper(&DoubleMat, Shapes, MATSHAPER_INPUT);
993 cmsFreeGammaTriple(Shapes);
995 return InMatSh;
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))
1017 return NULL;
1019 if (MAT3inverse(&DoubleMat, &DoubleInv) < 0)
1020 return NULL;
1023 InverseShapes[0] = cmsReadICCGammaReversed(OutputProfile, icSigRedTRCTag);
1024 InverseShapes[1] = cmsReadICCGammaReversed(OutputProfile, icSigGreenTRCTag);
1025 InverseShapes[2] = cmsReadICCGammaReversed(OutputProfile, icSigBlueTRCTag);
1027 if (InverseShapes[0] &&
1028 InverseShapes[1] &&
1029 InverseShapes[2]) {
1030 OutMatSh = cmsAllocMatShaper(&DoubleInv, InverseShapes, MATSHAPER_OUTPUT);
1031 } else {
1032 OutMatSh = NULL;
1035 cmsFreeGammaTriple(InverseShapes);
1037 return OutMatSh;
1042 // This function builds a transform matrix chaining parameters
1044 static
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))
1053 return FALSE;
1056 if (!cmsReadICCMatrixRGB2XYZ(&To, p -> OutputProfile))
1057 return FALSE;
1059 // invert dest
1061 if (MAT3inverse(&To, &ToInv) < 0)
1062 return FALSE;
1064 // Multiply
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];
1072 else {
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])
1084 return FALSE;
1086 else
1087 In[0] = In[1] = In[2] = NULL;
1090 // If the output interpolations aren't already cached, read reverse
1091 // gamma curves
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);
1099 return FALSE;
1102 else
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
1125 static
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");
1137 return XYZRel;
1143 static
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)) {
1176 DoBPC = TRUE;
1180 // Black point compensation does not apply to absolute intent
1182 if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1183 DoBPC = FALSE;
1185 // Black point compensation does not apply to devicelink profiles
1187 if (cmsGetDeviceClass(p ->InputProfile) == icSigLinkClass)
1188 DoBPC = FALSE;
1190 if (cmsGetDeviceClass(p ->OutputProfile) == icSigLinkClass)
1191 DoBPC = FALSE;
1193 if (DoBPC) {
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)
1205 DoBPC = FALSE;
1210 cmsChooseCnvrt(p -> Intent == INTENT_ABSOLUTE_COLORIMETRIC,
1212 p -> Phase1,
1213 &BlackPointIn,
1214 &WhitePointIn,
1215 &IlluminantIn,
1216 &ChromaticAdaptationMatrixIn,
1218 p -> Phase3,
1219 &BlackPointOut,
1220 &WhitePointOut,
1221 &IlluminantOut,
1222 &ChromaticAdaptationMatrixOut,
1224 DoBPC,
1225 p ->AdaptationState,
1226 &p->Stage1,
1227 &p->m1, &p->of1);
1230 else // Proofing
1234 cmsTakeIluminant(&IlluminantProof, p -> PreviewProfile);
1235 cmsTakeMediaWhitePoint(&WhitePointProof, p -> PreviewProfile);
1236 cmsTakeMediaBlackPoint(&BlackPointProof, p -> PreviewProfile);
1237 cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixProof, p -> PreviewProfile);
1239 if (DoBPC) {
1241 cmsDetectBlackPoint(&BlackPointProof, p->PreviewProfile, p->Intent, 0);
1242 cmsDetectBlackPoint(&BlackPointIn, p->InputProfile, p->Intent, 0);
1243 cmsDetectBlackPoint(&BlackPointOut, p->OutputProfile, p->Intent, 0);
1245 // Reality check
1247 if (BlackPointIn.X == BlackPointProof.X &&
1248 BlackPointIn.Y == BlackPointProof.Y &&
1249 BlackPointIn.Z == BlackPointProof.Z)
1250 DoBPC = FALSE;
1257 cmsChooseCnvrt(p -> Intent == INTENT_ABSOLUTE_COLORIMETRIC,
1259 p -> Phase1,
1260 &BlackPointIn,
1261 &WhitePointIn,
1262 &IlluminantIn,
1263 &ChromaticAdaptationMatrixIn,
1265 p -> Phase2,
1266 &BlackPointProof,
1267 &WhitePointProof,
1268 &IlluminantProof,
1269 &ChromaticAdaptationMatrixProof,
1270 DoBPC,
1271 p ->AdaptationState,
1272 &p->Stage1,
1273 &p->m1, &p->of1);
1275 cmsChooseCnvrt(p -> ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC,
1277 p -> Phase2,
1278 &BlackPointProof,
1279 &WhitePointProof,
1280 &IlluminantProof,
1281 &ChromaticAdaptationMatrixProof,
1283 p -> Phase3,
1284 &BlackPointOut,
1285 &WhitePointOut,
1286 &IlluminantOut,
1287 &ChromaticAdaptationMatrixOut,
1289 0.0,
1290 &p->Stage2,
1291 &p->m2, &p->of2);
1297 // Check colorspace
1299 static
1300 LCMSBOOL IsProperColorSpace(cmsHPROFILE hProfile, DWORD dwFormat, LCMSBOOL lUsePCS)
1302 int Space = T_COLORSPACE(dwFormat);
1304 if (Space == PT_ANY) return TRUE;
1306 if (lUsePCS)
1307 return (Space == _cmsLCMScolorSpace(cmsGetPCS(hProfile)));
1308 else
1309 return (Space == _cmsLCMScolorSpace(cmsGetColorSpace(hProfile)));
1313 // Auxiliary: allocate transform struct and set to defaults
1315 static
1316 _LPcmsTRANSFORM AllocEmptyTransform(void)
1318 // Allocate needed memory
1320 _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) _cmsMalloc(sizeof(_cmsTRANSFORM));
1321 if (!p) {
1323 cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateTransform: _cmsMalloc() failed");
1324 return NULL;
1327 ZeroMemory(p, sizeof(_cmsTRANSFORM));
1329 // Initialize default methods
1331 p -> xform = NULL;
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;
1339 p -> Gamut = 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);
1351 return p;
1355 // Identify whatever a transform is to be cached
1357 static
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;
1372 else {
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
1387 static
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");
1393 return NULL;
1396 if (!IsProperColorSpace(p->InputProfile, p->OutputFormat, TRUE)) {
1397 cmsSignalError(LCMS_ERRC_ABORTED, "Device link is operating on wrong colorspace on output");
1398 return NULL;
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);
1409 return NULL;
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");
1420 p -> Phase1 = -1;
1421 p -> Phase2 = -1;
1422 p -> Phase3 = -1;
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
1441 static
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;
1470 if (ProofTag) {
1472 p -> Preview = cmsReadICCLut(p ->PreviewProfile, ProofTag);
1473 p -> Phase2 = GetPhase(p ->PreviewProfile);
1476 else
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);
1507 else {
1509 // Nope, cannot be done.
1511 cmsSignalError(LCMS_ERRC_WARNING, "Sorry, the proof profile has not gamut checking capabilities");
1512 p -> Gamut = NULL;
1520 // Choose the adequate transform routine
1522 static
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;
1537 else {
1538 // Can we optimize matrix-shaper only transform?
1540 if ((*FromTagPtr == 0) &&
1541 (*ToTagPtr == 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;
1556 return p;
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;
1566 #else
1567 // Turn it off if we don't have it at runtime
1568 if (!SSE2Available())
1569 p -> dwOriginalFlags &= ~cmsFLAGS_FLOATSHAPER;
1570 #endif
1573 // Yes... try to smelt matrix-shapers
1575 #ifndef HAVE_SSE2_INTEL_MNEMONICS
1576 p -> xform = MatrixShaperXFORM;
1577 #else
1578 p -> xform = (p -> dwOriginalFlags & cmsFLAGS_FLOATSHAPER) ? MatrixShaperXFORMFloat : MatrixShaperXFORM;
1579 #endif
1581 if (!cmsBuildSmeltMatShaper(p))
1583 cmsSignalError(LCMS_ERRC_ABORTED, "unable to smelt shaper-matrix, required tags missing");
1584 return NULL;
1587 p -> Phase1 = p -> Phase3 = XYZRel;
1588 return p;
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");
1601 return NULL;
1605 else
1607 p -> FromDevice = ShaperMatrixToPCS;
1608 p -> InMatShaper = cmsBuildInputMatrixShaper(p -> InputProfile);
1610 if (!p ->InMatShaper) {
1611 cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for input");
1612 return NULL;
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");
1626 return NULL;
1630 else
1632 p -> ToDevice = PCStoShaperMatrix;
1633 p -> OutMatShaper = cmsBuildOutputMatrixShaper(p->OutputProfile);
1635 if (!p -> OutMatShaper) {
1636 cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for output");
1637 return NULL;
1639 p -> Phase3 = XYZRel;
1644 return p;
1650 // Create a transform.
1652 cmsHTRANSFORM LCMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1653 DWORD InputFormat,
1654 cmsHPROFILE OutputProfile,
1655 DWORD OutputFormat,
1656 cmsHPROFILE ProofingProfile,
1657 int nIntent,
1658 int ProofingIntent,
1659 DWORD dwFlags)
1662 _LPcmsTRANSFORM p;
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");
1670 return NULL;
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);
1706 return NULL;
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);
1720 return NULL;
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);
1752 return NULL;
1756 if (!IsProperColorSpace(OutputProfile, OutputFormat, FALSE)) {
1757 cmsSignalError(LCMS_ERRC_ABORTED, "Output profile is operating on wrong colorspace");
1758 cmsDeleteTransform((cmsHTRANSFORM) p);
1759 return NULL;
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);
1769 return NULL;
1772 p -> Phase1 = GetPhase(InputProfile);
1773 p -> Phase2 = -1;
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);
1823 return NULL;
1827 TakeConversionRoutines(p, dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1829 if (!(p -> dwOriginalFlags & cmsFLAGS_NOTPRECALC)) {
1831 LPLUT DeviceLink;
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);
1846 else {
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;
1883 if (DeviceLink) {
1885 p ->DeviceLink = DeviceLink;
1887 if ((nIntent != INTENT_ABSOLUTE_COLORIMETRIC) &&
1888 !(p -> dwOriginalFlags & cmsFLAGS_NOWHITEONWHITEFIXUP))
1890 _cmsFixWhiteMisalignment(p);
1893 else
1896 cmsSignalError(LCMS_ERRC_ABORTED,
1897 "Cannot precalculate %d->%d channels transform!",
1898 T_CHANNELS(InputFormat), T_CHANNELS(OutputFormat));
1900 cmsDeleteTransform(p);
1901 return NULL;
1905 SetPrecalculatedTransform(p);
1910 // Re-Identify formats
1911 p -> FromInput = _cmsIdentifyInputFormat(p, InputFormat);
1912 p -> ToOutput = _cmsIdentifyOutputFormat(p, OutputFormat);
1915 return p;
1919 // Wrapper por simpler non-proofing transforms.
1921 cmsHTRANSFORM LCMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1922 DWORD InputFormat,
1923 cmsHPROFILE Output,
1924 DWORD OutputFormat,
1925 int Intent,
1926 DWORD dwFlags)
1929 return cmsCreateProofingTransform(Input, InputFormat,
1930 Output, OutputFormat,
1931 NULL,
1932 Intent, INTENT_ABSOLUTE_COLORIMETRIC,
1933 dwFlags);
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);
1947 if (p -> Gamut)
1948 cmsFreeLUT(p -> Gamut);
1949 if (p -> Preview)
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,
1972 LPVOID InputBuffer,
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)) {
2006 case icSigGrayData:
2008 return cmsIsTag(hProfile, icSigGrayTRCTag);
2010 case icSigRgbData:
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));
2019 default:
2021 return FALSE;
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;
2042 default:
2043 cmsSignalError(LCMS_ERRC_ABORTED, "Unexpected direction (%d)", UsedDirection);
2044 return FALSE;
2047 if (cmsIsTag(hProfile, TagTable[Intent])) return TRUE;
2048 return _cmsIsMatrixShaper(hProfile);
2051 return (cmsTakeRenderingIntent(hProfile) == Intent);
2054 // Multiple profile transform.
2055 static
2056 int MultiprofileSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
2058 cmsHTRANSFORM* Transforms = (cmsHTRANSFORM*) Cargo;
2059 int i;
2061 cmsDoTransform(Transforms[0], In, Out, 1);
2063 for (i=1; Transforms[i]; i++)
2064 cmsDoTransform(Transforms[i], Out, Out, 1);
2068 return TRUE;
2072 static
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[],
2088 int nProfiles,
2089 DWORD dwInput,
2090 DWORD dwOutput,
2091 int Intent,
2092 DWORD dwFlags)
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;
2100 LPLUT Grid;
2101 int nGridPoints, ChannelsInput, ChannelsOutput = 3, i;
2102 _LPcmsTRANSFORM p;
2103 int nNamedColor;
2105 if (nProfiles > 255) {
2106 cmsSignalError(LCMS_ERRC_ABORTED, "What are you trying to do with more that 255 profiles?!?, of course aborted");
2107 return NULL;
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?
2135 nNamedColor = 0;
2136 for (i=0; i < nProfiles; i++) {
2137 if (cmsGetDeviceClass(hProfiles[i]) == icSigNamedColorClass)
2138 nNamedColor++;
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
2156 else
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");
2161 return NULL;
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;
2184 // Check colorspace
2186 hProfile = hProfiles[i];
2187 lIsDeviceLink = (cmsGetDeviceClass(hProfile) == icSigLinkClass);
2188 lIsInput = (CurrentColorSpace != icSigXYZData) &&
2189 (CurrentColorSpace != icSigLabData);
2191 if (lIsInput) {
2193 ColorSpaceIn = cmsGetColorSpace(hProfile);
2194 ColorSpaceOut = cmsGetPCS(hProfile);
2197 else {
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,
2217 NULL, FormatOutput,
2218 Intent, dwPrecalcFlags);
2221 else {
2223 if (lIsInput) {
2225 Transforms[i] = cmsCreateTransform(hProfile, FormatInput,
2226 (ColorSpaceOut == icSigLabData ? hLab : hXYZ), FormatOutput,
2227 Intent, dwPrecalcFlags);
2229 else {
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);
2247 else
2248 if (CurrentColorSpace == icSigLabData) {
2250 Transforms[i] = cmsCreateTransform(hLab, FormatInput,
2251 hProfile, FormatOutput,
2252 Intent, dwPrecalcFlags);
2255 else {
2256 cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateMultiprofileTransform: ColorSpace mismatch");
2257 goto ErrorCleanup;
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)) {
2282 cmsFreeLUT(Grid);
2283 goto ErrorCleanup;
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;
2313 ErrorCleanup:
2315 if (hLab) cmsCloseProfile(hLab);
2316 if (hXYZ) cmsCloseProfile(hXYZ);
2317 return NULL;
2322 double LCMSEXPORT cmsSetAdaptationState(double d)
2324 double OldVal = GlobalAdaptationState;
2326 if (d >= 0)
2327 GlobalAdaptationState = d;
2329 return OldVal;