1//---------------------------------------------------------------------------------
2//
3// Little Color Management System
4// Copyright (c) 1998-2017 Marti Maria Saguer
5//
6// Permission is hereby granted, free of charge, to any person obtaining
7// a copy of this software and associated documentation files (the "Software"),
8// to deal in the Software without restriction, including without limitation
9// the rights to use, copy, modify, merge, publish, distribute, sublicense,
10// and/or sell copies of the Software, and to permit persons to whom the Software
11// is furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23//
24//---------------------------------------------------------------------------------
25//
26
27#include "lcms2_internal.h"
28
29// inter PCS conversions XYZ <-> CIE L* a* b*
30/*
31
32
33 CIE 15:2004 CIELab is defined as:
34
35 L* = 116*f(Y/Yn) - 16 0 <= L* <= 100
36 a* = 500*[f(X/Xn) - f(Y/Yn)]
37 b* = 200*[f(Y/Yn) - f(Z/Zn)]
38
39 and
40
41 f(t) = t^(1/3) 1 >= t > (24/116)^3
42 (841/108)*t + (16/116) 0 <= t <= (24/116)^3
43
44
45 Reverse transform is:
46
47 X = Xn*[a* / 500 + (L* + 16) / 116] ^ 3 if (X/Xn) > (24/116)
48 = Xn*(a* / 500 + L* / 116) / 7.787 if (X/Xn) <= (24/116)
49
50
51
52 PCS in Lab2 is encoded as:
53
54 8 bit Lab PCS:
55
56 L* 0..100 into a 0..ff byte.
57 a* t + 128 range is -128.0 +127.0
58 b*
59
60 16 bit Lab PCS:
61
62 L* 0..100 into a 0..ff00 word.
63 a* t + 128 range is -128.0 +127.9961
64 b*
65
66
67
68Interchange Space Component Actual Range Encoded Range
69CIE XYZ X 0 -> 1.99997 0x0000 -> 0xffff
70CIE XYZ Y 0 -> 1.99997 0x0000 -> 0xffff
71CIE XYZ Z 0 -> 1.99997 0x0000 -> 0xffff
72
73Version 2,3
74-----------
75
76CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xff00
77CIELAB (16 bit) a* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff
78CIELAB (16 bit) b* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff
79
80
81Version 4
82---------
83
84CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xffff
85CIELAB (16 bit) a* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff
86CIELAB (16 bit) b* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff
87
88*/
89
90// Conversions
91void CMSEXPORT cmsXYZ2xyY(cmsContext ContextID, cmsCIExyY* Dest, const cmsCIEXYZ* Source)
92{
93 cmsFloat64Number ISum;
94 cmsUNUSED_PARAMETER(ContextID);
95
96 ISum = 1./(Source -> X + Source -> Y + Source -> Z);
97
98 Dest -> x = (Source -> X) * ISum;
99 Dest -> y = (Source -> Y) * ISum;
100 Dest -> Y = Source -> Y;
101}
102
103void CMSEXPORT cmsxyY2XYZ(cmsContext ContextID, cmsCIEXYZ* Dest, const cmsCIExyY* Source)
104{
105 cmsUNUSED_PARAMETER(ContextID);
106 Dest -> X = (Source -> x / Source -> y) * Source -> Y;
107 Dest -> Y = Source -> Y;
108 Dest -> Z = ((1 - Source -> x - Source -> y) / Source -> y) * Source -> Y;
109}
110
111/*
112 The break point (24/116)^3 = (6/29)^3 is a very small amount of tristimulus
113 primary (0.008856). Generally, this only happens for
114 nearly ideal blacks and for some orange / amber colors in transmission mode.
115 For example, the Z value of the orange turn indicator lamp lens on an
116 automobile will often be below this value. But the Z does not
117 contribute to the perceived color directly.
118*/
119
120static
121cmsFloat64Number f(cmsFloat64Number t)
122{
123 const cmsFloat64Number Limit = (24.0/116.0) * (24.0/116.0) * (24.0/116.0);
124
125 if (t <= Limit)
126 return (841.0/108.0) * t + (16.0/116.0);
127 else
128 return pow(t, 1.0/3.0);
129}
130
131static
132cmsFloat64Number f_1(cmsFloat64Number t)
133{
134 const cmsFloat64Number Limit = (24.0/116.0);
135
136 if (t <= Limit) {
137 return (108.0/841.0) * (t - (16.0/116.0));
138 }
139
140 return t * t * t;
141}
142
143
144// Standard XYZ to Lab. it can handle negative XZY numbers in some cases
145void CMSEXPORT cmsXYZ2Lab(cmsContext ContextID, const cmsCIEXYZ* WhitePoint, cmsCIELab* Lab, const cmsCIEXYZ* xyz)
146{
147 cmsFloat64Number fx, fy, fz;
148
149 if (WhitePoint == NULL)
150 WhitePoint = cmsD50_XYZ(ContextID);
151
152 fx = f(xyz->X / WhitePoint->X);
153 fy = f(xyz->Y / WhitePoint->Y);
154 fz = f(xyz->Z / WhitePoint->Z);
155
156 Lab->L = 116.0*fy - 16.0;
157 Lab->a = 500.0*(fx - fy);
158 Lab->b = 200.0*(fy - fz);
159}
160
161
162// Standard XYZ to Lab. It can return negative XYZ in some cases
163void CMSEXPORT cmsLab2XYZ(cmsContext ContextID, const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cmsCIELab* Lab)
164{
165 cmsFloat64Number x, y, z;
166
167 if (WhitePoint == NULL)
168 WhitePoint = cmsD50_XYZ(ContextID);
169
170 y = (Lab-> L + 16.0) / 116.0;
171 x = y + 0.002 * Lab -> a;
172 z = y - 0.005 * Lab -> b;
173
174 xyz -> X = f_1(x) * WhitePoint -> X;
175 xyz -> Y = f_1(y) * WhitePoint -> Y;
176 xyz -> Z = f_1(z) * WhitePoint -> Z;
177
178}
179
180static
181cmsFloat64Number L2float2(cmsUInt16Number v)
182{
183 return (cmsFloat64Number) v / 652.800;
184}
185
186// the a/b part
187static
188cmsFloat64Number ab2float2(cmsUInt16Number v)
189{
190 return ((cmsFloat64Number) v / 256.0) - 128.0;
191}
192
193static
194cmsUInt16Number L2Fix2(cmsFloat64Number L)
195{
196 return _cmsQuickSaturateWord(L * 652.8);
197}
198
199static
200cmsUInt16Number ab2Fix2(cmsFloat64Number ab)
201{
202 return _cmsQuickSaturateWord((ab + 128.0) * 256.0);
203}
204
205
206static
207cmsFloat64Number L2float4(cmsUInt16Number v)
208{
209 return (cmsFloat64Number) v / 655.35;
210}
211
212// the a/b part
213static
214cmsFloat64Number ab2float4(cmsUInt16Number v)
215{
216 return ((cmsFloat64Number) v / 257.0) - 128.0;
217}
218
219
220void CMSEXPORT cmsLabEncoded2FloatV2(cmsContext ContextID, cmsCIELab* Lab, const cmsUInt16Number wLab[3])
221{
222 cmsUNUSED_PARAMETER(ContextID);
223 Lab->L = L2float2(wLab[0]);
224 Lab->a = ab2float2(wLab[1]);
225 Lab->b = ab2float2(wLab[2]);
226}
227
228
229void CMSEXPORT cmsLabEncoded2Float(cmsContext ContextID, cmsCIELab* Lab, const cmsUInt16Number wLab[3])
230{
231 cmsUNUSED_PARAMETER(ContextID);
232 Lab->L = L2float4(wLab[0]);
233 Lab->a = ab2float4(wLab[1]);
234 Lab->b = ab2float4(wLab[2]);
235}
236
237static
238cmsFloat64Number Clamp_L_doubleV2(cmsFloat64Number L)
239{
240 const cmsFloat64Number L_max = (cmsFloat64Number) (0xFFFF * 100.0) / 0xFF00;
241
242 if (L < 0) L = 0;
243 if (L > L_max) L = L_max;
244
245 return L;
246}
247
248
249static
250cmsFloat64Number Clamp_ab_doubleV2(cmsFloat64Number ab)
251{
252 if (ab < MIN_ENCODEABLE_ab2) ab = MIN_ENCODEABLE_ab2;
253 if (ab > MAX_ENCODEABLE_ab2) ab = MAX_ENCODEABLE_ab2;
254
255 return ab;
256}
257
258void CMSEXPORT cmsFloat2LabEncodedV2(cmsContext ContextID, cmsUInt16Number wLab[3], const cmsCIELab* fLab)
259{
260 cmsCIELab Lab;
261 cmsUNUSED_PARAMETER(ContextID);
262
263 Lab.L = Clamp_L_doubleV2(fLab ->L);
264 Lab.a = Clamp_ab_doubleV2(fLab ->a);
265 Lab.b = Clamp_ab_doubleV2(fLab ->b);
266
267 wLab[0] = L2Fix2(Lab.L);
268 wLab[1] = ab2Fix2(Lab.a);
269 wLab[2] = ab2Fix2(Lab.b);
270}
271
272
273static
274cmsFloat64Number Clamp_L_doubleV4(cmsFloat64Number L)
275{
276 if (L < 0) L = 0;
277 if (L > 100.0) L = 100.0;
278
279 return L;
280}
281
282static
283cmsFloat64Number Clamp_ab_doubleV4(cmsFloat64Number ab)
284{
285 if (ab < MIN_ENCODEABLE_ab4) ab = MIN_ENCODEABLE_ab4;
286 if (ab > MAX_ENCODEABLE_ab4) ab = MAX_ENCODEABLE_ab4;
287
288 return ab;
289}
290
291static
292cmsUInt16Number L2Fix4(cmsFloat64Number L)
293{
294 return _cmsQuickSaturateWord(L * 655.35);
295}
296
297static
298cmsUInt16Number ab2Fix4(cmsFloat64Number ab)
299{
300 return _cmsQuickSaturateWord((ab + 128.0) * 257.0);
301}
302
303void CMSEXPORT cmsFloat2LabEncoded(cmsContext ContextID, cmsUInt16Number wLab[3], const cmsCIELab* fLab)
304{
305 cmsCIELab Lab;
306 cmsUNUSED_PARAMETER(ContextID);
307
308 Lab.L = Clamp_L_doubleV4(fLab ->L);
309 Lab.a = Clamp_ab_doubleV4(fLab ->a);
310 Lab.b = Clamp_ab_doubleV4(fLab ->b);
311
312 wLab[0] = L2Fix4(Lab.L);
313 wLab[1] = ab2Fix4(Lab.a);
314 wLab[2] = ab2Fix4(Lab.b);
315}
316
317// Auxiliary: convert to Radians
318static
319cmsFloat64Number RADIANS(cmsFloat64Number deg)
320{
321 return (deg * M_PI) / 180.;
322}
323
324
325// Auxiliary: atan2 but operating in degrees and returning 0 if a==b==0
326static
327cmsFloat64Number atan2deg(cmsFloat64Number a, cmsFloat64Number b)
328{
329 cmsFloat64Number h;
330
331 if (a == 0 && b == 0)
332 h = 0;
333 else
334 h = atan2(a, b);
335
336 h *= (180. / M_PI);
337
338 while (h > 360.)
339 h -= 360.;
340
341 while ( h < 0)
342 h += 360.;
343
344 return h;
345}
346
347
348// Auxiliary: Square
349static
350cmsFloat64Number Sqr(cmsFloat64Number v)
351{
352 return v * v;
353}
354// From cylindrical coordinates. No check is performed, then negative values are allowed
355void CMSEXPORT cmsLab2LCh(cmsContext ContextID, cmsCIELCh* LCh, const cmsCIELab* Lab)
356{
357 cmsUNUSED_PARAMETER(ContextID);
358 LCh -> L = Lab -> L;
359 LCh -> C = pow(Sqr(Lab ->a) + Sqr(Lab ->b), 0.5);
360 LCh -> h = atan2deg(Lab ->b, Lab ->a);
361}
362
363
364// To cylindrical coordinates. No check is performed, then negative values are allowed
365void CMSEXPORT cmsLCh2Lab(cmsContext ContextID, cmsCIELab* Lab, const cmsCIELCh* LCh)
366{
367 cmsFloat64Number h = (LCh -> h * M_PI) / 180.0;
368 cmsUNUSED_PARAMETER(ContextID);
369
370 Lab -> L = LCh -> L;
371 Lab -> a = LCh -> C * cos(h);
372 Lab -> b = LCh -> C * sin(h);
373}
374
375// In XYZ All 3 components are encoded using 1.15 fixed point
376static
377cmsUInt16Number XYZ2Fix(cmsFloat64Number d)
378{
379 return _cmsQuickSaturateWord(d * 32768.0);
380}
381
382void CMSEXPORT cmsFloat2XYZEncoded(cmsContext ContextID, cmsUInt16Number XYZ[3], const cmsCIEXYZ* fXYZ)
383{
384 cmsCIEXYZ xyz;
385 cmsUNUSED_PARAMETER(ContextID);
386
387 xyz.X = fXYZ -> X;
388 xyz.Y = fXYZ -> Y;
389 xyz.Z = fXYZ -> Z;
390
391 // Clamp to encodeable values.
392 if (xyz.Y <= 0) {
393
394 xyz.X = 0;
395 xyz.Y = 0;
396 xyz.Z = 0;
397 }
398
399 if (xyz.X > MAX_ENCODEABLE_XYZ)
400 xyz.X = MAX_ENCODEABLE_XYZ;
401
402 if (xyz.X < 0)
403 xyz.X = 0;
404
405 if (xyz.Y > MAX_ENCODEABLE_XYZ)
406 xyz.Y = MAX_ENCODEABLE_XYZ;
407
408 if (xyz.Y < 0)
409 xyz.Y = 0;
410
411 if (xyz.Z > MAX_ENCODEABLE_XYZ)
412 xyz.Z = MAX_ENCODEABLE_XYZ;
413
414 if (xyz.Z < 0)
415 xyz.Z = 0;
416
417
418 XYZ[0] = XYZ2Fix(xyz.X);
419 XYZ[1] = XYZ2Fix(xyz.Y);
420 XYZ[2] = XYZ2Fix(xyz.Z);
421}
422
423
424// To convert from Fixed 1.15 point to cmsFloat64Number
425static
426cmsFloat64Number XYZ2float(cmsContext ContextID, cmsUInt16Number v)
427{
428 cmsS15Fixed16Number fix32;
429
430 // From 1.15 to 15.16
431 fix32 = v << 1;
432
433 // From fixed 15.16 to cmsFloat64Number
434 return _cms15Fixed16toDouble(ContextID, fix32);
435}
436
437
438void CMSEXPORT cmsXYZEncoded2Float(cmsContext ContextID, cmsCIEXYZ* fXYZ, const cmsUInt16Number XYZ[3])
439{
440 fXYZ -> X = XYZ2float(ContextID, XYZ[0]);
441 fXYZ -> Y = XYZ2float(ContextID, XYZ[1]);
442 fXYZ -> Z = XYZ2float(ContextID, XYZ[2]);
443}
444
445
446// Returns dE on two Lab values
447cmsFloat64Number CMSEXPORT cmsDeltaE(cmsContext ContextID, const cmsCIELab* Lab1, const cmsCIELab* Lab2)
448{
449 cmsFloat64Number dL, da, db;
450 cmsUNUSED_PARAMETER(ContextID);
451
452 dL = fabs(Lab1 -> L - Lab2 -> L);
453 da = fabs(Lab1 -> a - Lab2 -> a);
454 db = fabs(Lab1 -> b - Lab2 -> b);
455
456 return pow(Sqr(dL) + Sqr(da) + Sqr(db), 0.5);
457}
458
459
460// Return the CIE94 Delta E
461cmsFloat64Number CMSEXPORT cmsCIE94DeltaE(cmsContext ContextID, const cmsCIELab* Lab1, const cmsCIELab* Lab2)
462{
463 cmsCIELCh LCh1, LCh2;
464 cmsFloat64Number dE, dL, dC, dh, dhsq;
465 cmsFloat64Number c12, sc, sh;
466
467 dL = fabs(Lab1 ->L - Lab2 ->L);
468
469 cmsLab2LCh(ContextID, &LCh1, Lab1);
470 cmsLab2LCh(ContextID, &LCh2, Lab2);
471
472 dC = fabs(LCh1.C - LCh2.C);
473 dE = cmsDeltaE(ContextID, Lab1, Lab2);
474
475 dhsq = Sqr(dE) - Sqr(dL) - Sqr(dC);
476 if (dhsq < 0)
477 dh = 0;
478 else
479 dh = pow(dhsq, 0.5);
480
481 c12 = sqrt(LCh1.C * LCh2.C);
482
483 sc = 1.0 + (0.048 * c12);
484 sh = 1.0 + (0.014 * c12);
485
486 return sqrt(Sqr(dL) + Sqr(dC) / Sqr(sc) + Sqr(dh) / Sqr(sh));
487}
488
489
490// Auxiliary
491static
492cmsFloat64Number ComputeLBFD(const cmsCIELab* Lab)
493{
494 cmsFloat64Number yt;
495
496 if (Lab->L > 7.996969)
497 yt = (Sqr((Lab->L+16)/116)*((Lab->L+16)/116))*100;
498 else
499 yt = 100 * (Lab->L / 903.3);
500
501 return (54.6 * (M_LOG10E * (log(yt + 1.5))) - 9.6);
502}
503
504
505
506// bfd - gets BFD(1:1) difference between Lab1, Lab2
507cmsFloat64Number CMSEXPORT cmsBFDdeltaE(cmsContext ContextID, const cmsCIELab* Lab1, const cmsCIELab* Lab2)
508{
509 cmsFloat64Number lbfd1,lbfd2,AveC,Aveh,dE,deltaL,
510 deltaC,deltah,dc,t,g,dh,rh,rc,rt,bfd;
511 cmsCIELCh LCh1, LCh2;
512
513
514 lbfd1 = ComputeLBFD(Lab1);
515 lbfd2 = ComputeLBFD(Lab2);
516 deltaL = lbfd2 - lbfd1;
517
518 cmsLab2LCh(ContextID, &LCh1, Lab1);
519 cmsLab2LCh(ContextID, &LCh2, Lab2);
520
521 deltaC = LCh2.C - LCh1.C;
522 AveC = (LCh1.C+LCh2.C)/2;
523 Aveh = (LCh1.h+LCh2.h)/2;
524
525 dE = cmsDeltaE(ContextID, Lab1, Lab2);
526
527 if (Sqr(dE)>(Sqr(Lab2->L-Lab1->L)+Sqr(deltaC)))
528 deltah = sqrt(Sqr(dE)-Sqr(Lab2->L-Lab1->L)-Sqr(deltaC));
529 else
530 deltah =0;
531
532
533 dc = 0.035 * AveC / (1 + 0.00365 * AveC)+0.521;
534 g = sqrt(Sqr(Sqr(AveC))/(Sqr(Sqr(AveC))+14000));
535 t = 0.627+(0.055*cos((Aveh-254)/(180/M_PI))-
536 0.040*cos((2*Aveh-136)/(180/M_PI))+
537 0.070*cos((3*Aveh-31)/(180/M_PI))+
538 0.049*cos((4*Aveh+114)/(180/M_PI))-
539 0.015*cos((5*Aveh-103)/(180/M_PI)));
540
541 dh = dc*(g*t+1-g);
542 rh = -0.260*cos((Aveh-308)/(180/M_PI))-
543 0.379*cos((2*Aveh-160)/(180/M_PI))-
544 0.636*cos((3*Aveh+254)/(180/M_PI))+
545 0.226*cos((4*Aveh+140)/(180/M_PI))-
546 0.194*cos((5*Aveh+280)/(180/M_PI));
547
548 rc = sqrt((AveC*AveC*AveC*AveC*AveC*AveC)/((AveC*AveC*AveC*AveC*AveC*AveC)+70000000));
549 rt = rh*rc;
550
551 bfd = sqrt(Sqr(deltaL)+Sqr(deltaC/dc)+Sqr(deltah/dh)+(rt*(deltaC/dc)*(deltah/dh)));
552
553 return bfd;
554}
555
556
557// cmc - CMC(l:c) difference between Lab1, Lab2
558cmsFloat64Number CMSEXPORT cmsCMCdeltaE(cmsContext ContextID, const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number l, cmsFloat64Number c)
559{
560 cmsFloat64Number dE,dL,dC,dh,sl,sc,sh,t,f,cmc;
561 cmsCIELCh LCh1, LCh2;
562
563 if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
564
565 cmsLab2LCh(ContextID, &LCh1, Lab1);
566 cmsLab2LCh(ContextID, &LCh2, Lab2);
567
568
569 dL = Lab2->L-Lab1->L;
570 dC = LCh2.C-LCh1.C;
571
572 dE = cmsDeltaE(ContextID, Lab1, Lab2);
573
574 if (Sqr(dE)>(Sqr(dL)+Sqr(dC)))
575 dh = sqrt(Sqr(dE)-Sqr(dL)-Sqr(dC));
576 else
577 dh =0;
578
579 if ((LCh1.h > 164) && (LCh1.h < 345))
580 t = 0.56 + fabs(0.2 * cos(((LCh1.h + 168)/(180/M_PI))));
581 else
582 t = 0.36 + fabs(0.4 * cos(((LCh1.h + 35 )/(180/M_PI))));
583
584 sc = 0.0638 * LCh1.C / (1 + 0.0131 * LCh1.C) + 0.638;
585 sl = 0.040975 * Lab1->L /(1 + 0.01765 * Lab1->L);
586
587 if (Lab1->L<16)
588 sl = 0.511;
589
590 f = sqrt((LCh1.C * LCh1.C * LCh1.C * LCh1.C)/((LCh1.C * LCh1.C * LCh1.C * LCh1.C)+1900));
591 sh = sc*(t*f+1-f);
592 cmc = sqrt(Sqr(dL/(l*sl))+Sqr(dC/(c*sc))+Sqr(dh/sh));
593
594 return cmc;
595}
596
597// dE2000 The weightings KL, KC and KH can be modified to reflect the relative
598// importance of lightness, chroma and hue in different industrial applications
599cmsFloat64Number CMSEXPORT cmsCIE2000DeltaE(cmsContext ContextID, const cmsCIELab* Lab1, const cmsCIELab* Lab2,
600 cmsFloat64Number Kl, cmsFloat64Number Kc, cmsFloat64Number Kh)
601{
602 cmsFloat64Number L1 = Lab1->L;
603 cmsFloat64Number a1 = Lab1->a;
604 cmsFloat64Number b1 = Lab1->b;
605 cmsFloat64Number C = sqrt( Sqr(a1) + Sqr(b1) );
606
607 cmsFloat64Number Ls = Lab2 ->L;
608 cmsFloat64Number as = Lab2 ->a;
609 cmsFloat64Number bs = Lab2 ->b;
610 cmsFloat64Number Cs = sqrt( Sqr(as) + Sqr(bs) );
611
612 cmsFloat64Number G = 0.5 * ( 1 - sqrt(pow((C + Cs) / 2 , 7.0) / (pow((C + Cs) / 2, 7.0) + pow(25.0, 7.0) ) ));
613
614 cmsFloat64Number a_p = (1 + G ) * a1;
615 cmsFloat64Number b_p = b1;
616 cmsFloat64Number C_p = sqrt( Sqr(a_p) + Sqr(b_p));
617 cmsFloat64Number h_p = atan2deg(b_p, a_p);
618
619
620 cmsFloat64Number a_ps = (1 + G) * as;
621 cmsFloat64Number b_ps = bs;
622 cmsFloat64Number C_ps = sqrt(Sqr(a_ps) + Sqr(b_ps));
623 cmsFloat64Number h_ps = atan2deg(b_ps, a_ps);
624
625 cmsFloat64Number meanC_p =(C_p + C_ps) / 2;
626
627 cmsFloat64Number hps_plus_hp = h_ps + h_p;
628 cmsFloat64Number hps_minus_hp = h_ps - h_p;
629
630 cmsFloat64Number meanh_p = fabs(hps_minus_hp) <= 180.000001 ? (hps_plus_hp)/2 :
631 (hps_plus_hp) < 360 ? (hps_plus_hp + 360)/2 :
632 (hps_plus_hp - 360)/2;
633
634 cmsFloat64Number delta_h = (hps_minus_hp) <= -180.000001 ? (hps_minus_hp + 360) :
635 (hps_minus_hp) > 180 ? (hps_minus_hp - 360) :
636 (hps_minus_hp);
637 cmsFloat64Number delta_L = (Ls - L1);
638 cmsFloat64Number delta_C = (C_ps - C_p );
639
640
641 cmsFloat64Number delta_H =2 * sqrt(C_ps*C_p) * sin(RADIANS(delta_h) / 2);
642
643 cmsFloat64Number T = 1 - 0.17 * cos(RADIANS(meanh_p-30))
644 + 0.24 * cos(RADIANS(2*meanh_p))
645 + 0.32 * cos(RADIANS(3*meanh_p + 6))
646 - 0.2 * cos(RADIANS(4*meanh_p - 63));
647
648 cmsFloat64Number Sl = 1 + (0.015 * Sqr((Ls + L1) /2- 50) )/ sqrt(20 + Sqr( (Ls+L1)/2 - 50) );
649
650 cmsFloat64Number Sc = 1 + 0.045 * (C_p + C_ps)/2;
651 cmsFloat64Number Sh = 1 + 0.015 * ((C_ps + C_p)/2) * T;
652
653 cmsFloat64Number delta_ro = 30 * exp( -Sqr(((meanh_p - 275 ) / 25)));
654
655 cmsFloat64Number Rc = 2 * sqrt(( pow(meanC_p, 7.0) )/( pow(meanC_p, 7.0) + pow(25.0, 7.0)));
656
657 cmsFloat64Number Rt = -sin(2 * RADIANS(delta_ro)) * Rc;
658
659 cmsFloat64Number deltaE00 = sqrt( Sqr(delta_L /(Sl * Kl)) +
660 Sqr(delta_C/(Sc * Kc)) +
661 Sqr(delta_H/(Sh * Kh)) +
662 Rt*(delta_C/(Sc * Kc)) * (delta_H / (Sh * Kh)));
663
664 cmsUNUSED_PARAMETER(ContextID);
665
666 return deltaE00;
667}
668
669// This function returns a number of gridpoints to be used as LUT table. It assumes same number
670// of gripdpoints in all dimensions. Flags may override the choice.
671cmsUInt32Number _cmsReasonableGridpointsByColorspace(cmsContext ContextID, cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags)
672{
673 cmsUInt32Number nChannels;
674
675 // Already specified?
676 if (dwFlags & 0x00FF0000) {
677 // Yes, grab'em
678 return (dwFlags >> 16) & 0xFF;
679 }
680
681 nChannels = cmsChannelsOf(ContextID, Colorspace);
682
683 // HighResPrecalc is maximum resolution
684 if (dwFlags & cmsFLAGS_HIGHRESPRECALC) {
685
686 if (nChannels > 4)
687 return 7; // 7 for Hifi
688
689 if (nChannels == 4) // 23 for CMYK
690 return 23;
691
692 return 49; // 49 for RGB and others
693 }
694
695
696 // LowResPrecal is lower resolution
697 if (dwFlags & cmsFLAGS_LOWRESPRECALC) {
698
699 if (nChannels > 4)
700 return 6; // 6 for more than 4 channels
701
702 if (nChannels == 1)
703 return 33; // For monochrome
704
705 return 17; // 17 for remaining
706 }
707
708 // Default values
709 if (nChannels > 4)
710 return 7; // 7 for Hifi
711
712 if (nChannels == 4)
713 return 17; // 17 for CMYK
714
715 return 33; // 33 for RGB
716}
717
718
719cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space,
720 cmsUInt16Number **White,
721 cmsUInt16Number **Black,
722 cmsUInt32Number *nOutputs)
723{
724 // Only most common spaces
725
726 static cmsUInt16Number RGBblack[4] = { 0, 0, 0 };
727 static cmsUInt16Number RGBwhite[4] = { 0xffff, 0xffff, 0xffff };
728 static cmsUInt16Number CMYKblack[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; // 400% of ink
729 static cmsUInt16Number CMYKwhite[4] = { 0, 0, 0, 0 };
730 static cmsUInt16Number LABblack[4] = { 0, 0x8080, 0x8080 }; // V4 Lab encoding
731 static cmsUInt16Number LABwhite[4] = { 0xFFFF, 0x8080, 0x8080 };
732 static cmsUInt16Number CMYblack[4] = { 0xffff, 0xffff, 0xffff };
733 static cmsUInt16Number CMYwhite[4] = { 0, 0, 0 };
734 static cmsUInt16Number Grayblack[4] = { 0 };
735 static cmsUInt16Number GrayWhite[4] = { 0xffff };
736
737 switch (Space) {
738
739 case cmsSigGrayData: if (White) *White = GrayWhite;
740 if (Black) *Black = Grayblack;
741 if (nOutputs) *nOutputs = 1;
742 return TRUE;
743
744 case cmsSigRgbData: if (White) *White = RGBwhite;
745 if (Black) *Black = RGBblack;
746 if (nOutputs) *nOutputs = 3;
747 return TRUE;
748
749 case cmsSigLabData: if (White) *White = LABwhite;
750 if (Black) *Black = LABblack;
751 if (nOutputs) *nOutputs = 3;
752 return TRUE;
753
754 case cmsSigCmykData: if (White) *White = CMYKwhite;
755 if (Black) *Black = CMYKblack;
756 if (nOutputs) *nOutputs = 4;
757 return TRUE;
758
759 case cmsSigCmyData: if (White) *White = CMYwhite;
760 if (Black) *Black = CMYblack;
761 if (nOutputs) *nOutputs = 3;
762 return TRUE;
763
764 default:;
765 }
766
767 return FALSE;
768}
769
770
771
772// Several utilities -------------------------------------------------------
773
774// Translate from our colorspace to ICC representation
775
776cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(cmsContext ContextID, int OurNotation)
777{
778 switch (OurNotation) {
779
780 case 1:
781 case PT_GRAY: return cmsSigGrayData;
782
783 case 2:
784 case PT_RGB: return cmsSigRgbData;
785
786 case PT_CMY: return cmsSigCmyData;
787 case PT_CMYK: return cmsSigCmykData;
788 case PT_YCbCr:return cmsSigYCbCrData;
789 case PT_YUV: return cmsSigLuvData;
790 case PT_XYZ: return cmsSigXYZData;
791
792 case PT_LabV2:
793 case PT_Lab: return cmsSigLabData;
794
795 case PT_YUVK: return cmsSigLuvKData;
796 case PT_HSV: return cmsSigHsvData;
797 case PT_HLS: return cmsSigHlsData;
798 case PT_Yxy: return cmsSigYxyData;
799
800 case PT_MCH1: return cmsSigMCH1Data;
801 case PT_MCH2: return cmsSigMCH2Data;
802 case PT_MCH3: return cmsSigMCH3Data;
803 case PT_MCH4: return cmsSigMCH4Data;
804 case PT_MCH5: return cmsSigMCH5Data;
805 case PT_MCH6: return cmsSigMCH6Data;
806 case PT_MCH7: return cmsSigMCH7Data;
807 case PT_MCH8: return cmsSigMCH8Data;
808
809 case PT_MCH9: return cmsSigMCH9Data;
810 case PT_MCH10: return cmsSigMCHAData;
811 case PT_MCH11: return cmsSigMCHBData;
812 case PT_MCH12: return cmsSigMCHCData;
813 case PT_MCH13: return cmsSigMCHDData;
814 case PT_MCH14: return cmsSigMCHEData;
815 case PT_MCH15: return cmsSigMCHFData;
816
817 default: return (cmsColorSpaceSignature) 0;
818 }
819 cmsUNUSED_PARAMETER(ContextID);
820}
821
822
823int CMSEXPORT _cmsLCMScolorSpace(cmsContext ContextID, cmsColorSpaceSignature ProfileSpace)
824{
825 cmsUNUSED_PARAMETER(ContextID);
826 switch (ProfileSpace) {
827
828 case cmsSigGrayData: return PT_GRAY;
829 case cmsSigRgbData: return PT_RGB;
830 case cmsSigCmyData: return PT_CMY;
831 case cmsSigCmykData: return PT_CMYK;
832 case cmsSigYCbCrData:return PT_YCbCr;
833 case cmsSigLuvData: return PT_YUV;
834 case cmsSigXYZData: return PT_XYZ;
835 case cmsSigLabData: return PT_Lab;
836 case cmsSigLuvKData: return PT_YUVK;
837 case cmsSigHsvData: return PT_HSV;
838 case cmsSigHlsData: return PT_HLS;
839 case cmsSigYxyData: return PT_Yxy;
840
841 case cmsSig1colorData:
842 case cmsSigMCH1Data: return PT_MCH1;
843
844 case cmsSig2colorData:
845 case cmsSigMCH2Data: return PT_MCH2;
846
847 case cmsSig3colorData:
848 case cmsSigMCH3Data: return PT_MCH3;
849
850 case cmsSig4colorData:
851 case cmsSigMCH4Data: return PT_MCH4;
852
853 case cmsSig5colorData:
854 case cmsSigMCH5Data: return PT_MCH5;
855
856 case cmsSig6colorData:
857 case cmsSigMCH6Data: return PT_MCH6;
858
859 case cmsSigMCH7Data:
860 case cmsSig7colorData:return PT_MCH7;
861
862 case cmsSigMCH8Data:
863 case cmsSig8colorData:return PT_MCH8;
864
865 case cmsSigMCH9Data:
866 case cmsSig9colorData:return PT_MCH9;
867
868 case cmsSigMCHAData:
869 case cmsSig10colorData:return PT_MCH10;
870
871 case cmsSigMCHBData:
872 case cmsSig11colorData:return PT_MCH11;
873
874 case cmsSigMCHCData:
875 case cmsSig12colorData:return PT_MCH12;
876
877 case cmsSigMCHDData:
878 case cmsSig13colorData:return PT_MCH13;
879
880 case cmsSigMCHEData:
881 case cmsSig14colorData:return PT_MCH14;
882
883 case cmsSigMCHFData:
884 case cmsSig15colorData:return PT_MCH15;
885
886 default: return (cmsColorSpaceSignature) 0;
887 }
888}
889
890
891cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsContext ContextID, cmsColorSpaceSignature ColorSpace)
892{
893 cmsUNUSED_PARAMETER(ContextID);
894 switch (ColorSpace) {
895
896 case cmsSigMCH1Data:
897 case cmsSig1colorData:
898 case cmsSigGrayData: return 1;
899
900 case cmsSigMCH2Data:
901 case cmsSig2colorData: return 2;
902
903 case cmsSigXYZData:
904 case cmsSigLabData:
905 case cmsSigLuvData:
906 case cmsSigYCbCrData:
907 case cmsSigYxyData:
908 case cmsSigRgbData:
909 case cmsSigHsvData:
910 case cmsSigHlsData:
911 case cmsSigCmyData:
912 case cmsSigMCH3Data:
913 case cmsSig3colorData: return 3;
914
915 case cmsSigLuvKData:
916 case cmsSigCmykData:
917 case cmsSigMCH4Data:
918 case cmsSig4colorData: return 4;
919
920 case cmsSigMCH5Data:
921 case cmsSig5colorData: return 5;
922
923 case cmsSigMCH6Data:
924 case cmsSig6colorData: return 6;
925
926 case cmsSigMCH7Data:
927 case cmsSig7colorData: return 7;
928
929 case cmsSigMCH8Data:
930 case cmsSig8colorData: return 8;
931
932 case cmsSigMCH9Data:
933 case cmsSig9colorData: return 9;
934
935 case cmsSigMCHAData:
936 case cmsSig10colorData: return 10;
937
938 case cmsSigMCHBData:
939 case cmsSig11colorData: return 11;
940
941 case cmsSigMCHCData:
942 case cmsSig12colorData: return 12;
943
944 case cmsSigMCHDData:
945 case cmsSig13colorData: return 13;
946
947 case cmsSigMCHEData:
948 case cmsSig14colorData: return 14;
949
950 case cmsSigMCHFData:
951 case cmsSig15colorData: return 15;
952
953 default: return 3;
954 }
955}
956