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
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
1287 double
1288 gamma,
1289 Iz,
1290 L,
1291 Lp,
1292 M,
1293 Mp,
1294 S,
1295 Sp,
1296 Xp,
1297 Yp,
1298 Zp,
1299 J,
1300 a,
1301 b;
1302
1303 Xp=(Jzazbz_b*X-(Jzazbz_b-1)*Z);
1304 Yp=(Jzazbz_g*Y-(Jzazbz_g-1)*X);
1305 Zp=Z;
1306 L=0.41478972*Xp+0.579999*Yp+0.0146480*Zp;
1307 M=(-0.2015100)*Xp+1.120649*Yp+0.0531008*Zp;
1308 S=(-0.0166008)*Xp+0.264800*Yp+0.6684799*Zp;
1309 gamma=pow(L*PerceptibleReciprocal(white_luminance),Jzazbz_n);
1310 Lp=pow((Jzazbz_c1+Jzazbz_c2*gamma)/(1.0+Jzazbz_c3*gamma),Jzazbz_p);
1311 gamma=pow(M*PerceptibleReciprocal(white_luminance),Jzazbz_n);
1312 Mp=pow((Jzazbz_c1+Jzazbz_c2*gamma)/(1.0+Jzazbz_c3*gamma),Jzazbz_p);
1313 gamma=pow(S*PerceptibleReciprocal(white_luminance),Jzazbz_n);
1314 Sp=pow((Jzazbz_c1+Jzazbz_c2*gamma)/(1.0+Jzazbz_c3*gamma),Jzazbz_p);
1315 Iz=0.5*Lp+0.5*Mp;
1316 J=((Jzazbz_d+1.0)*Iz)/(Jzazbz_d*Iz+1.0)-Jzazbz_d0;
1317 if (IsNaN(J) != 0)
1318 J=0.0;
1319 a=3.52400*Lp-4.066708*Mp+0.542708*Sp+0.5;
1320 if (IsNaN(a) != 0)
1321 a=0.5;
1322 b=0.199076*Lp+1.096799*Mp-1.295875*Sp+0.5;
1323 if (IsNaN(b) != 0)
1324 b=0.5;
1325 *Jz=J;
1326 *az=a;
1327 *bz=b;
1328}
1329
1330static inline void ConvertJzazbzToXYZ(const double Jz,const double az,
1331 const double bz,const double white_luminance,double *X,double *Y,double *Z)
1332{
1333 double
1334 azz,
1335 bzz,
1336 gamma,
1337 Iz,
1338 L,
1339 Lp,
1340 M,
1341 Mp,
1342 S,
1343 Sp,
1344 Xp,
1345 Yp,
1346 Zp;
1347
1348 gamma=Jz+Jzazbz_d0;
1349 Iz=gamma/(Jzazbz_d-Jzazbz_d*gamma+1.0);
1350 azz=az-0.5;
1351 bzz=bz-0.5;
1352 Lp=Iz+0.138605043271539*azz+0.0580473161561189*bzz;
1353 Mp=Iz-0.138605043271539*azz-0.0580473161561189*bzz;
1354 Sp=Iz-0.0960192420263189*azz-0.811891896056039*bzz;
1355 gamma=pow(Lp,1.0/Jzazbz_p);
1356 L=white_luminance*pow((Jzazbz_c1-gamma)/(Jzazbz_c3*gamma-Jzazbz_c2),1.0/
1357 Jzazbz_n);
1358 gamma=pow(Mp,1.0/Jzazbz_p);
1359 M=white_luminance*pow((Jzazbz_c1-gamma)/(Jzazbz_c3*gamma-Jzazbz_c2),1.0/
1360 Jzazbz_n);
1361 gamma=pow(Sp,1.0/Jzazbz_p);
1362 S=white_luminance*pow((Jzazbz_c1-gamma)/(Jzazbz_c3*gamma-Jzazbz_c2),1.0/
1363 Jzazbz_n);
1364 Xp=1.92422643578761*L-1.00479231259537*M+0.037651404030618*S;
1365 Yp=0.350316762094999*L+0.726481193931655*M-0.065384422948085*S;
1366 Zp=(-0.0909828109828476)*L-0.312728290523074*M+1.52276656130526*S;
1367 if (IsNaN(Zp) != 0)
1368 Zp=0.0;
1369 Xp=(Xp+(Jzazbz_b-1.0)*Zp)/Jzazbz_b;
1370 if (IsNaN(Xp) != 0)
1371 Xp=0.0;
1372 Yp=(Yp+(Jzazbz_g-1.0)*Xp)/Jzazbz_g;
1373 if (IsNaN(Yp) != 0)
1374 Yp=0.0;
1375 *X=Xp;
1376 *Y=Yp;
1377 *Z=Zp;
1378}
1379
1380static inline void ConvertRGBToJzazbz(const double red,const double green,
1381 const double blue,const double white_luminance,double *Jz,double *az,
1382 double *bz)
1383{
1384 double
1385 X,
1386 Y,
1387 Z;
1388
1389 ConvertRGBToXYZ(red,blue,green,&X,&Y,&Z);
1390 ConvertXYZToJzazbz(X,Y,Z,white_luminance,Jz,az,bz);
1391}
1392
1393static inline void ConvertJzazbzToRGB(const double Jz,const double az,
1394 const double bz,const double white_luminance,double *red,double *green,
1395 double *blue)
1396{
1397 double
1398 X,
1399 Y,
1400 Z;
1401
1402 ConvertJzazbzToXYZ(Jz,az,bz,white_luminance,&X,&Y,&Z);
1403 ConvertXYZToRGB(X,Y,Z,red,blue,green);
1404}
1405
1406static inline void ConvertOklabToRGB(const double L,const double a,
1407 const double b,double *red,double *green,double *blue)
1408{
1409 double
1410 B,
1411 G,
1412 l,
1413 m,
1414 R,
1415 s;
1416
1417 l=L+0.3963377774*(a-0.5)+0.2158037573*(b-0.5);
1418 m=L-0.1055613458*(a-0.5)-0.0638541728*(b-0.5);
1419 s=L-0.0894841775*(a-0.5)-1.2914855480*(b-0.5);
1420 l*=l*l;
1421 m*=m*m;
1422 s*=s*s;
1423 R=4.0767416621*l-3.3077115913*m+0.2309699292*s;
1424 G=(-1.2684380046)*l+2.6097574011*m-0.3413193965*s;
1425 B=(-0.0041960863)*l-0.7034186147*m+1.7076147010*s;
1426 *red=EncodePixelGamma((double) QuantumRange*R);
1427 *green=EncodePixelGamma((double) QuantumRange*G);
1428 *blue=EncodePixelGamma((double) QuantumRange*B);
1429}
1430
1431static inline void ConvertRGBToOklab(const double red,const double green,
1432 const double blue,double *L,double *a,double *b)
1433{
1434 double
1435 B,
1436 G,
1437 l,
1438 m,
1439 R,
1440 s;
1441
1442 R=QuantumScale*DecodePixelGamma(red);
1443 G=QuantumScale*DecodePixelGamma(green);
1444 B=QuantumScale*DecodePixelGamma(blue);
1445 l=cbrt(0.4122214708*R+0.5363325363*G+0.0514459929*B);
1446 m=cbrt(0.2119034982*R+0.6806995451*G+0.1073969566*B);
1447 s=cbrt(0.0883024619*R+0.2817188376*G+0.6299787005*B);
1448 *L=0.2104542553*l+0.7936177850*m-0.0040720468*s;
1449 *a=1.9779984951*l-2.4285922050*m+0.4505937099*s+0.5;
1450 *b=0.0259040371*l+0.7827717662*m-0.8086757660*s+0.5;
1451}
1452
1453static inline void ConvertOklchToRGB(const double L,const double C,
1454 const double h,double *red,double *green,double *blue)
1455{
1456 double
1457 a,
1458 b;
1459
1460 a=C*cos(2.0*MagickPI*h);
1461 b=C*sin(2.0*MagickPI*h);
1462 ConvertOklabToRGB(L,a,b,red,green,blue);
1463}
1464
1465static inline void ConvertRGBToOklch(const double red,const double green,
1466 const double blue,double *L,double *C,double *h)
1467{
1468 double
1469 a,
1470 b;
1471
1472 ConvertRGBToOklab(red,green,blue,L,&a,&b);
1473 *C=sqrt(a*a+b*b);
1474 *h=0.5+0.5*atan2(-b,-a)/MagickPI;
1475}
1476
1477static inline void ConvertRGBToYDbDr(const double red,const double green,
1478 const double blue,double *Y,double *Db,double *Dr)
1479{
1480 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1481 *Db=QuantumScale*(-0.450*red-0.883*green+1.333*blue)+0.5;
1482 *Dr=QuantumScale*(-1.333*red+1.116*green+0.217*blue)+0.5;
1483}
1484
1485static inline void ConvertRGBToYIQ(const double red,const double green,
1486 const double blue,double *Y,double *I,double *Q)
1487{
1488 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1489 *I=QuantumScale*(0.595716*red-0.274453*green-0.321263*blue)+0.5;
1490 *Q=QuantumScale*(0.211456*red-0.522591*green+0.311135*blue)+0.5;
1491}
1492
1493static inline void ConvertRGBToYPbPr(const double red,const double green,
1494 const double blue,double *Y,double *Pb,double *Pr)
1495{
1496 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1497 *Pb=QuantumScale*((-0.1687367)*red-0.331264*green+0.5*blue)+0.5;
1498 *Pr=QuantumScale*(0.5*red-0.418688*green-0.081312*blue)+0.5;
1499}
1500
1501static inline void ConvertRGBToYCbCr(const double red,const double green,
1502 const double blue,double *Y,double *Cb,double *Cr)
1503{
1504 ConvertRGBToYPbPr(red,green,blue,Y,Cb,Cr);
1505}
1506
1507static inline void ConvertRGBToYUV(const double red,const double green,
1508 const double blue,double *Y,double *U,double *V)
1509{
1510 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1511 *U=QuantumScale*((-0.147)*red-0.289*green+0.436*blue)+0.5;
1512 *V=QuantumScale*(0.615*red-0.515*green-0.100*blue)+0.5;
1513}
1514
1515static inline void ConvertRGBToCMYK(PixelInfo *pixel)
1516{
1517 MagickRealType
1518 black,
1519 blue,
1520 cyan,
1521 green,
1522 magenta,
1523 red,
1524 yellow;
1525
1526 if (pixel->colorspace != sRGBColorspace)
1527 {
1528 red=QuantumScale*pixel->red;
1529 green=QuantumScale*pixel->green;
1530 blue=QuantumScale*pixel->blue;
1531 }
1532 else
1533 {
1534 red=QuantumScale*DecodePixelGamma(pixel->red);
1535 green=QuantumScale*DecodePixelGamma(pixel->green);
1536 blue=QuantumScale*DecodePixelGamma(pixel->blue);
1537 }
1538 if ((fabs((double) red) < MagickEpsilon) &&
1539 (fabs((double) green) < MagickEpsilon) &&
1540 (fabs((double) blue) < MagickEpsilon))
1541 {
1542 pixel->black=(MagickRealType) QuantumRange;
1543 return;
1544 }
1545 cyan=(MagickRealType) (1.0-red);
1546 magenta=(MagickRealType) (1.0-green);
1547 yellow=(MagickRealType) (1.0-blue);
1548 black=cyan;
1549 if (magenta < black)
1550 black=magenta;
1551 if (yellow < black)
1552 black=yellow;
1553 cyan=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(cyan-black));
1554 magenta=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(magenta-black));
1555 yellow=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(yellow-black));
1556 pixel->colorspace=CMYKColorspace;
1557 pixel->red=(MagickRealType) QuantumRange*cyan;
1558 pixel->green=(MagickRealType) QuantumRange*magenta;
1559 pixel->blue=(MagickRealType) QuantumRange*yellow;
1560 pixel->black=(MagickRealType) QuantumRange*black;
1561}
1562
1563static inline void ConvertYPbPrToRGB(const double Y,const double Pb,
1564 const double Pr,double *red,double *green,double *blue)
1565{
1566 *red=(double) QuantumRange*(0.99999999999914679361*Y-1.2188941887145875e-06*
1567 (Pb-0.5)+1.4019995886561440468*(Pr-0.5));
1568 *green=(double) QuantumRange*(0.99999975910502514331*Y-0.34413567816504303521*
1569 (Pb-0.5)-0.71413649331646789076*(Pr-0.5));
1570 *blue=(double) QuantumRange*(1.00000124040004623180*Y+1.77200006607230409200*
1571 (Pb-0.5)+2.1453384174593273e-06*(Pr-0.5));
1572}
1573
1574static inline void ConvertYCbCrToRGB(const double Y,const double Cb,
1575 const double Cr,double *red,double *green,double *blue)
1576{
1577 ConvertYPbPrToRGB(Y,Cb,Cr,red,green,blue);
1578}
1579
1580static inline void ConvertYDbDrToRGB(const double Y,const double Db,
1581 const double Dr,double *red,double *green,double *blue)
1582{
1583 *red=(double) QuantumRange*(Y+9.2303716147657e-05*(Db-0.5)-
1584 0.52591263066186533*(Dr-0.5));
1585 *green=(double) QuantumRange*(Y-0.12913289889050927*(Db-0.5)+
1586 0.26789932820759876*(Dr-0.5));
1587 *blue=(double) QuantumRange*(Y+0.66467905997895482*(Db-0.5)-
1588 7.9202543533108e-05*(Dr-0.5));
1589}
1590
1591static inline void ConvertYIQToRGB(const double Y,const double I,const double Q,
1592 double *red,double *green,double *blue)
1593{
1594 *red=(double) QuantumRange*(Y+0.9562957197589482261*(I-0.5)+
1595 0.6210244164652610754*(Q-0.5));
1596 *green=(double) QuantumRange*(Y-0.2721220993185104464*(I-0.5)-
1597 0.6473805968256950427*(Q-0.5));
1598 *blue=(double) QuantumRange*(Y-1.1069890167364901945*(I-0.5)+
1599 1.7046149983646481374*(Q-0.5));
1600}
1601
1602static inline void ConvertxyYToRGB(const double low_x,const double low_y,
1603 const double cap_Y,double *red,double *green,double *blue)
1604{
1605 double
1606 gamma,
1607 X,
1608 Y,
1609 Z;
1610
1611 gamma=PerceptibleReciprocal(low_y);
1612 X=gamma*cap_Y*low_x;
1613 Y=cap_Y;
1614 Z=gamma*cap_Y*(1.0-low_x-low_y);
1615 ConvertXYZToRGB(X,Y,Z,red,green,blue);
1616}
1617
1618static inline void ConvertYUVToRGB(const double Y,const double U,const double V,
1619 double *red,double *green,double *blue)
1620{
1621 *red=(double) QuantumRange*(Y-3.945707070708279e-05*(U-0.5)+
1622 1.1398279671717170825*(V-0.5));
1623 *green=(double) QuantumRange*(Y-0.3946101641414141437*(U-0.5)-
1624 0.5805003156565656797*(V-0.5));
1625 *blue=(double) QuantumRange*(Y+2.0319996843434342537*(U-0.5)-
1626 4.813762626262513e-04*(V-0.5));
1627}
1628
1629static inline MagickBooleanType IsCMYKColorspace(
1630 const ColorspaceType colorspace)
1631{
1632 if (colorspace == CMYKColorspace)
1633 return(MagickTrue);
1634 return(MagickFalse);
1635}
1636
1637static inline MagickBooleanType IsGrayColorspace(
1638 const ColorspaceType colorspace)
1639{
1640 if ((colorspace == LinearGRAYColorspace) || (colorspace == GRAYColorspace))
1641 return(MagickTrue);
1642 return(MagickFalse);
1643}
1644
1645static inline MagickBooleanType IsGrayImageType(const ImageType type)
1646{
1647 if ((type == GrayscaleType) || (type == GrayscaleAlphaType) ||
1648 (type == BilevelType))
1649 return(MagickTrue);
1650 return(MagickFalse);
1651}
1652
1653static inline MagickBooleanType IsHueCompatibleColorspace(
1654 const ColorspaceType colorspace)
1655{
1656 if ((colorspace == HCLColorspace) || (colorspace == HCLpColorspace) ||
1657 (colorspace == HSBColorspace) || (colorspace == HSIColorspace) ||
1658 (colorspace == HSLColorspace) || (colorspace == HSVColorspace))
1659 return(MagickTrue);
1660 return(MagickFalse);
1661}
1662
1663static inline MagickBooleanType IsLabCompatibleColorspace(
1664 const ColorspaceType colorspace)
1665{
1666 if ((colorspace == LabColorspace) || (colorspace == LCHColorspace) ||
1667 (colorspace == LCHabColorspace) || (colorspace == LCHuvColorspace) ||
1668 (colorspace == OklabColorspace) || (colorspace == OklchColorspace))
1669 return(MagickTrue);
1670 return(MagickFalse);
1671}
1672
1673static inline MagickBooleanType IsRGBColorspace(const ColorspaceType colorspace)
1674{
1675 if ((colorspace == RGBColorspace) || (colorspace == scRGBColorspace) ||
1676 (colorspace == LinearGRAYColorspace))
1677 return(MagickTrue);
1678 return(MagickFalse);
1679}
1680
1681static inline MagickBooleanType IssRGBColorspace(
1682 const ColorspaceType colorspace)
1683{
1684 if ((colorspace == sRGBColorspace) || (colorspace == TransparentColorspace))
1685 return(MagickTrue);
1686 return(MagickFalse);
1687}
1688
1689static inline MagickBooleanType IssRGBCompatibleColorspace(
1690 const ColorspaceType colorspace)
1691{
1692 if ((colorspace == sRGBColorspace) || (colorspace == RGBColorspace) ||
1693 (colorspace == Adobe98Colorspace) || (colorspace == ProPhotoColorspace) ||
1694 (colorspace == DisplayP3Colorspace) || (colorspace == scRGBColorspace) ||
1695 (colorspace == TransparentColorspace) || (colorspace == GRAYColorspace) ||
1696 (colorspace == LinearGRAYColorspace))
1697 return(MagickTrue);
1698 return(MagickFalse);
1699}
1700
1701static inline MagickBooleanType IsYCbCrCompatibleColorspace(
1702 const ColorspaceType colorspace)
1703{
1704 if ((colorspace == YCbCrColorspace) ||
1705 (colorspace == Rec709YCbCrColorspace) ||
1706 (colorspace == Rec601YCbCrColorspace))
1707 return(MagickTrue);
1708 return(MagickFalse);
1709}
1710
1711extern MagickPrivate void
1712 ConvertGenericToRGB(const ColorspaceType,const double,const double,
1713 const double,const double,const IlluminantType,double *,double *,double *),
1714 ConvertRGBToGeneric(const ColorspaceType,const double,const double,
1715 const double,const double,const IlluminantType,double *,double *,double *);
1716
1717#if defined(__cplusplus) || defined(c_plusplus)
1718}
1719#endif
1720
1721#endif