MagickCore 7.1.1
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
colorspace-private.h
1/*
2 Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization
3 dedicated to making software imaging solutions freely available.
4
5 You may not use this file except in compliance with the License. You may
6 obtain a copy of the License at
7
8 https://imagemagick.org/script/license.php
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15
16 MagickCore image colorspace private methods.
17*/
18#ifndef MAGICKCORE_COLORSPACE_PRIVATE_H
19#define MAGICKCORE_COLORSPACE_PRIVATE_H
20
21#include "MagickCore/image.h"
22#include "MagickCore/image-private.h"
23#include "MagickCore/pixel.h"
24#include "MagickCore/pixel-accessor.h"
25
26#define IlluminantX 0.95047
27#define IlluminantY 1.0
28#define IlluminantZ 1.08883
29#define CIEEpsilon (216.0/24389.0)
30#define CIEK (24389.0/27.0)
31
32static const PrimaryInfo
33 illuminant_tristimulus[] =
34 {
35 { 1.09850, 1.00000, 0.35585 }, /* A */
36 { 0.99072, 1.00000, 0.85223 }, /* B */
37 { 0.98074, 1.00000, 1.18232 }, /* C */
38 { 0.96422, 1.00000, 0.82521 }, /* D50 */
39 { 0.95682, 1.00000, 0.92149 }, /* D55 */
40 { 0.95047, 1.00000, 1.08883 }, /* D65 */
41 { 0.94972, 1.00000, 1.22638 }, /* D75 */
42 { 1.00000, 1.00000, 1.00000 }, /* E */
43 { 0.99186, 1.00000, 0.67393 }, /* F2 */
44 { 0.95041, 1.00000, 1.08747 }, /* F7 */
45 { 1.00962, 1.00000, 0.64350 } /* F11 */
46 };
47
48#if defined(__cplusplus) || defined(c_plusplus)
49extern "C" {
50#endif
51
52
53static inline void ConvertAdobe98ToXYZ(const double red,const double green,
54 const double blue,double *X,double *Y,double *Z)
55{
56 double
57 b,
58 g,
59 r;
60
61 /*
62 Convert Adobe '98 to XYZ colorspace.
63 */
64 r=QuantumScale*DecodePixelGamma((double) QuantumRange*red);
65 g=QuantumScale*DecodePixelGamma((double) QuantumRange*green);
66 b=QuantumScale*DecodePixelGamma((double) QuantumRange*blue);
67 *X=0.57666904291013050*r+0.18555823790654630*g+0.18822864623499470*b;
68 *Y=0.29734497525053605*r+0.62736356625546610*g+0.07529145849399788*b;
69 *Z=0.02703136138641234*r+0.07068885253582723*g+0.99133753683763880*b;
70}
71
72static inline void ConvertXYZToRGB(const double X,const double Y,const double Z,
73 double *red,double *green,double *blue)
74{
75 double
76 b,
77 g,
78 min,
79 r;
80
81 r=(3.240969941904521*X)+(-1.537383177570093*Y)+(-0.498610760293*Z);
82 g=(-0.96924363628087*X)+(1.87596750150772*Y)+(0.041555057407175*Z);
83 b=(0.055630079696993*X)+(-0.20397695888897*Y)+(1.056971514242878*Z);
84 min=MagickMin(r,MagickMin(g,b));
85 if (min < 0.0)
86 {
87 r-=min;
88 g-=min;
89 b-=min;
90 }
91 *red=EncodePixelGamma((double) QuantumRange*r);
92 *green=EncodePixelGamma((double) QuantumRange*g);
93 *blue=EncodePixelGamma((double) QuantumRange*b);
94}
95
96static inline void ConvertAdobe98ToRGB(const double r,const double g,
97 const double b,double *red,double *green,double *blue)
98{
99 double
100 X,
101 Y,
102 Z;
103
104 ConvertAdobe98ToXYZ(r,g,b,&X,&Y,&Z);
105 ConvertXYZToRGB(X,Y,Z,red,green,blue);
106}
107
108static inline void ConvertCAT02LMSToXYZ(const double L,const double M,
109 const double S,double *X,double *Y,double *Z)
110{
111 /*
112 Convert CAT02LMS to XYZ colorspace.
113 */
114 *X=1.096123820835514*L-0.278869000218287*M+0.182745179382773*S;
115 *Y=0.454369041975359*L+0.473533154307412*M+0.072097803717229*S;
116 *Z=(-0.009627608738429)*L-0.005698031216113*M+1.015325639954543*S;
117}
118
119static inline void ConvertCAT02LMSToRGB(const double L,const double M,
120 const double S,double *R,double *G,double *B)
121{
122 double
123 X,
124 Y,
125 Z;
126
127 ConvertCAT02LMSToXYZ(L,M,S,&X,&Y,&Z);
128 ConvertXYZToRGB(X,Y,Z,R,G,B);
129}
130
131static inline void ConvertCMYKToRGB(PixelInfo *pixel)
132{
133 pixel->red=(((double) QuantumRange-(QuantumScale*pixel->red*
134 ((double) QuantumRange-pixel->black)+pixel->black)));
135 pixel->green=(((double) QuantumRange-(QuantumScale*pixel->green*
136 ((double) QuantumRange-pixel->black)+pixel->black)));
137 pixel->blue=(((double) QuantumRange-(QuantumScale*pixel->blue*
138 ((double) QuantumRange-pixel->black)+pixel->black)));
139}
140
141static inline void ConvertCMYToRGB(const double cyan,const double magenta,
142 const double yellow,double *red,double *green,double *blue)
143{
144 *red=(double) QuantumRange*(1.0-cyan);
145 *green=(double) QuantumRange*(1.0-magenta);
146 *blue=(double) QuantumRange*(1.0-yellow);
147}
148
149static inline void ConvertHCLToRGB(const double hue,const double chroma,
150 const double luma,double *red,double *green,double *blue)
151{
152 double
153 b,
154 c,
155 g,
156 h,
157 m,
158 r,
159 x;
160
161 /*
162 Convert HCL to RGB colorspace.
163 */
164 assert(red != (double *) NULL);
165 assert(green != (double *) NULL);
166 assert(blue != (double *) NULL);
167 h=6.0*hue;
168 c=chroma;
169 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
170 r=0.0;
171 g=0.0;
172 b=0.0;
173 if ((0.0 <= h) && (h < 1.0))
174 {
175 r=c;
176 g=x;
177 }
178 else
179 if ((1.0 <= h) && (h < 2.0))
180 {
181 r=x;
182 g=c;
183 }
184 else
185 if ((2.0 <= h) && (h < 3.0))
186 {
187 g=c;
188 b=x;
189 }
190 else
191 if ((3.0 <= h) && (h < 4.0))
192 {
193 g=x;
194 b=c;
195 }
196 else
197 if ((4.0 <= h) && (h < 5.0))
198 {
199 r=x;
200 b=c;
201 }
202 else
203 if ((5.0 <= h) && (h < 6.0))
204 {
205 r=c;
206 b=x;
207 }
208 m=luma-(0.298839*r+0.586811*g+0.114350*b);
209 *red=(double) QuantumRange*(r+m);
210 *green=(double) QuantumRange*(g+m);
211 *blue=(double) QuantumRange*(b+m);
212}
213
214static inline void ConvertHCLpToRGB(const double hue,const double chroma,
215 const double luma,double *red,double *green,double *blue)
216{
217 double
218 b,
219 c,
220 g,
221 h,
222 m,
223 r,
224 x,
225 z;
226
227 /*
228 Convert HCLp to RGB colorspace.
229 */
230 assert(red != (double *) NULL);
231 assert(green != (double *) NULL);
232 assert(blue != (double *) NULL);
233 h=6.0*hue;
234 c=chroma;
235 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
236 r=0.0;
237 g=0.0;
238 b=0.0;
239 if ((0.0 <= h) && (h < 1.0))
240 {
241 r=c;
242 g=x;
243 }
244 else
245 if ((1.0 <= h) && (h < 2.0))
246 {
247 r=x;
248 g=c;
249 }
250 else
251 if ((2.0 <= h) && (h < 3.0))
252 {
253 g=c;
254 b=x;
255 }
256 else
257 if ((3.0 <= h) && (h < 4.0))
258 {
259 g=x;
260 b=c;
261 }
262 else
263 if ((4.0 <= h) && (h < 5.0))
264 {
265 r=x;
266 b=c;
267 }
268 else
269 if ((5.0 <= h) && (h < 6.0))
270 {
271 r=c;
272 b=x;
273 }
274 m=luma-(0.298839*r+0.586811*g+0.114350*b);
275 z=1.0;
276 if (m < 0.0)
277 {
278 z=luma/(luma-m);
279 m=0.0;
280 }
281 else
282 if (m+c > 1.0)
283 {
284 z=(1.0-luma)/(m+c-luma);
285 m=1.0-z*c;
286 }
287 *red=(double) QuantumRange*(z*r+m);
288 *green=(double) QuantumRange*(z*g+m);
289 *blue=(double) QuantumRange*(z*b+m);
290}
291
292static inline void ConvertHSBToRGB(const double hue,const double saturation,
293 const double brightness,double *red,double *green,double *blue)
294{
295 double
296 f,
297 h,
298 p,
299 q,
300 t;
301
302 /*
303 Convert HSB to RGB colorspace.
304 */
305 assert(red != (double *) NULL);
306 assert(green != (double *) NULL);
307 assert(blue != (double *) NULL);
308 if (fabs(saturation) < MagickEpsilon)
309 {
310 *red=(double) QuantumRange*brightness;
311 *green=(*red);
312 *blue=(*red);
313 return;
314 }
315 h=6.0*(hue-floor(hue));
316 f=h-floor((double) h);
317 p=brightness*(1.0-saturation);
318 q=brightness*(1.0-saturation*f);
319 t=brightness*(1.0-(saturation*(1.0-f)));
320 switch ((int) h)
321 {
322 case 0:
323 default:
324 {
325 *red=(double) QuantumRange*brightness;
326 *green=(double) QuantumRange*t;
327 *blue=(double) QuantumRange*p;
328 break;
329 }
330 case 1:
331 {
332 *red=(double) QuantumRange*q;
333 *green=(double) QuantumRange*brightness;
334 *blue=(double) QuantumRange*p;
335 break;
336 }
337 case 2:
338 {
339 *red=(double) QuantumRange*p;
340 *green=(double) QuantumRange*brightness;
341 *blue=(double) QuantumRange*t;
342 break;
343 }
344 case 3:
345 {
346 *red=(double) QuantumRange*p;
347 *green=(double) QuantumRange*q;
348 *blue=(double) QuantumRange*brightness;
349 break;
350 }
351 case 4:
352 {
353 *red=(double) QuantumRange*t;
354 *green=(double) QuantumRange*p;
355 *blue=(double) QuantumRange*brightness;
356 break;
357 }
358 case 5:
359 {
360 *red=(double) QuantumRange*brightness;
361 *green=(double) QuantumRange*p;
362 *blue=(double) QuantumRange*q;
363 break;
364 }
365 }
366}
367
368static inline void ConvertHSIToRGB(const double hue,const double saturation,
369 const double intensity,double *red,double *green,double *blue)
370{
371 double
372 b,
373 g,
374 h,
375 r;
376
377 /*
378 Convert HSI to RGB colorspace.
379 */
380 assert(red != (double *) NULL);
381 assert(green != (double *) NULL);
382 assert(blue != (double *) NULL);
383 h=360.0*hue;
384 h-=360.0*floor(h/360.0);
385 if (h < 120.0)
386 {
387 b=intensity*(1.0-saturation);
388 r=intensity*(1.0+saturation*cos(h*(MagickPI/180.0))/cos((60.0-h)*
389 (MagickPI/180.0)));
390 g=3.0*intensity-r-b;
391 }
392 else
393 if (h < 240.0)
394 {
395 h-=120.0;
396 r=intensity*(1.0-saturation);
397 g=intensity*(1.0+saturation*cos(h*(MagickPI/180.0))/cos((60.0-h)*
398 (MagickPI/180.0)));
399 b=3.0*intensity-r-g;
400 }
401 else
402 {
403 h-=240.0;
404 g=intensity*(1.0-saturation);
405 b=intensity*(1.0+saturation*cos(h*(MagickPI/180.0))/cos((60.0-h)*
406 (MagickPI/180.0)));
407 r=3.0*intensity-g-b;
408 }
409 *red=(double) QuantumRange*r;
410 *green=(double) QuantumRange*g;
411 *blue=(double) QuantumRange*b;
412}
413
414static inline void ConvertHSVToRGB(const double hue,const double saturation,
415 const double value,double *red,double *green,double *blue)
416{
417 double
418 c,
419 h,
420 min,
421 x;
422
423 /*
424 Convert HSV to RGB colorspace.
425 */
426 assert(red != (double *) NULL);
427 assert(green != (double *) NULL);
428 assert(blue != (double *) NULL);
429 h=hue*360.0;
430 c=value*saturation;
431 min=value-c;
432 h-=360.0*floor(h/360.0);
433 h/=60.0;
434 x=c*(1.0-fabs(h-2.0*floor(h/2.0)-1.0));
435 switch ((int) floor(h))
436 {
437 case 0:
438 default:
439 {
440 *red=(double) QuantumRange*(min+c);
441 *green=(double) QuantumRange*(min+x);
442 *blue=(double) QuantumRange*min;
443 break;
444 }
445 case 1:
446 {
447 *red=(double) QuantumRange*(min+x);
448 *green=(double) QuantumRange*(min+c);
449 *blue=(double) QuantumRange*min;
450 break;
451 }
452 case 2:
453 {
454 *red=(double) QuantumRange*min;
455 *green=(double) QuantumRange*(min+c);
456 *blue=(double) QuantumRange*(min+x);
457 break;
458 }
459 case 3:
460 {
461 *red=(double) QuantumRange*min;
462 *green=(double) QuantumRange*(min+x);
463 *blue=(double) QuantumRange*(min+c);
464 break;
465 }
466 case 4:
467 {
468 *red=(double) QuantumRange*(min+x);
469 *green=(double) QuantumRange*min;
470 *blue=(double) QuantumRange*(min+c);
471 break;
472 }
473 case 5:
474 {
475 *red=(double) QuantumRange*(min+c);
476 *green=(double) QuantumRange*min;
477 *blue=(double) QuantumRange*(min+x);
478 break;
479 }
480 }
481}
482
483static inline void ConvertHWBToRGB(const double hue,const double whiteness,
484 const double blackness,double *red,double *green,double *blue)
485{
486 double
487 b,
488 f,
489 g,
490 n,
491 r,
492 v;
493
494 ssize_t
495 i;
496
497 /*
498 Convert HWB to RGB colorspace.
499 */
500 assert(red != (double *) NULL);
501 assert(green != (double *) NULL);
502 assert(blue != (double *) NULL);
503 v=1.0-blackness;
504 if (fabs(hue-(-1.0)) < MagickEpsilon)
505 {
506 *red=(double) QuantumRange*v;
507 *green=(double) QuantumRange*v;
508 *blue=(double) QuantumRange*v;
509 return;
510 }
511 i=CastDoubleToLong(floor(6.0*hue));
512 f=6.0*hue-i;
513 if ((i & 0x01) != 0)
514 f=1.0-f;
515 n=whiteness+f*(v-whiteness); /* linear interpolation */
516 switch (i)
517 {
518 case 0:
519 default: r=v; g=n; b=whiteness; break;
520 case 1: r=n; g=v; b=whiteness; break;
521 case 2: r=whiteness; g=v; b=n; break;
522 case 3: r=whiteness; g=n; b=v; break;
523 case 4: r=n; g=whiteness; b=v; break;
524 case 5: r=v; g=whiteness; b=n; break;
525 }
526 *red=(double) QuantumRange*r;
527 *green=(double) QuantumRange*g;
528 *blue=(double) QuantumRange*b;
529}
530
531static inline void ConvertLabToXYZ(const double L,const double a,const double b,
532 const IlluminantType illuminant,double *X,double *Y,double *Z)
533{
534 double
535 x,
536 y,
537 z;
538
539 y=(L+16.0)/116.0;
540 x=y+a/500.0;
541 z=y-b/200.0;
542 if ((x*x*x) > CIEEpsilon)
543 x=(x*x*x);
544 else
545 x=(116.0*x-16.0)/CIEK;
546 if (L > (CIEK*CIEEpsilon))
547 y=(y*y*y);
548 else
549 y=L/CIEK;
550 if ((z*z*z) > CIEEpsilon)
551 z=(z*z*z);
552 else
553 z=(116.0*z-16.0)/CIEK;
554 *X=illuminant_tristimulus[illuminant].x*x;
555 *Y=illuminant_tristimulus[illuminant].y*y;
556 *Z=illuminant_tristimulus[illuminant].z*z;
557}
558
559static inline void ConvertLabToRGB(const double L,const double a,
560 const double b,const IlluminantType illuminant,double *red,double *green,
561 double *blue)
562{
563 double
564 X,
565 Y,
566 Z;
567
568 ConvertLabToXYZ(100.0*L,255.0*(a-0.5),255.0*(b-0.5),illuminant,&X,&Y,&Z);
569 ConvertXYZToRGB(X,Y,Z,red,green,blue);
570}
571
572static inline void ConvertLCHabToXYZ(const double luma,const double chroma,
573 const double hue,const IlluminantType illuminant,double *X,double *Y,
574 double *Z)
575{
576 ConvertLabToXYZ(luma,chroma*cos(DegreesToRadians(hue)),chroma*
577 sin(DegreesToRadians(hue)),illuminant,X,Y,Z);
578}
579
580static inline void ConvertLCHabToRGB(const double luma,const double chroma,
581 const double hue,const IlluminantType illuminant,double *red,double *green,
582 double *blue)
583{
584 double
585 X,
586 Y,
587 Z;
588
589 /*
590 Convert LCHab to RGB colorspace.
591 */
592 assert(red != (double *) NULL);
593 assert(green != (double *) NULL);
594 assert(blue != (double *) NULL);
595 ConvertLCHabToXYZ(100.0*luma,255.0*(chroma-0.5),360.0*hue,illuminant,
596 &X,&Y,&Z);
597 ConvertXYZToRGB(X,Y,Z,red,green,blue);
598}
599
600static inline void ConvertLuvToXYZ(const double L,const double u,const double v,
601 const IlluminantType illuminant,double *X,double *Y,double *Z)
602{
603 double
604 gamma;
605
606 if (L > (CIEK*CIEEpsilon))
607 *Y=(double) pow((L+16.0)/116.0,3.0);
608 else
609 *Y=L/CIEK;
610 gamma=PerceptibleReciprocal((((52.0*L*PerceptibleReciprocal(u+13.0*L*
611 (4.0*illuminant_tristimulus[illuminant].x/
612 (illuminant_tristimulus[illuminant].x+15.0*
613 illuminant_tristimulus[illuminant].y+3.0*
614 illuminant_tristimulus[illuminant].z))))-1.0)/3.0)-(-1.0/3.0));
615 *X=gamma*((*Y*((39.0*L*PerceptibleReciprocal(v+13.0*L*(9.0*
616 illuminant_tristimulus[illuminant].y/
617 (illuminant_tristimulus[illuminant].x+15.0*
618 illuminant_tristimulus[illuminant].y+3.0*
619 illuminant_tristimulus[illuminant].z))))-5.0))+5.0*(*Y));
620 *Z=(*X*(((52.0*L*PerceptibleReciprocal(u+13.0*L*(4.0*
621 illuminant_tristimulus[illuminant].x/
622 (illuminant_tristimulus[illuminant].x+15.0*
623 illuminant_tristimulus[illuminant].y+3.0*
624 illuminant_tristimulus[illuminant].z))))-1.0)/3.0))-5.0*(*Y);
625}
626
627static inline void ConvertLCHuvToXYZ(const double luma,const double chroma,
628 const double hue,const IlluminantType illuminant,double *X,double *Y,
629 double *Z)
630{
631 ConvertLuvToXYZ(luma,chroma*cos(DegreesToRadians(hue)),chroma*
632 sin(DegreesToRadians(hue)),illuminant,X,Y,Z);
633}
634
635static inline void ConvertLCHuvToRGB(const double luma,const double chroma,
636 const double hue,const IlluminantType illuminant,double *red,double *green,
637 double *blue)
638{
639 double
640 X,
641 Y,
642 Z;
643
644 /*
645 Convert LCHuv to RGB colorspace.
646 */
647 assert(red != (double *) NULL);
648 assert(green != (double *) NULL);
649 assert(blue != (double *) NULL);
650 ConvertLCHuvToXYZ(100.0*luma,255.0*(chroma-0.5),360.0*hue,illuminant,
651 &X,&Y,&Z);
652 ConvertXYZToRGB(X,Y,Z,red,green,blue);
653}
654
655static inline void ConvertLMSToXYZ(const double L,const double M,const double S,
656 double *X,double *Y,double *Z)
657{
658 *X=1.096123820835514*L-0.278869000218287*M+0.182745179382773*S;
659 *Y=0.454369041975359*L+0.473533154307412*M+0.072097803717229*S;
660 *Z=(-0.009627608738429)*L-0.005698031216113*M+1.015325639954543*S;
661}
662
663static inline void ConvertLMSToRGB(const double L,const double M,
664 const double S,double *red,double *green,double *blue)
665{
666 double
667 X,
668 Y,
669 Z;
670
671 ConvertLMSToXYZ(L,M,S,&X,&Y,&Z);
672 ConvertXYZToRGB(X,Y,Z,red,green,blue);
673}
674
675static inline void ConvertDisplayP3ToXYZ(const double red,const double green,
676 const double blue,double *X,double *Y,double *Z)
677{
678 double
679 b,
680 g,
681 r;
682
683 /*
684 Convert Display P3 to XYZ colorspace.
685 */
686 r=QuantumScale*DecodePixelGamma((double) QuantumRange*red);
687 g=QuantumScale*DecodePixelGamma((double) QuantumRange*green);
688 b=QuantumScale*DecodePixelGamma((double) QuantumRange*blue);
689 *X=0.4865709486482162*r+0.26566769316909306*g+0.1982172852343625*b;
690 *Y=0.2289745640697488*r+0.69173852183650640*g+0.0792869140937450*b;
691 *Z=0.0000000000000000*r+0.04511338185890264*g+1.0439443689009760*b;
692}
693
694static inline void ConvertDisplayP3ToRGB(const double r,const double g,
695 const double b,double *red,double *green,double *blue)
696{
697 double
698 X,
699 Y,
700 Z;
701
702 ConvertDisplayP3ToXYZ(r,g,b,&X,&Y,&Z);
703 ConvertXYZToRGB(X,Y,Z,red,green,blue);
704}
705
706static inline void ConvertLuvToRGB(const double L,const double u,
707 const double v,const IlluminantType illuminant,double *red,double *green,
708 double *blue)
709{
710 double
711 X,
712 Y,
713 Z;
714
715 ConvertLuvToXYZ(100.0*L,354.0*u-134.0,262.0*v-140.0,illuminant,&X,&Y,&Z);
716 ConvertXYZToRGB(X,Y,Z,red,green,blue);
717}
718
719static inline void ConvertProPhotoToXYZ(const double red,const double green,
720 const double blue,double *X,double *Y,double *Z)
721{
722 double
723 b,
724 g,
725 r;
726
727 /*
728 Convert ProPhoto to XYZ colorspace.
729 */
730 r=QuantumScale*DecodePixelGamma((double) QuantumRange*red);
731 g=QuantumScale*DecodePixelGamma((double) QuantumRange*green);
732 b=QuantumScale*DecodePixelGamma((double) QuantumRange*blue);
733 *X=0.4865709486482162*r+0.26566769316909306*g+0.1982172852343625*b;
734 *X=0.7977604896723027*r+0.13518583717574031*g+0.03134934958152480000*b;
735 *Y=0.2880711282292934*r+0.71184321781010140*g+0.00008565396060525902*b;
736 *Z=0.0000000000000000*r+0.00000000000000000*g+0.82510460251046010000*b;
737}
738
739static inline void ConvertProPhotoToRGB(const double r,const double g,
740 const double b,double *red,double *green,double *blue)
741{
742 double
743 X,
744 Y,
745 Z;
746
747 ConvertProPhotoToXYZ(r,g,b,&X,&Y,&Z);
748 ConvertXYZToRGB(X,Y,Z,red,green,blue);
749}
750
751static inline void ConvertXYZToCAT02LMS(const double X,const double Y,
752 const double Z,double *L,double *M,double *S)
753{
754 *L=0.7328*X+0.4296*Y-0.1624*Z;
755 *M=(-0.7036)*X+1.6975*Y+0.0061*Z;
756 *S=0.0030*X+0.0136*Y+0.9834*Z;
757}
758
759static inline void ConvertRGBToXYZ(const double red,const double green,
760 const double blue,double *X,double *Y,double *Z)
761{
762 double
763 b,
764 g,
765 r;
766
767 /*
768 Convert RGB to XYZ colorspace.
769 */
770 r=QuantumScale*DecodePixelGamma(red);
771 g=QuantumScale*DecodePixelGamma(green);
772 b=QuantumScale*DecodePixelGamma(blue);
773 *X=(0.4123955889674142161*r)+(0.3575834307637148171*g)+
774 (0.1804926473817015735*b);
775 *Y=(0.2125862307855955516*r)+(0.7151703037034108499*g)+
776 (0.07220049864333622685*b);
777 *Z=(0.01929721549174694484*r)+(0.1191838645808485318*g)+
778 (0.9504971251315797660*b);
779}
780
781static inline void ConvertRGBToCAT02LMS(const double R,const double G,
782 const double B,double *L,double *M,double *S)
783{
784 double
785 X,
786 Y,
787 Z;
788
789 ConvertRGBToXYZ(R,G,B,&X,&Y,&Z);
790 ConvertXYZToCAT02LMS(X,Y,Z,L,M,S);
791}
792
793static inline void ConvertRGBToCMY(const double red,const double green,
794 const double blue,double *cyan,double *magenta,double *yellow)
795{
796 *cyan=QuantumScale*((double) QuantumRange-red);
797 *magenta=QuantumScale*((double) QuantumRange-green);
798 *yellow=QuantumScale*((double) QuantumRange-blue);
799}
800
801static inline void ConvertRGBToHCL(const double red,const double green,
802 const double blue,double *hue,double *chroma,double *luma)
803{
804 double
805 c,
806 h,
807 max;
808
809 /*
810 Convert RGB to HCL colorspace.
811 */
812 assert(hue != (double *) NULL);
813 assert(chroma != (double *) NULL);
814 assert(luma != (double *) NULL);
815 max=MagickMax(red,MagickMax(green,blue));
816 c=max-(double) MagickMin(red,MagickMin(green,blue));
817 h=0.0;
818 if (fabs(c) < MagickEpsilon)
819 h=0.0;
820 else
821 if (fabs(red-max) < MagickEpsilon)
822 h=fmod((green-blue)/c+6.0,6.0);
823 else
824 if (fabs(green-max) < MagickEpsilon)
825 h=((blue-red)/c)+2.0;
826 else
827 if (fabs(blue-max) < MagickEpsilon)
828 h=((red-green)/c)+4.0;
829 *hue=(h/6.0);
830 *chroma=QuantumScale*c;
831 *luma=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
832}
833
834static inline void ConvertRGBToHCLp(const double red,const double green,
835 const double blue,double *hue,double *chroma,double *luma)
836{
837 double
838 c,
839 h,
840 max;
841
842 /*
843 Convert RGB to HCL colorspace.
844 */
845 assert(hue != (double *) NULL);
846 assert(chroma != (double *) NULL);
847 assert(luma != (double *) NULL);
848 max=MagickMax(red,MagickMax(green,blue));
849 c=max-MagickMin(red,MagickMin(green,blue));
850 h=0.0;
851 if (fabs(c) < MagickEpsilon)
852 h=0.0;
853 else
854 if (fabs(red-max) < MagickEpsilon)
855 h=fmod((green-blue)/c+6.0,6.0);
856 else
857 if (fabs(green-max) < MagickEpsilon)
858 h=((blue-red)/c)+2.0;
859 else
860 if (fabs(blue-max) < MagickEpsilon)
861 h=((red-green)/c)+4.0;
862 *hue=(h/6.0);
863 *chroma=QuantumScale*c;
864 *luma=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
865}
866
867static inline void ConvertRGBToHSB(const double red,const double green,
868 const double blue,double *hue,double *saturation,double *brightness)
869{
870 double
871 delta,
872 max,
873 min;
874
875 /*
876 Convert RGB to HSB colorspace.
877 */
878 assert(hue != (double *) NULL);
879 assert(saturation != (double *) NULL);
880 assert(brightness != (double *) NULL);
881 *hue=0.0;
882 *saturation=0.0;
883 *brightness=0.0;
884 min=red < green ? red : green;
885 if (blue < min)
886 min=blue;
887 max=red > green ? red : green;
888 if (blue > max)
889 max=blue;
890 if (fabs(max) < MagickEpsilon)
891 return;
892 delta=max-min;
893 *saturation=delta/max;
894 *brightness=QuantumScale*max;
895 if (fabs(delta) < MagickEpsilon)
896 return;
897 if (fabs(red-max) < MagickEpsilon)
898 *hue=(green-blue)/delta;
899 else
900 if (fabs(green-max) < MagickEpsilon)
901 *hue=2.0+(blue-red)/delta;
902 else
903 *hue=4.0+(red-green)/delta;
904 *hue/=6.0;
905 if (*hue < 0.0)
906 *hue+=1.0;
907}
908
909static inline void ConvertRGBToHSI(const double red,const double green,
910 const double blue,double *hue,double *saturation,double *intensity)
911{
912 double
913 alpha,
914 beta;
915
916 /*
917 Convert RGB to HSI colorspace.
918 */
919 assert(hue != (double *) NULL);
920 assert(saturation != (double *) NULL);
921 assert(intensity != (double *) NULL);
922 *intensity=(QuantumScale*red+QuantumScale*green+QuantumScale*blue)/3.0;
923 if (*intensity <= 0.0)
924 {
925 *hue=0.0;
926 *saturation=0.0;
927 return;
928 }
929 *saturation=1.0-MagickMin(QuantumScale*red,MagickMin(QuantumScale*green,
930 QuantumScale*blue))/(*intensity);
931 alpha=0.5*(2.0*QuantumScale*red-QuantumScale*green-QuantumScale*blue);
932 beta=0.8660254037844385*(QuantumScale*green-QuantumScale*blue);
933 *hue=atan2(beta,alpha)*(180.0/MagickPI)/360.0;
934 if (*hue < 0.0)
935 *hue+=1.0;
936}
937
938static inline void ConvertXYZToAdobe98(const double X,const double Y,
939 const double Z,double *red,double *green,double *blue)
940{
941 double
942 b,
943 g,
944 r;
945
946 r=2.041587903810746500*X-0.56500697427885960*Y-0.34473135077832956*Z;
947 g=(-0.969243636280879500)*X+1.87596750150772020*Y+0.04155505740717557*Z;
948 b=0.013444280632031142*X-0.11836239223101838*Y+1.01517499439120540*Z;
949 *red=QuantumScale*EncodePixelGamma((double) QuantumRange*r);
950 *green=QuantumScale*EncodePixelGamma((double) QuantumRange*g);
951 *blue=QuantumScale*EncodePixelGamma((double) QuantumRange*b);
952}
953
954static inline void ConvertRGBToAdobe98(const double red,const double green,
955 const double blue,double *r,double *g,double *b)
956{
957 double
958 X,
959 Y,
960 Z;
961
962 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
963 ConvertXYZToAdobe98(X,Y,Z,r,g,b);
964}
965
966static inline void ConvertXYZToDisplayP3(const double X,const double Y,
967 const double Z,double *red,double *green,double *blue)
968{
969 double
970 b,
971 g,
972 r;
973
974 r=2.49349691194142500*X-0.93138361791912390*Y-0.402710784450716840*Z;
975 g=(-0.82948896956157470)*X+1.76266406031834630*Y+0.023624685841943577*Z;
976 b=0.03584583024378447*X-0.07617238926804182*Y+0.956884524007687200*Z;
977 *red=QuantumScale*EncodePixelGamma((double) QuantumRange*r);
978 *green=QuantumScale*EncodePixelGamma((double) QuantumRange*g);
979 *blue=QuantumScale*EncodePixelGamma((double) QuantumRange*b);
980}
981
982static inline void ConvertRGBToDisplayP3(const double red,const double green,
983 const double blue,double *r,double *g,double *b)
984{
985 double
986 X,
987 Y,
988 Z;
989
990 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
991 ConvertXYZToDisplayP3(X,Y,Z,r,g,b);
992}
993
994static inline void ConvertRGBToHSV(const double red,const double green,
995 const double blue,double *hue,double *saturation,double *value)
996{
997 double
998 c,
999 max,
1000 min;
1001
1002 /*
1003 Convert RGB to HSV colorspace.
1004 */
1005 assert(hue != (double *) NULL);
1006 assert(saturation != (double *) NULL);
1007 assert(value != (double *) NULL);
1008 max=MagickMax(QuantumScale*red,MagickMax(QuantumScale*green,
1009 QuantumScale*blue));
1010 min=MagickMin(QuantumScale*red,MagickMin(QuantumScale*green,
1011 QuantumScale*blue));
1012 c=max-min;
1013 *value=max;
1014 if (c <= 0.0)
1015 {
1016 *hue=0.0;
1017 *saturation=0.0;
1018 return;
1019 }
1020 if (fabs(max-QuantumScale*red) < MagickEpsilon)
1021 {
1022 *hue=(QuantumScale*green-QuantumScale*blue)/c;
1023 if ((QuantumScale*green) < (QuantumScale*blue))
1024 *hue+=6.0;
1025 }
1026 else
1027 if (fabs(max-QuantumScale*green) < MagickEpsilon)
1028 *hue=2.0+(QuantumScale*blue-QuantumScale*red)/c;
1029 else
1030 *hue=4.0+(QuantumScale*red-QuantumScale*green)/c;
1031 *hue*=60.0/360.0;
1032 *saturation=c*PerceptibleReciprocal(max);
1033}
1034
1035static inline void ConvertRGBToHWB(const double red,const double green,
1036 const double blue,double *hue,double *whiteness,double *blackness)
1037{
1038 double
1039 f,
1040 p,
1041 v,
1042 w;
1043
1044 /*
1045 Convert RGB to HWB colorspace.
1046 */
1047 assert(hue != (double *) NULL);
1048 assert(whiteness != (double *) NULL);
1049 assert(blackness != (double *) NULL);
1050 w=MagickMin(red,MagickMin(green,blue));
1051 v=MagickMax(red,MagickMax(green,blue));
1052 *blackness=1.0-QuantumScale*v;
1053 *whiteness=QuantumScale*w;
1054 if (fabs(v-w) < MagickEpsilon)
1055 {
1056 *hue=(-1.0);
1057 return;
1058 }
1059 f=(fabs(red-w) < MagickEpsilon) ? green-blue :
1060 ((fabs(green-w) < MagickEpsilon) ? blue-red : red-green);
1061 p=(fabs(red-w) < MagickEpsilon) ? 3.0 :
1062 ((fabs(green-w) < MagickEpsilon) ? 5.0 : 1.0);
1063 *hue=(p-f/(v-1.0*w))/6.0;
1064}
1065
1066static inline void ConvertXYZToLab(const double X,const double Y,const double Z,
1067 const IlluminantType illuminant,double *L,double *a,double *b)
1068{
1069 double
1070 x,
1071 y,
1072 z;
1073
1074 if ((X/illuminant_tristimulus[illuminant].x) > CIEEpsilon)
1075 x=pow(X/illuminant_tristimulus[illuminant].x,1.0/3.0);
1076 else
1077 x=(CIEK*X/illuminant_tristimulus[illuminant].x+16.0)/116.0;
1078 if ((Y/illuminant_tristimulus[illuminant].y) > CIEEpsilon)
1079 y=pow(Y/illuminant_tristimulus[illuminant].y,1.0/3.0);
1080 else
1081 y=(CIEK*Y/illuminant_tristimulus[illuminant].y+16.0)/116.0;
1082 if ((Z/illuminant_tristimulus[illuminant].z) > CIEEpsilon)
1083 z=pow(Z/illuminant_tristimulus[illuminant].z,1.0/3.0);
1084 else
1085 z=(CIEK*Z/illuminant_tristimulus[illuminant].z+16.0)/116.0;
1086 *L=((116.0*y)-16.0)/100.0;
1087 *a=(500.0*(x-y))/255.0+0.5;
1088 *b=(200.0*(y-z))/255.0+0.5;
1089}
1090
1091static inline void ConvertRGBToLab(const double red,const double green,
1092 const double blue,const IlluminantType illuminant,double *L,double *a,
1093 double *b)
1094{
1095 double
1096 X,
1097 Y,
1098 Z;
1099
1100 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1101 ConvertXYZToLab(X,Y,Z,illuminant,L,a,b);
1102}
1103
1104static inline void ConvertXYZToLCHab(const double X,const double Y,
1105 const double Z,const IlluminantType illuminant,double *luma,double *chroma,
1106 double *hue)
1107{
1108 double
1109 a,
1110 b;
1111
1112 ConvertXYZToLab(X,Y,Z,illuminant,luma,&a,&b);
1113 *chroma=hypot(a-0.5,b-0.5)/1.0+0.5;
1114 *hue=180.0*atan2(b-0.5,a-0.5)/MagickPI/360.0;
1115 if (*hue < 0.0)
1116 *hue+=1.0;
1117}
1118
1119static inline void ConvertRGBToLCHab(const double red,const double green,
1120 const double blue,const IlluminantType illuminant,double *luma,double *chroma,
1121 double *hue)
1122{
1123 double
1124 X,
1125 Y,
1126 Z;
1127
1128 /*
1129 Convert RGB to LCHab colorspace.
1130 */
1131 assert(luma != (double *) NULL);
1132 assert(chroma != (double *) NULL);
1133 assert(hue != (double *) NULL);
1134 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1135 ConvertXYZToLCHab(X,Y,Z,illuminant,luma,chroma,hue);
1136}
1137
1138static inline void ConvertXYZToLuv(const double X,const double Y,const double Z,
1139 const IlluminantType illuminant,double *L,double *u,double *v)
1140{
1141 double
1142 alpha;
1143
1144 if ((Y/illuminant_tristimulus[illuminant].y) > CIEEpsilon)
1145 *L=(double) (116.0*pow(Y/illuminant_tristimulus[illuminant].y,
1146 1.0/3.0)-16.0);
1147 else
1148 *L=CIEK*(Y/illuminant_tristimulus[illuminant].y);
1149 alpha=PerceptibleReciprocal(X+15.0*Y+3.0*Z);
1150 *u=13.0*(*L)*((4.0*alpha*X)-(4.0*illuminant_tristimulus[illuminant].x/
1151 (illuminant_tristimulus[illuminant].x+15.0*
1152 illuminant_tristimulus[illuminant].y+3.0*
1153 illuminant_tristimulus[illuminant].z)));
1154 *v=13.0*(*L)*((9.0*alpha*Y)-(9.0*illuminant_tristimulus[illuminant].y/
1155 (illuminant_tristimulus[illuminant].x+15.0*
1156 illuminant_tristimulus[illuminant].y+3.0*
1157 illuminant_tristimulus[illuminant].z)));
1158 *L/=100.0;
1159 *u=(*u+134.0)/354.0;
1160 *v=(*v+140.0)/262.0;
1161}
1162
1163static inline void ConvertXYZToLCHuv(const double X,const double Y,
1164 const double Z,const IlluminantType illuminant,double *luma,double *chroma,
1165 double *hue)
1166{
1167 double
1168 u,
1169 v;
1170
1171 ConvertXYZToLuv(X,Y,Z,illuminant,luma,&u,&v);
1172 *chroma=hypot(354.0*u-134.0,262.0*v-140.0)/255.0+0.5;
1173 *hue=180.0*atan2(262.0*v-140.0,354.0*u-134.0)/MagickPI/360.0;
1174 if (*hue < 0.0)
1175 *hue+=1.0;
1176}
1177
1178static inline void ConvertRGBToLCHuv(const double red,const double green,
1179 const double blue,const IlluminantType illuminant,double *luma,double *chroma,
1180 double *hue)
1181{
1182 double
1183 X,
1184 Y,
1185 Z;
1186
1187 /*
1188 Convert RGB to LCHuv colorspace.
1189 */
1190 assert(luma != (double *) NULL);
1191 assert(chroma != (double *) NULL);
1192 assert(hue != (double *) NULL);
1193 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1194 ConvertXYZToLCHuv(X,Y,Z,illuminant,luma,chroma,hue);
1195}
1196
1197static inline void ConvertXYZToProPhoto(const double X,const double Y,
1198 const double Z,double *red,double *green,double *blue)
1199{
1200 double
1201 b,
1202 g,
1203 r;
1204
1205 r=1.3457989731028281*X-0.25558010007997534*Y-0.05110628506753401*Z;
1206 g=(-0.5446224939028347)*X+1.50823274131327810*Y+0.02053603239147973*Z;
1207 b=0.0000000000000000*X+0.0000000000000000*Y+1.21196754563894540*Z;
1208 *red=QuantumScale*EncodePixelGamma((double) QuantumRange*r);
1209 *green=QuantumScale*EncodePixelGamma((double) QuantumRange*g);
1210 *blue=QuantumScale*EncodePixelGamma((double) QuantumRange*b);
1211}
1212
1213static inline void ConvertRGBToProPhoto(const double red,const double green,
1214 const double blue,double *r,double *g,double *b)
1215{
1216 double
1217 X,
1218 Y,
1219 Z;
1220
1221 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1222 ConvertXYZToProPhoto(X,Y,Z,r,g,b);
1223}
1224
1225static inline void ConvertXYZToLMS(const double x,const double y,
1226 const double z,double *L,double *M,double *S)
1227{
1228 *L=0.7328*x+0.4296*y-0.1624*z;
1229 *M=(-0.7036*x+1.6975*y+0.0061*z);
1230 *S=0.0030*x+0.0136*y+0.9834*z;
1231}
1232
1233static inline void ConvertRGBToLMS(const double red,const double green,
1234 const double blue,double *L,double *M,double *S)
1235{
1236 double
1237 X,
1238 Y,
1239 Z;
1240
1241 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1242 ConvertXYZToLMS(X,Y,Z,L,M,S);
1243}
1244
1245static inline void ConvertRGBToLuv(const double red,const double green,
1246 const double blue,const IlluminantType illuminant,double *L,double *u,
1247 double *v)
1248{
1249 double
1250 X,
1251 Y,
1252 Z;
1253
1254 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1255 ConvertXYZToLuv(X,Y,Z,illuminant,L,u,v);
1256}
1257
1258static inline void ConvertRGBToxyY(const double red,const double green,
1259 const double blue,double *low_x,double *low_y,double *cap_Y)
1260{
1261 double
1262 gamma,
1263 X,
1264 Y,
1265 Z;
1266
1267 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1268 gamma=PerceptibleReciprocal(X+Y+Z);
1269 *low_x=gamma*X;
1270 *low_y=gamma*Y;
1271 *cap_Y=Y;
1272}
1273
1274static inline void ConvertXYZToJzazbz(const double X,const double Y,
1275 const double Z,const double white_luminance,double *Jz,double *az,double *bz)
1276{
1277#define Jzazbz_b (1.15) /* https://observablehq.com/@jrus/jzazbz */
1278#define Jzazbz_g (0.66) /* refactored by N. Robidoux */
1279#define Jzazbz_c1 (3424.0/4096.0)
1280#define Jzazbz_c2 (2413.0/128.0)
1281#define Jzazbz_c3 (2392.0/128.0)
1282#define Jzazbz_n (2610.0/16384.0)
1283#define Jzazbz_p (1.7*2523.0/32.0)
1284#define Jzazbz_d (-0.56)
1285#define Jzazbz_d0 (1.6295499532821566e-11)
1286#define Jzazbz_LX (0.41478972)
1287#define Jzazbz_LY (0.579999)
1288#define Jzazbz_LZ (0.0146480)
1289#define Jzazbz_MX (-0.2015100)
1290#define Jzazbz_MY (1.120649)
1291#define Jzazbz_MZ (0.0531008)
1292#define Jzazbz_SX (-0.0166008)
1293#define Jzazbz_SY (0.264800)
1294#define Jzazbz_SZ (0.6684799)
1295#define Jzazbz_aL (3.52400)
1296#define Jzazbz_aM (-4.066708)
1297#define Jzazbz_aS (0.542708)
1298#define Jzazbz_bL (0.199076)
1299#define Jzazbz_bM (1.096799)
1300#define Jzazbz_bS (-1.295875)
1301
1302 double
1303 a,
1304 b,
1305 dL,
1306 dM,
1307 dS,
1308 gL,
1309 gM,
1310 gS,
1311 nL,
1312 nM,
1313 nS,
1314 Iz,
1315 J,
1316 JdI,
1317 L,
1318 Lp,
1319 M,
1320 Mp,
1321 S,
1322 Sp,
1323 WLr,
1324 Xp,
1325 Yp;
1326
1327 WLr=PerceptibleReciprocal(white_luminance);
1328 Xp=Z+Jzazbz_b*(X-Z); /* If X and Z are vectorized, better done */
1329 Yp=X+Jzazbz_g*(Y-X); /* as Xp=Jzazbz_b*X+(1.0-Jzazbz_b)*Z; etc. */
1330 L=Jzazbz_LZ*Z;
1331 M=Jzazbz_MZ*Z;
1332 S=Jzazbz_SZ*Z;
1333 L+=Jzazbz_LX*Xp;
1334 M+=Jzazbz_MX*Xp;
1335 S+=Jzazbz_SX*Xp;
1336 L+=Jzazbz_LY*Yp;
1337 M+=Jzazbz_MY*Yp;
1338 S+=Jzazbz_SY*Yp;
1339 gL=pow(L*WLr,Jzazbz_n);
1340 gM=pow(M*WLr,Jzazbz_n);
1341 gS=pow(S*WLr,Jzazbz_n);
1342 nL=Jzazbz_c1+Jzazbz_c2*gL;
1343 nM=Jzazbz_c1+Jzazbz_c2*gM;
1344 nS=Jzazbz_c1+Jzazbz_c2*gS;
1345 dL=1.0+Jzazbz_c3*gL;
1346 dM=1.0+Jzazbz_c3*gM;
1347 dS=1.0+Jzazbz_c3*gS;
1348 Lp=pow(nL/dL,Jzazbz_p);
1349 Mp=pow(nM/dM,Jzazbz_p);
1350 Sp=pow(nS/dS,Jzazbz_p);
1351 Iz=(Lp+Mp)*0.5;
1352 JdI=Jzazbz_d*Iz;
1353 J=(JdI+Iz)/(JdI+1.0)-Jzazbz_d0;
1354 a=0.5+Jzazbz_aL*Lp;
1355 b=0.5+Jzazbz_bL*Lp;
1356 a+=Jzazbz_aM*Mp;
1357 b+=Jzazbz_bM*Mp;
1358 a+=Jzazbz_aS*Sp;
1359 b+=Jzazbz_bS*Sp;
1360 *Jz=IsNaN(J) != 0 ? 0.0 : J;
1361 *az=IsNaN(a) != 0 ? 0.5 : a;
1362 *bz=IsNaN(b) != 0 ? 0.5 : b;
1363}
1364
1365static inline void ConvertRGBToJzazbz(const double red,const double green,
1366 const double blue,const double white_luminance,double *Jz,double *az,
1367 double *bz)
1368{
1369 double
1370 X,
1371 Y,
1372 Z;
1373
1374 ConvertRGBToXYZ(red,blue,green,&X,&Y,&Z);
1375 ConvertXYZToJzazbz(X,Y,Z,white_luminance,Jz,az,bz);
1376}
1377
1378static inline void ConvertJzazbzToXYZ(const double Jz,const double az,
1379 const double bz,const double white_luminance,double *X,double *Y,double *Z)
1380{
1381#define Jzazbz_Ca (0.138605043271539)
1382#define Jzazbz_Cb (0.0580473161561189)
1383#define Jzazbz_Sa (-0.0960192420263189)
1384#define Jzazbz_Sb (-0.811891896056039)
1385#define Jzazbz_XL (1.92422643578761)
1386#define Jzazbz_XM (-1.00479231259537)
1387#define Jzazbz_XS (0.037651404030618)
1388#define Jzazbz_YL (0.350316762094999)
1389#define Jzazbz_YM (0.726481193931655)
1390#define Jzazbz_YS (-0.065384422948085)
1391#define Jzazbz_ZL (-0.0909828109828476)
1392#define Jzazbz_ZM (-0.312728290523074)
1393#define Jzazbz_ZS (1.52276656130526)
1394#define mJzazbz_c3 (-2392.0/128.0)
1395
1396 double
1397 azz,
1398 bzz,
1399 C,
1400 dL,
1401 dM,
1402 dS,
1403 g,
1404 gL,
1405 gM,
1406 gS,
1407 Jnr,
1408 Jpr,
1409 L,
1410 Lp,
1411 M,
1412 Mp,
1413 S,
1414 Sp,
1415 nL,
1416 nM,
1417 nS,
1418 Xp,
1419 Zp,
1420 Yp;
1421
1422 g=Jz+Jzazbz_d0;
1423 azz=az-0.5;
1424 bzz=bz-0.5;
1425 C=Jzazbz_Ca*azz+Jzazbz_Cb*bzz;
1426 Sp=g/(1.0+Jzazbz_d*(1.0-g));
1427 Lp=Sp+C;
1428 Mp=Sp-C;
1429 Sp+=Jzazbz_Sa*azz;
1430 Sp+=Jzazbz_Sb*bzz;
1431 Jpr=1.0/Jzazbz_p;
1432 gL=pow(Lp,Jpr);
1433 gM=pow(Mp,Jpr);
1434 gS=pow(Sp,Jpr);
1435 Jnr=1.0/Jzazbz_n;
1436 nL=gL-Jzazbz_c1;
1437 nM=gM-Jzazbz_c1;
1438 nS=gS-Jzazbz_c1;
1439 dL=Jzazbz_c2+mJzazbz_c3*gL;
1440 dM=Jzazbz_c2+mJzazbz_c3*gM;
1441 dS=Jzazbz_c2+mJzazbz_c3*gS;
1442 L=pow(nL/dL,Jnr);
1443 M=pow(nM/dM,Jnr);
1444 S=pow(nS/dS,Jnr);
1445 L*=white_luminance;
1446 M*=white_luminance;
1447 S*=white_luminance;
1448 Zp=Jzazbz_ZL*L;
1449 Xp=Jzazbz_XL*L;
1450 Yp=Jzazbz_YL*L;
1451 Zp+=Jzazbz_ZM*M;
1452 Xp+=Jzazbz_XM*M;
1453 Yp+=Jzazbz_YM*M;
1454 Zp+=Jzazbz_ZS*S;
1455 Xp+=Jzazbz_XS*S;
1456 Yp+=Jzazbz_YS*S;
1457 Zp=IsNaN(Zp) != 0 ? 0.0 : Zp;
1458 Xp=Zp+(Xp-Zp)/Jzazbz_b; /* If Xp and Zp are vectorized, better done */
1459 Xp=IsNaN(Xp) != 0 ? 0.0 : Xp; /* as rJzazbz_b = 1.0/Jzazbz_b; */
1460 Yp=Xp+(Yp-Xp)/Jzazbz_g; /* Xp=rJzazbz_b*Xp+(1.0-rJzazbz_b)*Zp; etc. */
1461 Yp=IsNaN(Yp) != 0 ? 0.0 : Yp;
1462 *Z=Zp;
1463 *X=Xp;
1464 *Y=Yp;
1465}
1466
1467static inline void ConvertJzazbzToRGB(const double Jz,const double az,
1468 const double bz,const double white_luminance,double *red,double *green,
1469 double *blue)
1470{
1471 double
1472 X,
1473 Y,
1474 Z;
1475
1476 ConvertJzazbzToXYZ(Jz,az,bz,white_luminance,&X,&Y,&Z);
1477 ConvertXYZToRGB(X,Y,Z,red,blue,green);
1478}
1479
1480static inline void ConvertOklabToRGB(const double L,const double a,
1481 const double b,double *red,double *green,double *blue)
1482{
1483#define Oklab_la (0.3963377774)
1484#define Oklab_lb (0.2158037573)
1485#define Oklab_ma (-0.1055613458)
1486#define Oklab_mb (-0.0638541728)
1487#define Oklab_sa (-0.0894841775)
1488#define Oklab_sb (-1.2914855480)
1489#define Oklab_Rl (4.0767416621)
1490#define Oklab_Rm (-3.3077115913)
1491#define Oklab_Rs (0.2309699292)
1492#define Oklab_Gl (-1.2684380046)
1493#define Oklab_Gm (2.6097574011)
1494#define Oklab_Gs (-0.3413193965)
1495#define Oklab_Bl (-0.0041960863)
1496#define Oklab_Bm (-0.7034186147)
1497#define Oklab_Bs (1.7076147010)
1498
1499 double
1500 aa,
1501 B,
1502 bb,
1503 Bl,
1504 Bm,
1505 Bs,
1506 G,
1507 Gl,
1508 Gm,
1509 Gs,
1510 l,
1511 m,
1512 R,
1513 Rl,
1514 Rm,
1515 Rs,
1516 s;
1517
1518 aa=a-0.5;
1519 bb=b-0.5;
1520 l=L+Oklab_la*aa+Oklab_lb*bb;
1521 m=L+Oklab_ma*aa+Oklab_mb*bb;
1522 s=L+Oklab_sa*aa+Oklab_sb*bb;
1523 l*=l*l;
1524 m*=m*m;
1525 s*=s*s;
1526 Rl=Oklab_Rl*(double) QuantumRange; /* constant folding */
1527 Rm=Oklab_Rm*(double) QuantumRange;
1528 Rs=Oklab_Rs*(double) QuantumRange;
1529 Gl=Oklab_Gl*(double) QuantumRange;
1530 Gm=Oklab_Gm*(double) QuantumRange;
1531 Gs=Oklab_Gs*(double) QuantumRange;
1532 Bl=Oklab_Bl*(double) QuantumRange;
1533 Bm=Oklab_Bm*(double) QuantumRange;
1534 Bs=Oklab_Bs*(double) QuantumRange;
1535 R=Rl*l+Rm*m+Rs*s;
1536 G=Gl*l+Gm*m+Gs*s;
1537 B=Bl*l+Bm*m+Bs*s;
1538 *red=EncodePixelGamma(R);
1539 *green=EncodePixelGamma(G);
1540 *blue=EncodePixelGamma(B);
1541}
1542
1543static inline void ConvertOklchToRGB(const double L,const double C,
1544 const double h,double *red,double *green,double *blue)
1545{
1546 double
1547 a,
1548 b;
1549
1550 a=C*cos(2.0*MagickPI*h);
1551 b=C*sin(2.0*MagickPI*h);
1552 ConvertOklabToRGB(L,a,b,red,green,blue);
1553}
1554
1555static inline void ConvertRGBToOklab(const double red,const double green,
1556 const double blue,double *L,double *a,double *b)
1557{
1558#define Oklab_lR (0.4122214708)
1559#define Oklab_lG (0.5363325363)
1560#define Oklab_lB (0.0514459929)
1561#define Oklab_mR (0.2119034982)
1562#define Oklab_mG (0.6806995451)
1563#define Oklab_mB (0.1073969566)
1564#define Oklab_sR (0.0883024619)
1565#define Oklab_sG (0.2817188376)
1566#define Oklab_sB (0.6299787005)
1567#define Oklab_Ll (0.2104542553)
1568#define Oklab_Lm (0.7936177850)
1569#define Oklab_Ls (-0.0040720468)
1570#define Oklab_al (1.9779984951)
1571#define Oklab_am (-2.4285922050)
1572#define Oklab_as (0.4505937099)
1573#define Oklab_bl (0.0259040371)
1574#define Oklab_bm (0.7827717662)
1575#define Oklab_bs (-0.8086757660)
1576
1577 double
1578 B,
1579 G,
1580 l,
1581 lR,
1582 lG,
1583 lB,
1584 m,
1585 mR,
1586 mG,
1587 mB,
1588 R,
1589 s,
1590 sR,
1591 sG,
1592 sB;
1593
1594 R=DecodePixelGamma(red);
1595 G=DecodePixelGamma(green);
1596 B=DecodePixelGamma(blue);
1597 lR=Oklab_lR*QuantumScale; /* constant folding */
1598 lG=Oklab_lG*QuantumScale;
1599 lB=Oklab_lB*QuantumScale;
1600 mR=Oklab_mR*QuantumScale;
1601 mG=Oklab_mG*QuantumScale;
1602 mB=Oklab_mB*QuantumScale;
1603 sR=Oklab_sR*QuantumScale;
1604 sG=Oklab_sG*QuantumScale;
1605 sB=Oklab_sB*QuantumScale;
1606 l=lR*R+lG*G+lB*B;
1607 m=mR*R+mG*G+mB*B;
1608 s=sR*R+sG*G+sB*B;
1609 l=cbrt(l);
1610 m=cbrt(m);
1611 s=cbrt(s);
1612 *L=Oklab_Ll*l+Oklab_Lm*m+Oklab_Ls*s;
1613 *a=Oklab_al*l+Oklab_am*m+Oklab_as*s+0.5;
1614 *b=Oklab_bl*l+Oklab_bm*m+Oklab_bs*s+0.5;
1615}
1616
1617static inline void ConvertRGBToOklch(const double red,const double green,
1618 const double blue,double *L,double *C,double *h)
1619{
1620 double
1621 a,
1622 b;
1623
1624 ConvertRGBToOklab(red,green,blue,L,&a,&b);
1625 *C=sqrt(a*a+b*b);
1626 *h=0.5+0.5*atan2(-b,-a)/MagickPI;
1627}
1628
1629static inline void ConvertRGBToYDbDr(const double red,const double green,
1630 const double blue,double *Y,double *Db,double *Dr)
1631{
1632 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1633 *Db=QuantumScale*(-0.450*red-0.883*green+1.333*blue)+0.5;
1634 *Dr=QuantumScale*(-1.333*red+1.116*green+0.217*blue)+0.5;
1635}
1636
1637static inline void ConvertRGBToYIQ(const double red,const double green,
1638 const double blue,double *Y,double *I,double *Q)
1639{
1640 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1641 *I=QuantumScale*(0.595716*red-0.274453*green-0.321263*blue)+0.5;
1642 *Q=QuantumScale*(0.211456*red-0.522591*green+0.311135*blue)+0.5;
1643}
1644
1645static inline void ConvertRGBToYPbPr(const double red,const double green,
1646 const double blue,double *Y,double *Pb,double *Pr)
1647{
1648 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1649 *Pb=QuantumScale*((-0.1687367)*red-0.331264*green+0.5*blue)+0.5;
1650 *Pr=QuantumScale*(0.5*red-0.418688*green-0.081312*blue)+0.5;
1651}
1652
1653static inline void ConvertRGBToYCbCr(const double red,const double green,
1654 const double blue,double *Y,double *Cb,double *Cr)
1655{
1656 ConvertRGBToYPbPr(red,green,blue,Y,Cb,Cr);
1657}
1658
1659static inline void ConvertRGBToYUV(const double red,const double green,
1660 const double blue,double *Y,double *U,double *V)
1661{
1662 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1663 *U=QuantumScale*((-0.147)*red-0.289*green+0.436*blue)+0.5;
1664 *V=QuantumScale*(0.615*red-0.515*green-0.100*blue)+0.5;
1665}
1666
1667static inline void ConvertRGBToCMYK(PixelInfo *pixel)
1668{
1669 MagickRealType
1670 black,
1671 blue,
1672 cyan,
1673 green,
1674 magenta,
1675 red,
1676 yellow;
1677
1678 if (pixel->colorspace != sRGBColorspace)
1679 {
1680 red=QuantumScale*pixel->red;
1681 green=QuantumScale*pixel->green;
1682 blue=QuantumScale*pixel->blue;
1683 }
1684 else
1685 {
1686 red=QuantumScale*DecodePixelGamma(pixel->red);
1687 green=QuantumScale*DecodePixelGamma(pixel->green);
1688 blue=QuantumScale*DecodePixelGamma(pixel->blue);
1689 }
1690 if ((fabs((double) red) < MagickEpsilon) &&
1691 (fabs((double) green) < MagickEpsilon) &&
1692 (fabs((double) blue) < MagickEpsilon))
1693 {
1694 pixel->black=(MagickRealType) QuantumRange;
1695 return;
1696 }
1697 cyan=(MagickRealType) (1.0-red);
1698 magenta=(MagickRealType) (1.0-green);
1699 yellow=(MagickRealType) (1.0-blue);
1700 black=cyan;
1701 if (magenta < black)
1702 black=magenta;
1703 if (yellow < black)
1704 black=yellow;
1705 cyan=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(cyan-black));
1706 magenta=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(magenta-black));
1707 yellow=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(yellow-black));
1708 pixel->colorspace=CMYKColorspace;
1709 pixel->red=(MagickRealType) QuantumRange*cyan;
1710 pixel->green=(MagickRealType) QuantumRange*magenta;
1711 pixel->blue=(MagickRealType) QuantumRange*yellow;
1712 pixel->black=(MagickRealType) QuantumRange*black;
1713}
1714
1715static inline void ConvertYPbPrToRGB(const double Y,const double Pb,
1716 const double Pr,double *red,double *green,double *blue)
1717{
1718 *red=(double) QuantumRange*(0.99999999999914679361*Y-1.2188941887145875e-06*
1719 (Pb-0.5)+1.4019995886561440468*(Pr-0.5));
1720 *green=(double) QuantumRange*(0.99999975910502514331*Y-0.34413567816504303521*
1721 (Pb-0.5)-0.71413649331646789076*(Pr-0.5));
1722 *blue=(double) QuantumRange*(1.00000124040004623180*Y+1.77200006607230409200*
1723 (Pb-0.5)+2.1453384174593273e-06*(Pr-0.5));
1724}
1725
1726static inline void ConvertYCbCrToRGB(const double Y,const double Cb,
1727 const double Cr,double *red,double *green,double *blue)
1728{
1729 ConvertYPbPrToRGB(Y,Cb,Cr,red,green,blue);
1730}
1731
1732static inline void ConvertYDbDrToRGB(const double Y,const double Db,
1733 const double Dr,double *red,double *green,double *blue)
1734{
1735 *red=(double) QuantumRange*(Y+9.2303716147657e-05*(Db-0.5)-
1736 0.52591263066186533*(Dr-0.5));
1737 *green=(double) QuantumRange*(Y-0.12913289889050927*(Db-0.5)+
1738 0.26789932820759876*(Dr-0.5));
1739 *blue=(double) QuantumRange*(Y+0.66467905997895482*(Db-0.5)-
1740 7.9202543533108e-05*(Dr-0.5));
1741}
1742
1743static inline void ConvertYIQToRGB(const double Y,const double I,const double Q,
1744 double *red,double *green,double *blue)
1745{
1746 *red=(double) QuantumRange*(Y+0.9562957197589482261*(I-0.5)+
1747 0.6210244164652610754*(Q-0.5));
1748 *green=(double) QuantumRange*(Y-0.2721220993185104464*(I-0.5)-
1749 0.6473805968256950427*(Q-0.5));
1750 *blue=(double) QuantumRange*(Y-1.1069890167364901945*(I-0.5)+
1751 1.7046149983646481374*(Q-0.5));
1752}
1753
1754static inline void ConvertxyYToRGB(const double low_x,const double low_y,
1755 const double cap_Y,double *red,double *green,double *blue)
1756{
1757 double
1758 gamma,
1759 X,
1760 Y,
1761 Z;
1762
1763 gamma=PerceptibleReciprocal(low_y);
1764 X=gamma*cap_Y*low_x;
1765 Y=cap_Y;
1766 Z=gamma*cap_Y*(1.0-low_x-low_y);
1767 ConvertXYZToRGB(X,Y,Z,red,green,blue);
1768}
1769
1770static inline void ConvertYUVToRGB(const double Y,const double U,const double V,
1771 double *red,double *green,double *blue)
1772{
1773 *red=(double) QuantumRange*(Y-3.945707070708279e-05*(U-0.5)+
1774 1.1398279671717170825*(V-0.5));
1775 *green=(double) QuantumRange*(Y-0.3946101641414141437*(U-0.5)-
1776 0.5805003156565656797*(V-0.5));
1777 *blue=(double) QuantumRange*(Y+2.0319996843434342537*(U-0.5)-
1778 4.813762626262513e-04*(V-0.5));
1779}
1780
1781static inline MagickBooleanType IsCMYKColorspace(
1782 const ColorspaceType colorspace)
1783{
1784 if (colorspace == CMYKColorspace)
1785 return(MagickTrue);
1786 return(MagickFalse);
1787}
1788
1789static inline MagickBooleanType IsGrayColorspace(
1790 const ColorspaceType colorspace)
1791{
1792 if ((colorspace == LinearGRAYColorspace) || (colorspace == GRAYColorspace))
1793 return(MagickTrue);
1794 return(MagickFalse);
1795}
1796
1797static inline MagickBooleanType IsGrayImageType(const ImageType type)
1798{
1799 if ((type == GrayscaleType) || (type == GrayscaleAlphaType) ||
1800 (type == BilevelType))
1801 return(MagickTrue);
1802 return(MagickFalse);
1803}
1804
1805static inline MagickBooleanType IsHueCompatibleColorspace(
1806 const ColorspaceType colorspace)
1807{
1808 if ((colorspace == HCLColorspace) || (colorspace == HCLpColorspace) ||
1809 (colorspace == HSBColorspace) || (colorspace == HSIColorspace) ||
1810 (colorspace == HSLColorspace) || (colorspace == HSVColorspace))
1811 return(MagickTrue);
1812 return(MagickFalse);
1813}
1814
1815static inline MagickBooleanType IsLabCompatibleColorspace(
1816 const ColorspaceType colorspace)
1817{
1818 if ((colorspace == LabColorspace) || (colorspace == LCHColorspace) ||
1819 (colorspace == LCHabColorspace) || (colorspace == LCHuvColorspace) ||
1820 (colorspace == OklabColorspace) || (colorspace == OklchColorspace))
1821 return(MagickTrue);
1822 return(MagickFalse);
1823}
1824
1825static inline MagickBooleanType IsRGBColorspace(const ColorspaceType colorspace)
1826{
1827 if ((colorspace == RGBColorspace) || (colorspace == scRGBColorspace) ||
1828 (colorspace == LinearGRAYColorspace))
1829 return(MagickTrue);
1830 return(MagickFalse);
1831}
1832
1833static inline MagickBooleanType IssRGBColorspace(
1834 const ColorspaceType colorspace)
1835{
1836 if ((colorspace == sRGBColorspace) || (colorspace == TransparentColorspace))
1837 return(MagickTrue);
1838 return(MagickFalse);
1839}
1840
1841static inline MagickBooleanType IssRGBCompatibleColorspace(
1842 const ColorspaceType colorspace)
1843{
1844 if ((colorspace == sRGBColorspace) || (colorspace == RGBColorspace) ||
1845 (colorspace == Adobe98Colorspace) || (colorspace == ProPhotoColorspace) ||
1846 (colorspace == DisplayP3Colorspace) || (colorspace == scRGBColorspace) ||
1847 (colorspace == TransparentColorspace) || (colorspace == GRAYColorspace) ||
1848 (colorspace == LinearGRAYColorspace))
1849 return(MagickTrue);
1850 return(MagickFalse);
1851}
1852
1853static inline MagickBooleanType IsYCbCrCompatibleColorspace(
1854 const ColorspaceType colorspace)
1855{
1856 if ((colorspace == YCbCrColorspace) ||
1857 (colorspace == Rec709YCbCrColorspace) ||
1858 (colorspace == Rec601YCbCrColorspace))
1859 return(MagickTrue);
1860 return(MagickFalse);
1861}
1862
1863extern MagickPrivate void
1864 ConvertGenericToRGB(const ColorspaceType,const double,const double,
1865 const double,const double,const IlluminantType,double *,double *,double *),
1866 ConvertRGBToGeneric(const ColorspaceType,const double,const double,
1867 const double,const double,const IlluminantType,double *,double *,double *);
1868
1869#if defined(__cplusplus) || defined(c_plusplus)
1870}
1871#endif
1872
1873#endif