b=450088 backing out (new reftest failed)
[wine-gecko.git] / modules / lcms / src / cmsgmt.c
blob74cde8b97b6eec8e7155e34317933efb85d1665c
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 Gamut check by default is a catching of 0xFFFF/0xFFFF/0xFFFF PCS values, used
28 internally by lcms to hold invalid values. Matrix LUT's, operates in a way that
29 unencodeable values are marked as this combination, if PCS is XYZ, this is a very
30 high value since encoding is a 1.15 fixed point, something like 1.9997, 1.9997, 1.9997
31 not a very common color after all. Lab PCS is not to be a problem, since L>100 are truely
32 undefined. There is a posibility than ICC comitee defines L>100 as a valid means
33 to use highlights, then it will be lost.
35 (1.10 - Actually ICC did it, so this should be checked for full ICC 4.0 support)
40 LCMSBOOL _cmsEndPointsBySpace(icColorSpaceSignature Space, WORD **White, WORD **Black,
41 int *nOutputs)
43 // Only most common spaces
45 static WORD RGBblack[4] = { 0, 0, 0 };
46 static WORD RGBwhite[4] = { 0xffff, 0xffff, 0xffff };
47 static WORD CMYKblack[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; // 400% of ink
48 static WORD CMYKwhite[4] = { 0, 0, 0, 0 };
49 static WORD LABblack[4] = { 0, 0x8000, 0x8000 };
50 static WORD LABwhite[4] = { 0xFF00, 0x8000, 0x8000 };
51 static WORD CMYblack[4] = { 0xffff, 0xffff, 0xffff };
52 static WORD CMYwhite[4] = { 0, 0, 0 };
53 static WORD Grayblack[4] = { 0 };
54 static WORD GrayWhite[4] = { 0xffff };
56 switch (Space) {
58 case icSigGrayData: if (White) *White = GrayWhite;
59 if (Black) *Black = Grayblack;
60 if (nOutputs) *nOutputs = 1;
61 return TRUE;
63 case icSigRgbData: if (White) *White = RGBwhite;
64 if (Black) *Black = RGBblack;
65 if (nOutputs) *nOutputs = 3;
66 return TRUE;
68 case icSigLabData: if (White) *White = LABwhite;
69 if (Black) *Black = LABblack;
70 if (nOutputs) *nOutputs = 3;
71 return TRUE;
73 case icSigCmykData: if (White) *White = CMYKwhite;
74 if (Black) *Black = CMYKblack;
75 if (nOutputs) *nOutputs = 4;
76 return TRUE;
78 case icSigCmyData: if (White) *White = CMYwhite;
79 if (Black) *Black = CMYblack;
80 if (nOutputs) *nOutputs = 3;
81 return TRUE;
83 default:;
86 return FALSE;
90 WORD *_cmsWhiteBySpace(icColorSpaceSignature Space)
92 WORD *White= NULL, *Black = NULL;
93 int Dummy;
94 static WORD Default[MAXCHANNELS];
96 if (_cmsEndPointsBySpace(Space, &White, &Black, &Dummy))
97 return White;
99 return Default;
106 WORD Clamp_L(Fixed32 in)
108 if (in == 0xFFFF) return 0xFFFFU; // Marker
110 if (in > 0xFF00) return 0xFF00U; // L* = 100.0
111 return (WORD) in;
115 #define ENCODE_AB(x) (WORD) (((x) + 128.0) * 256.0 + 0.5)
117 WORD Clamp_ab(Fixed32 in)
119 if (in == 0xFFFF) return 0xFFFFU; // Marker
121 if (in < 0) return ENCODE_AB(-128.0); // Max negative number
122 if (in > 0xFFFF) return ENCODE_AB(+127.9961); // Max positive number
123 return (WORD) in;
128 // Returns dE on two Lab values
130 double LCMSEXPORT cmsDeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
132 double dL, da, db;
134 if (Lab1 -> L < 0 ||
135 Lab2 -> L < 0) return 65536.;
137 if (Lab1 -> a < -200 || Lab1 -> a > 200) return 65536.;
138 if (Lab1 -> b < -200 || Lab1 -> b > 200) return 65536.;
140 if (Lab2 -> a < -200 || Lab2 -> a > 200) return 65536.;
141 if (Lab2 -> b < -200 || Lab2 -> b > 200) return 65536.;
143 if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
145 dL = fabs(Lab1 -> L - Lab2 -> L);
146 da = fabs(Lab1 -> a - Lab2 -> a);
147 db = fabs(Lab1 -> b - Lab2 -> b);
149 return pow(dL*dL + da * da + db * db, 0.5);
154 // Square
155 static
156 double Sqr(double v)
158 return v * v;
161 // Return the CIE94 Delta E
162 double LCMSEXPORT cmsCIE94DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
164 cmsCIELCh LCh1, LCh2;
165 double dE, dL, dC, dh, dhsq;
166 double c12, sc, sh;
168 if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
170 dL = fabs(Lab1 ->L - Lab2 ->L);
172 cmsLab2LCh(&LCh1, Lab1);
173 cmsLab2LCh(&LCh2, Lab2);
175 dC = fabs(LCh1.C - LCh2.C);
176 dE = cmsDeltaE(Lab1, Lab2);
178 dhsq = Sqr(dE) - Sqr(dL) - Sqr(dC);
179 if (dhsq < 0)
180 dh = 0;
181 else
182 dh = pow(dhsq, 0.5);
184 c12 = sqrt(LCh1.C * LCh2.C);
186 sc = 1.0 + (0.048 * c12);
187 sh = 1.0 + (0.014 * c12);
189 return sqrt(Sqr(dL) + Sqr(dC) / Sqr(sc) + Sqr(dh) / Sqr(sh));
193 // Auxiliary
195 static
196 double ComputeLBFD(LPcmsCIELab Lab)
198 double yt;
200 if (Lab->L > 7.996969)
201 yt = (Sqr((Lab->L+16)/116)*((Lab->L+16)/116))*100;
202 else
203 yt = 100 * (Lab->L / 903.3);
205 return (54.6 * (LOGE * (log(yt + 1.5))) - 9.6);
210 // bfd - gets BFD(1:1) difference between Lab1, Lab2
211 double LCMSEXPORT cmsBFDdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
213 double lbfd1,lbfd2,AveC,Aveh,dE,deltaL,
214 deltaC,deltah,dc,t,g,dh,rh,rc,rt,bfd;
215 cmsCIELCh LCh1, LCh2;
218 if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
220 lbfd1 = ComputeLBFD(Lab1);
221 lbfd2 = ComputeLBFD(Lab2);
222 deltaL = lbfd2 - lbfd1;
224 cmsLab2LCh(&LCh1, Lab1);
225 cmsLab2LCh(&LCh2, Lab2);
227 deltaC = LCh2.C - LCh1.C;
228 AveC = (LCh1.C+LCh2.C)/2;
229 Aveh = (LCh1.h+LCh2.h)/2;
231 dE = cmsDeltaE(Lab1, Lab2);
233 if (Sqr(dE)>(Sqr(Lab2->L-Lab1->L)+Sqr(deltaC)))
234 deltah = sqrt(Sqr(dE)-Sqr(Lab2->L-Lab1->L)-Sqr(deltaC));
235 else
236 deltah =0;
239 dc = 0.035 * AveC / (1 + 0.00365 * AveC)+0.521;
240 g = sqrt(Sqr(Sqr(AveC))/(Sqr(Sqr(AveC))+14000));
241 t = 0.627+(0.055*cos((Aveh-254)/(180/M_PI))-
242 0.040*cos((2*Aveh-136)/(180/M_PI))+
243 0.070*cos((3*Aveh-31)/(180/M_PI))+
244 0.049*cos((4*Aveh+114)/(180/M_PI))-
245 0.015*cos((5*Aveh-103)/(180/M_PI)));
247 dh = dc*(g*t+1-g);
248 rh = -0.260*cos((Aveh-308)/(180/M_PI))-
249 0.379*cos((2*Aveh-160)/(180/M_PI))-
250 0.636*cos((3*Aveh+254)/(180/M_PI))+
251 0.226*cos((4*Aveh+140)/(180/M_PI))-
252 0.194*cos((5*Aveh+280)/(180/M_PI));
254 rc = sqrt((AveC*AveC*AveC*AveC*AveC*AveC)/((AveC*AveC*AveC*AveC*AveC*AveC)+70000000));
255 rt = rh*rc;
257 bfd = sqrt(Sqr(deltaL)+Sqr(deltaC/dc)+Sqr(deltah/dh)+(rt*(deltaC/dc)*(deltah/dh)));
259 return bfd;
263 // cmc - CMC(1:1) difference between Lab1, Lab2
264 double LCMSEXPORT cmsCMCdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
266 double dE,dL,dC,dh,sl,sc,sh,t,f,cmc;
267 cmsCIELCh LCh1, LCh2;
269 if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
271 cmsLab2LCh(&LCh1, Lab1);
272 cmsLab2LCh(&LCh2, Lab2);
275 dL = Lab2->L-Lab1->L;
276 dC = LCh2.C-LCh1.C;
278 dE = cmsDeltaE(Lab1, Lab2);
279 if (Sqr(dE)>(Sqr(dL)+Sqr(dC)))
280 dh = sqrt(Sqr(dE)-Sqr(dL)-Sqr(dC));
281 else
282 dh =0;
284 if ((LCh1.h > 164) && (LCh1.h<345))
285 t = 0.56 + fabs(0.2 * cos(((LCh1.h + 168)/(180/M_PI))));
286 else
287 t = 0.36 + fabs(0.4 * cos(((LCh1.h + 35 )/(180/M_PI))));
289 sc = 0.0638 * LCh1.C / (1 + 0.0131 * LCh1.C) + 0.638;
290 sl = 0.040975 * Lab1->L /(1 + 0.01765 * Lab1->L);
292 if (Lab1->L<16)
293 sl = 0.511;
295 f = sqrt((LCh1.C * LCh1.C * LCh1.C * LCh1.C)/((LCh1.C * LCh1.C * LCh1.C * LCh1.C)+1900));
296 sh = sc*(t*f+1-f);
297 cmc = sqrt(Sqr(dL/sl)+Sqr(dC/sc)+Sqr(dh/sh));
299 return cmc;
304 static
305 double atan2deg(double b, double a)
307 double h;
309 if (a == 0 && b == 0)
310 h = 0;
311 else
312 h = atan2(a, b);
314 h *= (180. / M_PI);
316 while (h > 360.)
317 h -= 360.;
319 while ( h < 0)
320 h += 360.;
322 return h;
327 static
328 double RADIANES(double deg)
330 return (deg * M_PI) / 180.;
334 // dE2000 The weightings KL, KC and KH can be modified to reflect the relative
335 // importance of lightness, chroma and hue in different industrial applications
337 double LCMSEXPORT cmsCIE2000DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2,
338 double Kl, double Kc, double Kh)
340 double L1 = Lab1->L;
341 double a1 = Lab1->a;
342 double b1 = Lab1->b;
343 double C = sqrt( Sqr(a1) + Sqr(b1) );
345 double Ls = Lab2 ->L;
346 double as = Lab2 ->a;
347 double bs = Lab2 ->b;
348 double Cs = sqrt( Sqr(as) + Sqr(bs) );
350 double G = 0.5 * ( 1 - sqrt(pow((C + Cs) / 2 , 7.0) / (pow((C + Cs) / 2, 7.0) + pow(25.0, 7.0) ) ));
352 double a_p = (1 + G ) * a1;
353 double b_p = b1;
354 double C_p = sqrt( Sqr(a_p) + Sqr(b_p));
355 double h_p = atan2deg(a_p, b_p);
358 double a_ps = (1 + G) * as;
359 double b_ps = bs;
360 double C_ps = sqrt(Sqr(a_ps) + Sqr(b_ps));
361 double h_ps = atan2deg(a_ps, b_ps);
363 double meanC_p =(C_p + C_ps) / 2;
365 double hps_plus_hp = h_ps + h_p;
366 double hps_minus_hp = h_ps - h_p;
368 double meanh_p = fabs(hps_minus_hp) <= 180.000001 ? (hps_plus_hp)/2 :
369 (hps_plus_hp) < 360 ? (hps_plus_hp + 360)/2 :
370 (hps_plus_hp - 360)/2;
372 double delta_h = (hps_minus_hp) <= -180.000001 ? (hps_minus_hp + 360) :
373 (hps_minus_hp) > 180 ? (hps_minus_hp - 360) :
374 (hps_minus_hp);
375 double delta_L = (Ls - L1);
376 double delta_C = (C_ps - C_p );
379 double delta_H =2 * sqrt(C_ps*C_p) * sin(RADIANES(delta_h) / 2);
381 double T = 1 - 0.17 * cos(RADIANES(meanh_p-30))
382 + 0.24 * cos(RADIANES(2*meanh_p))
383 + 0.32 * cos(RADIANES(3*meanh_p + 6))
384 - 0.2 * cos(RADIANES(4*meanh_p - 63));
386 double Sl = 1 + (0.015 * Sqr((Ls + L1) /2- 50) )/ sqrt(20 + Sqr( (Ls+L1)/2 - 50) );
388 double Sc = 1 + 0.045 * (C_p + C_ps)/2;
389 double Sh = 1 + 0.015 * ((C_ps + C_p)/2) * T;
391 double delta_ro = 30 * exp( -Sqr(((meanh_p - 275 ) / 25)));
393 double Rc = 2 * sqrt(( pow(meanC_p, 7.0) )/( pow(meanC_p, 7.0) + pow(25.0, 7.0)));
395 double Rt = -sin(2 * RADIANES(delta_ro)) * Rc;
397 double deltaE00 = sqrt( Sqr(delta_L /(Sl * Kl)) +
398 Sqr(delta_C/(Sc * Kc)) +
399 Sqr(delta_H/(Sh * Kh)) +
400 Rt*(delta_C/(Sc * Kc)) * (delta_H / (Sh * Kh)));
402 return deltaE00;
407 // Carefully, clamp on CIELab space.
409 void LCMSEXPORT cmsClampLab(LPcmsCIELab Lab, double amax, double amin,
410 double bmax, double bmin)
413 // Whole Luma surface to zero
415 if (Lab -> L < 0) {
417 Lab-> L = Lab->a = Lab-> b = 0.0;
418 return;
421 // Clamp white, DISCARD HIGHLIGHTS. This is done
422 // in such way because icc spec doesn't allow the
423 // use of L>100 as a highlight means.
425 if (Lab->L > 100)
426 Lab -> L = 100;
428 // Check out gamut prism, on a, b faces
430 if (Lab -> a < amin || Lab->a > amax||
431 Lab -> b < bmin || Lab->b > bmax) {
433 cmsCIELCh LCh;
434 double h, slope;
436 // Falls outside a, b limits. Transports to LCh space,
437 // and then do the clipping
440 if (Lab -> a == 0.0) { // Is hue exactly 90?
442 // atan will not work, so clamp here
443 Lab -> b = Lab->b < 0 ? bmin : bmax;
444 return;
447 cmsLab2LCh(&LCh, Lab);
449 slope = Lab -> b / Lab -> a;
450 h = LCh.h;
452 // There are 4 zones
454 if ((h >= 0. && h < 45.) ||
455 (h >= 315 && h <= 360.)) {
457 // clip by amax
458 Lab -> a = amax;
459 Lab -> b = amax * slope;
461 else
462 if (h >= 45. && h < 135)
464 // clip by bmax
465 Lab -> b = bmax;
466 Lab -> a = bmax / slope;
468 else
469 if (h >= 135 && h < 225) {
470 // clip by amin
471 Lab -> a = amin;
472 Lab -> b = amin * slope;
475 else
476 if (h >= 225 && h < 315) {
477 // clip by bmin
478 Lab -> b = bmin;
479 Lab -> a = bmin / slope;
481 else
482 cmsSignalError(LCMS_ERRC_ABORTED, "Invalid angle");
487 // Several utilities -------------------------------------------------------
489 // Translate from our colorspace to ICC representation
491 icColorSpaceSignature LCMSEXPORT _cmsICCcolorSpace(int OurNotation)
493 switch (OurNotation) {
495 case 1:
496 case PT_GRAY: return icSigGrayData;
498 case 2:
499 case PT_RGB: return icSigRgbData;
501 case PT_CMY: return icSigCmyData;
502 case PT_CMYK: return icSigCmykData;
503 case PT_YCbCr:return icSigYCbCrData;
504 case PT_YUV: return icSigLuvData;
505 case PT_XYZ: return icSigXYZData;
506 case PT_Lab: return icSigLabData;
507 case PT_YUVK: return icSigLuvKData;
508 case PT_HSV: return icSigHsvData;
509 case PT_HLS: return icSigHlsData;
510 case PT_Yxy: return icSigYxyData;
511 case PT_HiFi: return icSigHexachromeData;
512 case PT_HiFi7: return icSigHeptachromeData;
513 case PT_HiFi8: return icSigOctachromeData;
515 case PT_HiFi9: return icSigMCH9Data;
516 case PT_HiFi10: return icSigMCHAData;
517 case PT_HiFi11: return icSigMCHBData;
518 case PT_HiFi12: return icSigMCHCData;
519 case PT_HiFi13: return icSigMCHDData;
520 case PT_HiFi14: return icSigMCHEData;
521 case PT_HiFi15: return icSigMCHFData;
523 default: return icMaxEnumData;
528 int LCMSEXPORT _cmsLCMScolorSpace(icColorSpaceSignature ProfileSpace)
530 switch (ProfileSpace) {
532 case icSigGrayData: return PT_GRAY;
533 case icSigRgbData: return PT_RGB;
534 case icSigCmyData: return PT_CMY;
535 case icSigCmykData: return PT_CMYK;
536 case icSigYCbCrData:return PT_YCbCr;
537 case icSigLuvData: return PT_YUV;
538 case icSigXYZData: return PT_XYZ;
539 case icSigLabData: return PT_Lab;
540 case icSigLuvKData: return PT_YUVK;
541 case icSigHsvData: return PT_HSV;
542 case icSigHlsData: return PT_HLS;
543 case icSigYxyData: return PT_Yxy;
545 case icSig6colorData:
546 case icSigHexachromeData: return PT_HiFi;
548 case icSigHeptachromeData:
549 case icSig7colorData: return PT_HiFi7;
551 case icSigOctachromeData:
552 case icSig8colorData: return PT_HiFi8;
554 case icSigMCH9Data:
555 case icSig9colorData: return PT_HiFi9;
557 case icSigMCHAData:
558 case icSig10colorData: return PT_HiFi10;
560 case icSigMCHBData:
561 case icSig11colorData: return PT_HiFi11;
563 case icSigMCHCData:
564 case icSig12colorData: return PT_HiFi12;
566 case icSigMCHDData:
567 case icSig13colorData: return PT_HiFi13;
569 case icSigMCHEData:
570 case icSig14colorData: return PT_HiFi14;
572 case icSigMCHFData:
573 case icSig15colorData: return PT_HiFi15;
575 default: return icMaxEnumData;
580 int LCMSEXPORT _cmsChannelsOf(icColorSpaceSignature ColorSpace)
583 switch (ColorSpace) {
585 case icSigGrayData: return 1;
587 case icSig2colorData: return 2;
589 case icSigXYZData:
590 case icSigLabData:
591 case icSigLuvData:
592 case icSigYCbCrData:
593 case icSigYxyData:
594 case icSigRgbData:
595 case icSigHsvData:
596 case icSigHlsData:
597 case icSigCmyData:
598 case icSig3colorData: return 3;
600 case icSigLuvKData:
601 case icSigCmykData:
602 case icSig4colorData: return 4;
604 case icSigMCH5Data:
605 case icSig5colorData: return 5;
607 case icSigHexachromeData:
608 case icSig6colorData: return 6;
610 case icSigHeptachromeData:
611 case icSig7colorData: return 7;
613 case icSigOctachromeData:
614 case icSig8colorData: return 8;
616 case icSigMCH9Data:
617 case icSig9colorData: return 9;
619 case icSigMCHAData:
620 case icSig10colorData: return 10;
622 case icSigMCHBData:
623 case icSig11colorData: return 11;
625 case icSigMCHCData:
626 case icSig12colorData: return 12;
628 case icSigMCHDData:
629 case icSig13colorData: return 13;
631 case icSigMCHEData:
632 case icSig14colorData: return 14;
634 case icSigMCHFData:
635 case icSig15colorData: return 15;
637 default: return 3;
643 // v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable
644 // number of gridpoints that would make exact match. However, a
645 // prelinearization of 258 entries, would map 0xFF00 on entry 257.
646 // This is almost what we need, unfortunately, the rest of entries
647 // should be scaled by (255*257/256) and this is not exact.
649 // An intermediate solution would be to use 257 entries. This does not
650 // map 0xFF00 exactly on a node, but so close that the dE induced is
651 // negligible. AND the rest of curve is exact.
653 static
654 void CreateLabPrelinearization(LPGAMMATABLE LabTable[])
656 int i;
658 LabTable[0] = cmsAllocGamma(257);
659 LabTable[1] = cmsBuildGamma(257, 1.0);
660 LabTable[2] = cmsBuildGamma(257, 1.0);
662 // L* uses 257 entries. Entry 256 holds 0xFFFF, so, the effective range
663 // is 0..0xFF00. Last entry (257) is also collapsed to 0xFFFF
665 // From 0 to 0xFF00
666 for (i=0; i < 256; i++)
667 LabTable[0]->GammaTable[i] = RGB_8_TO_16(i);
669 // Repeat last for 0xFFFF
670 LabTable[0] ->GammaTable[256] = 0xFFFF;
674 // Used by gamut & softproofing
676 typedef struct {
678 cmsHTRANSFORM hInput; // From whatever input color space. NULL for Lab
679 cmsHTRANSFORM hForward, hReverse; // Transforms going from Lab to colorant and back
680 double Thereshold; // The thereshold after which is considered out of gamut
682 } GAMUTCHAIN,FAR* LPGAMUTCHAIN;
684 // This sampler does compute gamut boundaries by comparing original
685 // values with a transform going back and forth. Values above ERR_THERESHOLD
686 // of maximum are considered out of gamut.
689 #define ERR_THERESHOLD 5
692 static
693 int GamutSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
695 LPGAMUTCHAIN t = (LPGAMUTCHAIN) Cargo;
696 WORD Proof[MAXCHANNELS], Check[MAXCHANNELS];
697 WORD Proof2[MAXCHANNELS], Check2[MAXCHANNELS];
698 cmsCIELab LabIn1, LabOut1;
699 cmsCIELab LabIn2, LabOut2;
700 double dE1, dE2, ErrorRatio;
702 // Assume in-gamut by default.
703 dE1 = 0.;
704 dE2 = 0;
705 ErrorRatio = 1.0;
708 // Any input space? I can use In[] no matter channels
709 // because is just one pixel
711 if (t -> hInput != NULL) cmsDoTransform(t -> hInput, In, In, 1);
713 // converts from PCS to colorant. This always
714 // does return in-gamut values,
715 cmsDoTransform(t -> hForward, In, Proof, 1);
717 // Now, do the inverse, from colorant to PCS.
718 cmsDoTransform(t -> hReverse, Proof, Check, 1);
721 // Try again, but this time taking Check as input
722 cmsDoTransform(t -> hForward, Check, Proof2, 1);
723 cmsDoTransform(t -> hReverse, Proof2, Check2, 1);
727 // Does the transform returns out-of-gamut?
728 if (Check[0] == 0xFFFF &&
729 Check[1] == 0xFFFF &&
730 Check[2] == 0xFFFF)
732 Out[0] = 0xFF00; // Out of gamut!
733 else {
735 // Transport encoded values
736 cmsLabEncoded2Float(&LabIn1, In);
737 cmsLabEncoded2Float(&LabOut1, Check);
739 // Take difference of direct value
740 dE1 = cmsDeltaE(&LabIn1, &LabOut1);
742 cmsLabEncoded2Float(&LabIn2, Check);
743 cmsLabEncoded2Float(&LabOut2, Check2);
745 // Take difference of converted value
746 dE2 = cmsDeltaE(&LabIn2, &LabOut2);
749 // if dE1 is small and dE2 is small, value is likely to be in gamut
750 if (dE1 < t->Thereshold && dE2 < t->Thereshold)
751 Out[0] = 0;
752 else
753 // if dE1 is small and dE2 is big, undefined. Assume in gamut
754 if (dE1 < t->Thereshold && dE2 > t->Thereshold)
755 Out[0] = 0;
756 else
757 // dE1 is big and dE2 is small, clearly out of gamut
758 if (dE1 > t->Thereshold && dE2 < t->Thereshold)
759 Out[0] = (WORD) _cmsQuickFloor((dE1 - t->Thereshold) + .5);
760 else {
762 // dE1 is big and dE2 is also big, could be due to perceptual mapping
763 // so take error ratio
764 if (dE2 == 0.0)
765 ErrorRatio = dE1;
766 else
767 ErrorRatio = dE1 / dE2;
769 if (ErrorRatio > t->Thereshold)
770 Out[0] = (WORD) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5);
771 else
772 Out[0] = 0;
777 return TRUE;
781 // Does compute a gamut LUT going back and forth across
782 // pcs -> relativ. colorimetric intent -> pcs
783 // the dE obtained is then annotated on the LUT.
784 // values truely out of gamut, are clipped to dE = 0xFFFE
785 // and values changed are supposed to be handled by
786 // any gamut remapping, so, are out of gamut as well.
788 // **WARNING: This algorithm does assume that gamut
789 // remapping algorithms does NOT move in-gamut colors,
790 // of course, many perceptual and saturation intents does
791 // not work in such way, but relativ. ones should.
793 static
794 LPLUT ComputeGamutWithInput(cmsHPROFILE hInput, cmsHPROFILE hProfile, int Intent)
796 cmsHPROFILE hLab;
797 LPLUT Gamut;
798 DWORD dwFormat;
799 GAMUTCHAIN Chain;
800 int nErrState, nChannels, nGridpoints;
801 LPGAMMATABLE Trans[3];
802 icColorSpaceSignature ColorSpace;
805 ZeroMemory(&Chain, sizeof(GAMUTCHAIN));
807 hLab = cmsCreateLabProfile(NULL);
809 // Safeguard against early abortion
810 nErrState = cmsErrorAction(LCMS_ERROR_IGNORE);
812 // The figure of merit. On matrix-shaper profiles, should be almost zero as
813 // the conversion is pretty exact. On LUT based profiles, different resolutions
814 // of input and output CLUT may result in differences.
816 if (!cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) &&
817 !cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_OUTPUT))
819 Chain.Thereshold = 1.0;
820 else
821 Chain.Thereshold = ERR_THERESHOLD;
823 ColorSpace = cmsGetColorSpace(hProfile);
825 // If input profile specified, create a transform from such profile to Lab
826 if (hInput != NULL) {
828 nChannels = _cmsChannelsOf(ColorSpace);
829 nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC);
830 dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2));
832 Chain.hInput = cmsCreateTransform(hInput, dwFormat,
833 hLab, TYPE_Lab_16,
834 Intent,
835 cmsFLAGS_NOTPRECALC);
837 else {
838 // Input transform=NULL (Lab) Used to compute the gamut tag
839 // This table will take 53 points to give some accurancy,
840 // 53 * 53 * 53 * 2 = 291K
842 nChannels = 3; // For Lab
843 nGridpoints = 53;
844 Chain.hInput = NULL;
845 dwFormat = (CHANNELS_SH(_cmsChannelsOf(ColorSpace))|BYTES_SH(2));
849 // Does create the forward step
850 Chain.hForward = cmsCreateTransform(hLab, TYPE_Lab_16,
851 hProfile, dwFormat,
852 INTENT_RELATIVE_COLORIMETRIC,
853 cmsFLAGS_NOTPRECALC);
855 // Does create the backwards step
856 Chain.hReverse = cmsCreateTransform(hProfile, dwFormat,
857 hLab, TYPE_Lab_16,
858 INTENT_RELATIVE_COLORIMETRIC,
859 cmsFLAGS_NOTPRECALC);
861 // Restores error handler previous state
862 cmsErrorAction(nErrState);
865 // All ok?
866 if (Chain.hForward && Chain.hReverse) {
868 // Go on, try to compute gamut LUT from PCS.
869 // This consist on a single channel containing
870 // dE when doing a transform back and forth on
871 // the colorimetric intent.
873 Gamut = cmsAllocLUT();
874 Gamut = cmsAlloc3DGrid(Gamut, nGridpoints, nChannels, 1);
876 // If no input, then this is a gamut tag operated by Lab,
877 // so include pertinent prelinearization
878 if (hInput == NULL) {
880 CreateLabPrelinearization(Trans);
881 cmsAllocLinearTable(Gamut, Trans, 1);
882 cmsFreeGammaTriple(Trans);
886 cmsSample3DGrid(Gamut, GamutSampler, (LPVOID) &Chain, Gamut ->wFlags);
888 else
889 Gamut = NULL; // Didn't work...
891 // Free all needed stuff.
892 if (Chain.hInput) cmsDeleteTransform(Chain.hInput);
893 if (Chain.hForward) cmsDeleteTransform(Chain.hForward);
894 if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse);
896 cmsCloseProfile(hLab);
898 // And return computed hull
899 return Gamut;
903 // Wrapper
905 LPLUT _cmsComputeGamutLUT(cmsHPROFILE hProfile, int Intent)
907 return ComputeGamutWithInput(NULL, hProfile, Intent);
911 // This routine does compute the gamut check CLUT. This CLUT goes from whatever
912 // input space to the 0 or != 0 gamut check.
914 LPLUT _cmsPrecalculateGamutCheck(cmsHTRANSFORM h)
916 _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h;
918 return ComputeGamutWithInput(p->InputProfile, p ->PreviewProfile, p->Intent);
922 // SoftProofing. Convert from Lab to device, then back to Lab,
923 // any gamut remapping is applied
925 static
926 int SoftProofSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
928 LPGAMUTCHAIN t = (LPGAMUTCHAIN) Cargo;
929 WORD Colorant[MAXCHANNELS];
931 // From pcs to colorant
932 cmsDoTransform(t -> hForward, In, Colorant, 1);
934 // Now, do the inverse, from colorant to pcs.
935 cmsDoTransform(t -> hReverse, Colorant, Out, 1);
937 return TRUE;
940 // Does return Softproofing LUT on desired intent
942 LPLUT _cmsComputeSoftProofLUT(cmsHPROFILE hProfile, int nIntent)
944 cmsHPROFILE hLab;
945 LPLUT SoftProof;
946 DWORD dwFormat;
947 GAMUTCHAIN Chain;
948 int nErrState;
949 LPGAMMATABLE Trans[3];
952 // LUTs are never abs. colorimetric, is the transform who
953 // is responsible of generating white point displacement
954 if (nIntent == INTENT_ABSOLUTE_COLORIMETRIC)
955 nIntent = INTENT_RELATIVE_COLORIMETRIC;
957 ZeroMemory(&Chain, sizeof(GAMUTCHAIN));
959 hLab = cmsCreateLabProfile(NULL);
961 // ONLY 4 channels
962 dwFormat = (CHANNELS_SH(4)|BYTES_SH(2));
964 // Safeguard against early abortion
965 nErrState = cmsErrorAction(LCMS_ERROR_IGNORE);
967 // Does create the first step
968 Chain.hForward = cmsCreateTransform(hLab, TYPE_Lab_16,
969 hProfile, dwFormat,
970 nIntent,
971 cmsFLAGS_NOTPRECALC);
973 // Does create the last step
974 Chain.hReverse = cmsCreateTransform(hProfile, dwFormat,
975 hLab, TYPE_Lab_16,
976 INTENT_RELATIVE_COLORIMETRIC,
977 cmsFLAGS_NOTPRECALC);
979 // Restores error handler previous state
980 cmsErrorAction(nErrState);
982 // All ok?
983 if (Chain.hForward && Chain.hReverse) {
985 // This is Lab -> Lab, so 33 point should hold anything
986 SoftProof = cmsAllocLUT();
987 SoftProof = cmsAlloc3DGrid(SoftProof, 33, 3, 3);
989 CreateLabPrelinearization(Trans);
990 cmsAllocLinearTable(SoftProof, Trans, 1);
991 cmsFreeGammaTriple(Trans);
993 cmsSample3DGrid(SoftProof, SoftProofSampler, (LPVOID) &Chain, SoftProof->wFlags);
995 else
996 SoftProof = NULL; // Didn't work...
998 // Free all needed stuff.
999 if (Chain.hForward) cmsDeleteTransform(Chain.hForward);
1000 if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse);
1002 cmsCloseProfile(hLab);
1004 return SoftProof;
1008 static
1009 int MostlyLinear(WORD Table[], int nEntries)
1011 register int i;
1012 int diff;
1014 for (i=5; i < nEntries; i++) {
1016 diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries));
1017 if (diff > 0x0300)
1018 return 0;
1021 return 1;
1025 static
1026 void SlopeLimiting(WORD Table[], int nEntries)
1028 int At = (int) floor((double) nEntries * 0.02 + 0.5); // Cutoff at 2%
1029 double Val, Slope;
1030 int i;
1032 Val = Table[At];
1033 Slope = Val / At;
1035 for (i=0; i < At; i++)
1036 Table[i] = (WORD) floor(i * Slope + 0.5);
1041 // Check for monotonicity.
1043 static
1044 LCMSBOOL IsMonotonic(LPGAMMATABLE t)
1046 int n = t -> nEntries;
1047 int i, last;
1049 last = t ->GammaTable[n-1];
1051 for (i = n-2; i >= 0; --i) {
1053 if (t ->GammaTable[i] > last)
1055 return FALSE;
1056 else
1057 last = t ->GammaTable[i];
1061 return TRUE;
1064 // Check for endpoints
1066 static
1067 LCMSBOOL HasProperEndpoints(LPGAMMATABLE t)
1069 if (t ->GammaTable[0] != 0) return FALSE;
1070 if (t ->GammaTable[t ->nEntries-1] != 0xFFFF) return FALSE;
1072 return TRUE;
1077 #define PRELINEARIZATION_POINTS 4096
1079 // Fixes the gamma balancing of transform. Thanks to Mike Chaney
1080 // for pointing this subtle bug.
1082 void _cmsComputePrelinearizationTablesFromXFORM(cmsHTRANSFORM h[], int nTransforms, LPLUT Grid)
1084 LPGAMMATABLE Trans[MAXCHANNELS];
1085 unsigned int t, i, v;
1086 int j;
1087 WORD In[MAXCHANNELS], Out[MAXCHANNELS];
1088 LCMSBOOL lIsSuitable;
1089 _LPcmsTRANSFORM InputXForm = (_LPcmsTRANSFORM) h[0];
1090 _LPcmsTRANSFORM OutputXForm = (_LPcmsTRANSFORM) h[nTransforms-1];
1093 // First space is *Lab, use our specialized curves for v2 Lab
1095 if (InputXForm ->EntryColorSpace == icSigLabData &&
1096 OutputXForm->ExitColorSpace != icSigLabData) {
1098 CreateLabPrelinearization(Trans);
1099 cmsAllocLinearTable(Grid, Trans, 1);
1100 cmsFreeGammaTriple(Trans);
1101 return;
1105 // Do nothing on all but Gray/RGB to Gray/RGB transforms
1107 if (((InputXForm ->EntryColorSpace != icSigRgbData) && (InputXForm ->EntryColorSpace != icSigGrayData)) ||
1108 ((OutputXForm->ExitColorSpace != icSigRgbData) && (OutputXForm->ExitColorSpace != icSigGrayData))) return;
1111 for (t = 0; t < Grid -> InputChan; t++)
1112 Trans[t] = cmsAllocGamma(PRELINEARIZATION_POINTS);
1114 for (i=0; i < PRELINEARIZATION_POINTS; i++) {
1116 v = _cmsQuantizeVal(i, PRELINEARIZATION_POINTS);
1118 for (t=0; t < Grid -> InputChan; t++)
1119 In[t] = (WORD) v;
1121 cmsDoTransform(h[0], In, Out, 1);
1122 for (j=1; j < nTransforms; j++)
1123 cmsDoTransform(h[j], Out, Out, 1);
1125 for (t=0; t < Grid -> InputChan; t++)
1126 Trans[t] ->GammaTable[i] = Out[t];
1131 // Check transfer curves
1132 lIsSuitable = TRUE;
1133 for (t=0; (lIsSuitable && (t < Grid->InputChan)); t++) {
1136 // Exclude if already linear
1137 if (MostlyLinear(Trans[t]->GammaTable, PRELINEARIZATION_POINTS))
1138 lIsSuitable = FALSE;
1140 // Exclude if non-monotonic
1141 if (!IsMonotonic(Trans[t]))
1142 lIsSuitable = FALSE;
1144 // Exclude if weird endpoints
1145 if (!HasProperEndpoints(Trans[t]))
1146 lIsSuitable = FALSE;
1149 // Exclude if transfer function is not smooth enough
1150 // to be modelled as a gamma function, or the gamma is reversed
1152 if (cmsEstimateGamma(Trans[t]) < 1.0)
1153 lIsSuitable = FALSE;
1158 if (lIsSuitable) {
1160 for (t = 0; t < Grid ->InputChan; t++)
1161 SlopeLimiting(Trans[t]->GammaTable, Trans[t]->nEntries);
1164 if (lIsSuitable) cmsAllocLinearTable(Grid, Trans, 1);
1167 for (t = 0; t < Grid ->InputChan; t++)
1168 cmsFreeGamma(Trans[t]);
1174 // Compute K -> L* relationship. Flags may include black point compensation. In this case,
1175 // the relationship is assumed from the profile with BPC to a black point zero.
1176 static
1177 LPGAMMATABLE ComputeKToLstar(cmsHPROFILE hProfile, int nPoints, int Intent, DWORD dwFlags)
1179 LPGAMMATABLE out;
1180 int i;
1181 WORD cmyk[4], wLab[3];
1182 cmsHPROFILE hLab = cmsCreateLabProfile(NULL);
1183 cmsHTRANSFORM xform = cmsCreateTransform(hProfile, TYPE_CMYK_16,
1184 hLab, TYPE_Lab_16,
1185 Intent, (dwFlags|cmsFLAGS_NOTPRECALC));
1188 out = cmsAllocGamma(nPoints);
1189 for (i=0; i < nPoints; i++) {
1191 cmyk[0] = 0;
1192 cmyk[1] = 0;
1193 cmyk[2] = 0;
1194 cmyk[3] = _cmsQuantizeVal(i, nPoints);
1196 cmsDoTransform(xform, cmyk, wLab, 1);
1197 out->GammaTable[i] = (WORD) (0xFFFF - wLab[0]);
1200 cmsDeleteTransform(xform);
1201 cmsCloseProfile(hLab);
1203 return out;
1208 // Compute Black tone curve on a CMYK -> CMYK transform. This is done by
1209 // using the proof direction on both profiles to find K->L* relationship
1210 // then joining both curves. dwFlags may include black point compensation.
1212 LPGAMMATABLE _cmsBuildKToneCurve(cmsHTRANSFORM hCMYK2CMYK, int nPoints)
1214 LPGAMMATABLE in, out;
1215 LPGAMMATABLE KTone;
1216 _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK;
1219 // Make sure CMYK -> CMYK
1220 if (p -> EntryColorSpace != icSigCmykData ||
1221 p -> ExitColorSpace != icSigCmykData) return NULL;
1223 // Create individual curves. BPC works also as each K to L* is
1224 // computed as a BPC to zero black point in case of L*
1225 in = ComputeKToLstar(p ->InputProfile, nPoints, p->Intent, p -> dwOriginalFlags);
1226 out = ComputeKToLstar(p ->OutputProfile, nPoints, p->Intent, p -> dwOriginalFlags);
1228 // Build the relationship
1229 KTone = cmsJoinGamma(in, out);
1231 cmsFreeGamma(in); cmsFreeGamma(out);
1233 // Make sure it is monotonic
1235 if (!IsMonotonic(KTone)) {
1237 cmsFreeGamma(KTone);
1238 return NULL;
1242 return KTone;