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 r;
79
80 r=3.2404542*X-1.5371385*Y-0.4985314*Z;
81 g=(-0.9692660)*X+1.8760108*Y+0.0415560*Z;
82 b=0.0556434*X-0.2040259*Y+1.0572252*Z;
83 *red=EncodePixelGamma((double) QuantumRange*r);
84 *green=EncodePixelGamma((double) QuantumRange*g);
85 *blue=EncodePixelGamma((double) QuantumRange*b);
86}
87
88static inline void ConvertAdobe98ToRGB(const double r,const double g,
89 const double b,double *red,double *green,double *blue)
90{
91 double
92 X,
93 Y,
94 Z;
95
96 ConvertAdobe98ToXYZ(r,g,b,&X,&Y,&Z);
97 ConvertXYZToRGB(X,Y,Z,red,green,blue);
98}
99
100static inline void ConvertCMYKToRGB(PixelInfo *pixel)
101{
102 pixel->red=(((double) QuantumRange-(QuantumScale*pixel->red*
103 ((double) QuantumRange-pixel->black)+pixel->black)));
104 pixel->green=(((double) QuantumRange-(QuantumScale*pixel->green*
105 ((double) QuantumRange-pixel->black)+pixel->black)));
106 pixel->blue=(((double) QuantumRange-(QuantumScale*pixel->blue*
107 ((double) QuantumRange-pixel->black)+pixel->black)));
108}
109
110static inline void ConvertCMYToRGB(const double cyan,const double magenta,
111 const double yellow,double *red,double *green,double *blue)
112{
113 *red=(double) QuantumRange*(1.0-cyan);
114 *green=(double) QuantumRange*(1.0-magenta);
115 *blue=(double) QuantumRange*(1.0-yellow);
116}
117
118static inline void ConvertHCLToRGB(const double hue,const double chroma,
119 const double luma,double *red,double *green,double *blue)
120{
121 double
122 b,
123 c,
124 g,
125 h,
126 m,
127 r,
128 x;
129
130 /*
131 Convert HCL to RGB colorspace.
132 */
133 assert(red != (double *) NULL);
134 assert(green != (double *) NULL);
135 assert(blue != (double *) NULL);
136 h=6.0*hue;
137 c=chroma;
138 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
139 r=0.0;
140 g=0.0;
141 b=0.0;
142 if ((0.0 <= h) && (h < 1.0))
143 {
144 r=c;
145 g=x;
146 }
147 else
148 if ((1.0 <= h) && (h < 2.0))
149 {
150 r=x;
151 g=c;
152 }
153 else
154 if ((2.0 <= h) && (h < 3.0))
155 {
156 g=c;
157 b=x;
158 }
159 else
160 if ((3.0 <= h) && (h < 4.0))
161 {
162 g=x;
163 b=c;
164 }
165 else
166 if ((4.0 <= h) && (h < 5.0))
167 {
168 r=x;
169 b=c;
170 }
171 else
172 if ((5.0 <= h) && (h < 6.0))
173 {
174 r=c;
175 b=x;
176 }
177 m=luma-(0.298839*r+0.586811*g+0.114350*b);
178 *red=(double) QuantumRange*(r+m);
179 *green=(double) QuantumRange*(g+m);
180 *blue=(double) QuantumRange*(b+m);
181}
182
183static inline void ConvertHCLpToRGB(const double hue,const double chroma,
184 const double luma,double *red,double *green,double *blue)
185{
186 double
187 b,
188 c,
189 g,
190 h,
191 m,
192 r,
193 x,
194 z;
195
196 /*
197 Convert HCLp to RGB colorspace.
198 */
199 assert(red != (double *) NULL);
200 assert(green != (double *) NULL);
201 assert(blue != (double *) NULL);
202 h=6.0*hue;
203 c=chroma;
204 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
205 r=0.0;
206 g=0.0;
207 b=0.0;
208 if ((0.0 <= h) && (h < 1.0))
209 {
210 r=c;
211 g=x;
212 }
213 else
214 if ((1.0 <= h) && (h < 2.0))
215 {
216 r=x;
217 g=c;
218 }
219 else
220 if ((2.0 <= h) && (h < 3.0))
221 {
222 g=c;
223 b=x;
224 }
225 else
226 if ((3.0 <= h) && (h < 4.0))
227 {
228 g=x;
229 b=c;
230 }
231 else
232 if ((4.0 <= h) && (h < 5.0))
233 {
234 r=x;
235 b=c;
236 }
237 else
238 if ((5.0 <= h) && (h < 6.0))
239 {
240 r=c;
241 b=x;
242 }
243 m=luma-(0.298839*r+0.586811*g+0.114350*b);
244 z=1.0;
245 if (m < 0.0)
246 {
247 z=luma/(luma-m);
248 m=0.0;
249 }
250 else
251 if (m+c > 1.0)
252 {
253 z=(1.0-luma)/(m+c-luma);
254 m=1.0-z*c;
255 }
256 *red=(double) QuantumRange*(z*r+m);
257 *green=(double) QuantumRange*(z*g+m);
258 *blue=(double) QuantumRange*(z*b+m);
259}
260
261static inline void ConvertHSBToRGB(const double hue,const double saturation,
262 const double brightness,double *red,double *green,double *blue)
263{
264 double
265 f,
266 h,
267 p,
268 q,
269 t;
270
271 /*
272 Convert HSB to RGB colorspace.
273 */
274 assert(red != (double *) NULL);
275 assert(green != (double *) NULL);
276 assert(blue != (double *) NULL);
277 if (fabs(saturation) < MagickEpsilon)
278 {
279 *red=(double) QuantumRange*brightness;
280 *green=(*red);
281 *blue=(*red);
282 return;
283 }
284 h=6.0*(hue-floor(hue));
285 f=h-floor((double) h);
286 p=brightness*(1.0-saturation);
287 q=brightness*(1.0-saturation*f);
288 t=brightness*(1.0-(saturation*(1.0-f)));
289 switch ((int) h)
290 {
291 case 0:
292 default:
293 {
294 *red=(double) QuantumRange*brightness;
295 *green=(double) QuantumRange*t;
296 *blue=(double) QuantumRange*p;
297 break;
298 }
299 case 1:
300 {
301 *red=(double) QuantumRange*q;
302 *green=(double) QuantumRange*brightness;
303 *blue=(double) QuantumRange*p;
304 break;
305 }
306 case 2:
307 {
308 *red=(double) QuantumRange*p;
309 *green=(double) QuantumRange*brightness;
310 *blue=(double) QuantumRange*t;
311 break;
312 }
313 case 3:
314 {
315 *red=(double) QuantumRange*p;
316 *green=(double) QuantumRange*q;
317 *blue=(double) QuantumRange*brightness;
318 break;
319 }
320 case 4:
321 {
322 *red=(double) QuantumRange*t;
323 *green=(double) QuantumRange*p;
324 *blue=(double) QuantumRange*brightness;
325 break;
326 }
327 case 5:
328 {
329 *red=(double) QuantumRange*brightness;
330 *green=(double) QuantumRange*p;
331 *blue=(double) QuantumRange*q;
332 break;
333 }
334 }
335}
336
337static inline void ConvertHSIToRGB(const double hue,const double saturation,
338 const double intensity,double *red,double *green,double *blue)
339{
340 double
341 b,
342 g,
343 h,
344 r;
345
346 /*
347 Convert HSI to RGB colorspace.
348 */
349 assert(red != (double *) NULL);
350 assert(green != (double *) NULL);
351 assert(blue != (double *) NULL);
352 h=360.0*hue;
353 h-=360.0*floor(h/360.0);
354 if (h < 120.0)
355 {
356 b=intensity*(1.0-saturation);
357 r=intensity*(1.0+saturation*cos(h*(MagickPI/180.0))/cos((60.0-h)*
358 (MagickPI/180.0)));
359 g=3.0*intensity-r-b;
360 }
361 else
362 if (h < 240.0)
363 {
364 h-=120.0;
365 r=intensity*(1.0-saturation);
366 g=intensity*(1.0+saturation*cos(h*(MagickPI/180.0))/cos((60.0-h)*
367 (MagickPI/180.0)));
368 b=3.0*intensity-r-g;
369 }
370 else
371 {
372 h-=240.0;
373 g=intensity*(1.0-saturation);
374 b=intensity*(1.0+saturation*cos(h*(MagickPI/180.0))/cos((60.0-h)*
375 (MagickPI/180.0)));
376 r=3.0*intensity-g-b;
377 }
378 *red=(double) QuantumRange*r;
379 *green=(double) QuantumRange*g;
380 *blue=(double) QuantumRange*b;
381}
382
383static inline void ConvertHSVToRGB(const double hue,const double saturation,
384 const double value,double *red,double *green,double *blue)
385{
386 double
387 c,
388 h,
389 min,
390 x;
391
392 /*
393 Convert HSV to RGB colorspace.
394 */
395 assert(red != (double *) NULL);
396 assert(green != (double *) NULL);
397 assert(blue != (double *) NULL);
398 h=hue*360.0;
399 c=value*saturation;
400 min=value-c;
401 h-=360.0*floor(h/360.0);
402 h/=60.0;
403 x=c*(1.0-fabs(h-2.0*floor(h/2.0)-1.0));
404 switch ((int) floor(h))
405 {
406 case 0:
407 default:
408 {
409 *red=(double) QuantumRange*(min+c);
410 *green=(double) QuantumRange*(min+x);
411 *blue=(double) QuantumRange*min;
412 break;
413 }
414 case 1:
415 {
416 *red=(double) QuantumRange*(min+x);
417 *green=(double) QuantumRange*(min+c);
418 *blue=(double) QuantumRange*min;
419 break;
420 }
421 case 2:
422 {
423 *red=(double) QuantumRange*min;
424 *green=(double) QuantumRange*(min+c);
425 *blue=(double) QuantumRange*(min+x);
426 break;
427 }
428 case 3:
429 {
430 *red=(double) QuantumRange*min;
431 *green=(double) QuantumRange*(min+x);
432 *blue=(double) QuantumRange*(min+c);
433 break;
434 }
435 case 4:
436 {
437 *red=(double) QuantumRange*(min+x);
438 *green=(double) QuantumRange*min;
439 *blue=(double) QuantumRange*(min+c);
440 break;
441 }
442 case 5:
443 {
444 *red=(double) QuantumRange*(min+c);
445 *green=(double) QuantumRange*min;
446 *blue=(double) QuantumRange*(min+x);
447 break;
448 }
449 }
450}
451
452static inline void ConvertHWBToRGB(const double hue,const double whiteness,
453 const double blackness,double *red,double *green,double *blue)
454{
455 double
456 b,
457 f,
458 g,
459 n,
460 r,
461 v;
462
463 ssize_t
464 i;
465
466 /*
467 Convert HWB to RGB colorspace.
468 */
469 assert(red != (double *) NULL);
470 assert(green != (double *) NULL);
471 assert(blue != (double *) NULL);
472 v=1.0-blackness;
473 if (fabs(hue-(-1.0)) < MagickEpsilon)
474 {
475 *red=(double) QuantumRange*v;
476 *green=(double) QuantumRange*v;
477 *blue=(double) QuantumRange*v;
478 return;
479 }
480 i=CastDoubleToLong(floor(6.0*hue));
481 f=6.0*hue-i;
482 if ((i & 0x01) != 0)
483 f=1.0-f;
484 n=whiteness+f*(v-whiteness); /* linear interpolation */
485 switch (i)
486 {
487 case 0:
488 default: r=v; g=n; b=whiteness; break;
489 case 1: r=n; g=v; b=whiteness; break;
490 case 2: r=whiteness; g=v; b=n; break;
491 case 3: r=whiteness; g=n; b=v; break;
492 case 4: r=n; g=whiteness; b=v; break;
493 case 5: r=v; g=whiteness; b=n; break;
494 }
495 *red=(double) QuantumRange*r;
496 *green=(double) QuantumRange*g;
497 *blue=(double) QuantumRange*b;
498}
499
500static inline void ConvertLabToXYZ(const double L,const double a,const double b,
501 const IlluminantType illuminant,double *X,double *Y,double *Z)
502{
503 double
504 x,
505 y,
506 z;
507
508 y=(L+16.0)/116.0;
509 x=y+a/500.0;
510 z=y-b/200.0;
511 if ((x*x*x) > CIEEpsilon)
512 x=(x*x*x);
513 else
514 x=(116.0*x-16.0)/CIEK;
515 if (L > (CIEK*CIEEpsilon))
516 y=(y*y*y);
517 else
518 y=L/CIEK;
519 if ((z*z*z) > CIEEpsilon)
520 z=(z*z*z);
521 else
522 z=(116.0*z-16.0)/CIEK;
523 *X=illuminant_tristimulus[illuminant].x*x;
524 *Y=illuminant_tristimulus[illuminant].y*y;
525 *Z=illuminant_tristimulus[illuminant].z*z;
526}
527
528static inline void ConvertLabToRGB(const double L,const double a,
529 const double b,const IlluminantType illuminant,double *red,double *green,
530 double *blue)
531{
532 double
533 X,
534 Y,
535 Z;
536
537 ConvertLabToXYZ(100.0*L,255.0*(a-0.5),255.0*(b-0.5),illuminant,&X,&Y,&Z);
538 ConvertXYZToRGB(X,Y,Z,red,green,blue);
539}
540
541static inline void ConvertLCHabToXYZ(const double luma,const double chroma,
542 const double hue,const IlluminantType illuminant,double *X,double *Y,
543 double *Z)
544{
545 ConvertLabToXYZ(luma,chroma*cos(DegreesToRadians(hue)),chroma*
546 sin(DegreesToRadians(hue)),illuminant,X,Y,Z);
547}
548
549static inline void ConvertLCHabToRGB(const double luma,const double chroma,
550 const double hue,const IlluminantType illuminant,double *red,double *green,
551 double *blue)
552{
553 double
554 X,
555 Y,
556 Z;
557
558 /*
559 Convert LCHab to RGB colorspace.
560 */
561 assert(red != (double *) NULL);
562 assert(green != (double *) NULL);
563 assert(blue != (double *) NULL);
564 ConvertLCHabToXYZ(100.0*luma,255.0*(chroma-0.5),360.0*hue,illuminant,
565 &X,&Y,&Z);
566 ConvertXYZToRGB(X,Y,Z,red,green,blue);
567}
568
569static inline void ConvertLuvToXYZ(const double L,const double u,const double v,
570 const IlluminantType illuminant,double *X,double *Y,double *Z)
571{
572 double
573 gamma;
574
575 if (L > (CIEK*CIEEpsilon))
576 *Y=(double) pow((L+16.0)/116.0,3.0);
577 else
578 *Y=L/CIEK;
579 gamma=PerceptibleReciprocal((((52.0*L*PerceptibleReciprocal(u+13.0*L*
580 (4.0*illuminant_tristimulus[illuminant].x/
581 (illuminant_tristimulus[illuminant].x+15.0*
582 illuminant_tristimulus[illuminant].y+3.0*
583 illuminant_tristimulus[illuminant].z))))-1.0)/3.0)-(-1.0/3.0));
584 *X=gamma*((*Y*((39.0*L*PerceptibleReciprocal(v+13.0*L*(9.0*
585 illuminant_tristimulus[illuminant].y/
586 (illuminant_tristimulus[illuminant].x+15.0*
587 illuminant_tristimulus[illuminant].y+3.0*
588 illuminant_tristimulus[illuminant].z))))-5.0))+5.0*(*Y));
589 *Z=(*X*(((52.0*L*PerceptibleReciprocal(u+13.0*L*(4.0*
590 illuminant_tristimulus[illuminant].x/
591 (illuminant_tristimulus[illuminant].x+15.0*
592 illuminant_tristimulus[illuminant].y+3.0*
593 illuminant_tristimulus[illuminant].z))))-1.0)/3.0))-5.0*(*Y);
594}
595
596static inline void ConvertLCHuvToXYZ(const double luma,const double chroma,
597 const double hue,const IlluminantType illuminant,double *X,double *Y,
598 double *Z)
599{
600 ConvertLuvToXYZ(luma,chroma*cos(DegreesToRadians(hue)),chroma*
601 sin(DegreesToRadians(hue)),illuminant,X,Y,Z);
602}
603
604static inline void ConvertLCHuvToRGB(const double luma,const double chroma,
605 const double hue,const IlluminantType illuminant,double *red,double *green,
606 double *blue)
607{
608 double
609 X,
610 Y,
611 Z;
612
613 /*
614 Convert LCHuv to RGB colorspace.
615 */
616 assert(red != (double *) NULL);
617 assert(green != (double *) NULL);
618 assert(blue != (double *) NULL);
619 ConvertLCHuvToXYZ(100.0*luma,255.0*(chroma-0.5),360.0*hue,illuminant,
620 &X,&Y,&Z);
621 ConvertXYZToRGB(X,Y,Z,red,green,blue);
622}
623
624static inline void ConvertLMSToXYZ(const double L,const double M,const double S,
625 double *X,double *Y,double *Z)
626{
627 *X=1.096123820835514*L-0.278869000218287*M+0.182745179382773*S;
628 *Y=0.454369041975359*L+0.473533154307412*M+0.072097803717229*S;
629 *Z=(-0.009627608738429)*L-0.005698031216113*M+1.015325639954543*S;
630}
631
632static inline void ConvertLMSToRGB(const double L,const double M,
633 const double S,double *red,double *green,double *blue)
634{
635 double
636 X,
637 Y,
638 Z;
639
640 ConvertLMSToXYZ(L,M,S,&X,&Y,&Z);
641 ConvertXYZToRGB(X,Y,Z,red,green,blue);
642}
643
644static inline void ConvertDisplayP3ToXYZ(const double red,const double green,
645 const double blue,double *X,double *Y,double *Z)
646{
647 double
648 b,
649 g,
650 r;
651
652 /*
653 Convert Display P3 to XYZ colorspace.
654 */
655 r=QuantumScale*DecodePixelGamma((double) QuantumRange*red);
656 g=QuantumScale*DecodePixelGamma((double) QuantumRange*green);
657 b=QuantumScale*DecodePixelGamma((double) QuantumRange*blue);
658 *X=0.4865709486482162*r+0.26566769316909306*g+0.1982172852343625*b;
659 *Y=0.2289745640697488*r+0.69173852183650640*g+0.0792869140937450*b;
660 *Z=0.0000000000000000*r+0.04511338185890264*g+1.0439443689009760*b;
661}
662
663static inline void ConvertDisplayP3ToRGB(const double r,const double g,
664 const double b,double *red,double *green,double *blue)
665{
666 double
667 X,
668 Y,
669 Z;
670
671 ConvertDisplayP3ToXYZ(r,g,b,&X,&Y,&Z);
672 ConvertXYZToRGB(X,Y,Z,red,green,blue);
673}
674
675static inline void ConvertLuvToRGB(const double L,const double u,
676 const double v,const IlluminantType illuminant,double *red,double *green,
677 double *blue)
678{
679 double
680 X,
681 Y,
682 Z;
683
684 ConvertLuvToXYZ(100.0*L,354.0*u-134.0,262.0*v-140.0,illuminant,&X,&Y,&Z);
685 ConvertXYZToRGB(X,Y,Z,red,green,blue);
686}
687
688static inline void ConvertProPhotoToXYZ(const double red,const double green,
689 const double blue,double *X,double *Y,double *Z)
690{
691 double
692 b,
693 g,
694 r;
695
696 /*
697 Convert ProPhoto to XYZ colorspace.
698 */
699 r=QuantumScale*DecodePixelGamma((double) QuantumRange*red);
700 g=QuantumScale*DecodePixelGamma((double) QuantumRange*green);
701 b=QuantumScale*DecodePixelGamma((double) QuantumRange*blue);
702 *X=0.4865709486482162*r+0.26566769316909306*g+0.1982172852343625*b;
703 *X=0.7977604896723027*r+0.13518583717574031*g+0.03134934958152480000*b;
704 *Y=0.2880711282292934*r+0.71184321781010140*g+0.00008565396060525902*b;
705 *Z=0.0000000000000000*r+0.00000000000000000*g+0.82510460251046010000*b;
706}
707
708static inline void ConvertProPhotoToRGB(const double r,const double g,
709 const double b,double *red,double *green,double *blue)
710{
711 double
712 X,
713 Y,
714 Z;
715
716 ConvertProPhotoToXYZ(r,g,b,&X,&Y,&Z);
717 ConvertXYZToRGB(X,Y,Z,red,green,blue);
718}
719
720static inline void ConvertRGBToCMY(const double red,const double green,
721 const double blue,double *cyan,double *magenta,double *yellow)
722{
723 *cyan=QuantumScale*((double) QuantumRange-red);
724 *magenta=QuantumScale*((double) QuantumRange-green);
725 *yellow=QuantumScale*((double) QuantumRange-blue);
726}
727
728static inline void ConvertRGBToHCL(const double red,const double green,
729 const double blue,double *hue,double *chroma,double *luma)
730{
731 double
732 c,
733 h,
734 max;
735
736 /*
737 Convert RGB to HCL colorspace.
738 */
739 assert(hue != (double *) NULL);
740 assert(chroma != (double *) NULL);
741 assert(luma != (double *) NULL);
742 max=MagickMax(red,MagickMax(green,blue));
743 c=max-(double) MagickMin(red,MagickMin(green,blue));
744 h=0.0;
745 if (fabs(c) < MagickEpsilon)
746 h=0.0;
747 else
748 if (fabs(red-max) < MagickEpsilon)
749 h=fmod((green-blue)/c+6.0,6.0);
750 else
751 if (fabs(green-max) < MagickEpsilon)
752 h=((blue-red)/c)+2.0;
753 else
754 if (fabs(blue-max) < MagickEpsilon)
755 h=((red-green)/c)+4.0;
756 *hue=(h/6.0);
757 *chroma=QuantumScale*c;
758 *luma=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
759}
760
761static inline void ConvertRGBToHCLp(const double red,const double green,
762 const double blue,double *hue,double *chroma,double *luma)
763{
764 double
765 c,
766 h,
767 max;
768
769 /*
770 Convert RGB to HCL colorspace.
771 */
772 assert(hue != (double *) NULL);
773 assert(chroma != (double *) NULL);
774 assert(luma != (double *) NULL);
775 max=MagickMax(red,MagickMax(green,blue));
776 c=max-MagickMin(red,MagickMin(green,blue));
777 h=0.0;
778 if (fabs(c) < MagickEpsilon)
779 h=0.0;
780 else
781 if (fabs(red-max) < MagickEpsilon)
782 h=fmod((green-blue)/c+6.0,6.0);
783 else
784 if (fabs(green-max) < MagickEpsilon)
785 h=((blue-red)/c)+2.0;
786 else
787 if (fabs(blue-max) < MagickEpsilon)
788 h=((red-green)/c)+4.0;
789 *hue=(h/6.0);
790 *chroma=QuantumScale*c;
791 *luma=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
792}
793
794static inline void ConvertRGBToHSB(const double red,const double green,
795 const double blue,double *hue,double *saturation,double *brightness)
796{
797 double
798 delta,
799 max,
800 min;
801
802 /*
803 Convert RGB to HSB colorspace.
804 */
805 assert(hue != (double *) NULL);
806 assert(saturation != (double *) NULL);
807 assert(brightness != (double *) NULL);
808 *hue=0.0;
809 *saturation=0.0;
810 *brightness=0.0;
811 min=red < green ? red : green;
812 if (blue < min)
813 min=blue;
814 max=red > green ? red : green;
815 if (blue > max)
816 max=blue;
817 if (fabs(max) < MagickEpsilon)
818 return;
819 delta=max-min;
820 *saturation=delta/max;
821 *brightness=QuantumScale*max;
822 if (fabs(delta) < MagickEpsilon)
823 return;
824 if (fabs(red-max) < MagickEpsilon)
825 *hue=(green-blue)/delta;
826 else
827 if (fabs(green-max) < MagickEpsilon)
828 *hue=2.0+(blue-red)/delta;
829 else
830 *hue=4.0+(red-green)/delta;
831 *hue/=6.0;
832 if (*hue < 0.0)
833 *hue+=1.0;
834}
835
836static inline void ConvertRGBToHSI(const double red,const double green,
837 const double blue,double *hue,double *saturation,double *intensity)
838{
839 double
840 alpha,
841 beta;
842
843 /*
844 Convert RGB to HSI colorspace.
845 */
846 assert(hue != (double *) NULL);
847 assert(saturation != (double *) NULL);
848 assert(intensity != (double *) NULL);
849 *intensity=(QuantumScale*red+QuantumScale*green+QuantumScale*blue)/3.0;
850 if (*intensity <= 0.0)
851 {
852 *hue=0.0;
853 *saturation=0.0;
854 return;
855 }
856 *saturation=1.0-MagickMin(QuantumScale*red,MagickMin(QuantumScale*green,
857 QuantumScale*blue))/(*intensity);
858 alpha=0.5*(2.0*QuantumScale*red-QuantumScale*green-QuantumScale*blue);
859 beta=0.8660254037844385*(QuantumScale*green-QuantumScale*blue);
860 *hue=atan2(beta,alpha)*(180.0/MagickPI)/360.0;
861 if (*hue < 0.0)
862 *hue+=1.0;
863}
864
865static inline void ConvertRGBToXYZ(const double red,const double green,
866 const double blue,double *X,double *Y,double *Z)
867{
868 double
869 b,
870 g,
871 r;
872
873 /*
874 Convert RGB to XYZ colorspace.
875 */
876 r=QuantumScale*DecodePixelGamma(red);
877 g=QuantumScale*DecodePixelGamma(green);
878 b=QuantumScale*DecodePixelGamma(blue);
879 *X=0.4124564*r+0.3575761*g+0.1804375*b;
880 *Y=0.2126729*r+0.7151522*g+0.0721750*b;
881 *Z=0.0193339*r+0.1191920*g+0.9503041*b;
882}
883
884static inline void ConvertXYZToAdobe98(const double X,const double Y,
885 const double Z,double *red,double *green,double *blue)
886{
887 double
888 b,
889 g,
890 r;
891
892 r=2.041587903810746500*X-0.56500697427885960*Y-0.34473135077832956*Z;
893 g=(-0.969243636280879500)*X+1.87596750150772020*Y+0.04155505740717557*Z;
894 b=0.013444280632031142*X-0.11836239223101838*Y+1.01517499439120540*Z;
895 *red=QuantumScale*EncodePixelGamma((double) QuantumRange*r);
896 *green=QuantumScale*EncodePixelGamma((double) QuantumRange*g);
897 *blue=QuantumScale*EncodePixelGamma((double) QuantumRange*b);
898}
899
900static inline void ConvertRGBToAdobe98(const double red,const double green,
901 const double blue,double *r,double *g,double *b)
902{
903 double
904 X,
905 Y,
906 Z;
907
908 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
909 ConvertXYZToAdobe98(X,Y,Z,r,g,b);
910}
911
912static inline void ConvertXYZToDisplayP3(const double X,const double Y,
913 const double Z,double *red,double *green,double *blue)
914{
915 double
916 b,
917 g,
918 r;
919
920 r=2.49349691194142500*X-0.93138361791912390*Y-0.402710784450716840*Z;
921 g=(-0.82948896956157470)*X+1.76266406031834630*Y+0.023624685841943577*Z;
922 b=0.03584583024378447*X-0.07617238926804182*Y+0.956884524007687200*Z;
923 *red=QuantumScale*EncodePixelGamma((double) QuantumRange*r);
924 *green=QuantumScale*EncodePixelGamma((double) QuantumRange*g);
925 *blue=QuantumScale*EncodePixelGamma((double) QuantumRange*b);
926}
927
928static inline void ConvertRGBToDisplayP3(const double red,const double green,
929 const double blue,double *r,double *g,double *b)
930{
931 double
932 X,
933 Y,
934 Z;
935
936 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
937 ConvertXYZToDisplayP3(X,Y,Z,r,g,b);
938}
939
940static inline void ConvertRGBToHSV(const double red,const double green,
941 const double blue,double *hue,double *saturation,double *value)
942{
943 double
944 c,
945 max,
946 min;
947
948 /*
949 Convert RGB to HSV colorspace.
950 */
951 assert(hue != (double *) NULL);
952 assert(saturation != (double *) NULL);
953 assert(value != (double *) NULL);
954 max=MagickMax(QuantumScale*red,MagickMax(QuantumScale*green,
955 QuantumScale*blue));
956 min=MagickMin(QuantumScale*red,MagickMin(QuantumScale*green,
957 QuantumScale*blue));
958 c=max-min;
959 *value=max;
960 if (c <= 0.0)
961 {
962 *hue=0.0;
963 *saturation=0.0;
964 return;
965 }
966 if (fabs(max-QuantumScale*red) < MagickEpsilon)
967 {
968 *hue=(QuantumScale*green-QuantumScale*blue)/c;
969 if ((QuantumScale*green) < (QuantumScale*blue))
970 *hue+=6.0;
971 }
972 else
973 if (fabs(max-QuantumScale*green) < MagickEpsilon)
974 *hue=2.0+(QuantumScale*blue-QuantumScale*red)/c;
975 else
976 *hue=4.0+(QuantumScale*red-QuantumScale*green)/c;
977 *hue*=60.0/360.0;
978 *saturation=c*PerceptibleReciprocal(max);
979}
980
981static inline void ConvertRGBToHWB(const double red,const double green,
982 const double blue,double *hue,double *whiteness,double *blackness)
983{
984 double
985 f,
986 p,
987 v,
988 w;
989
990 /*
991 Convert RGB to HWB colorspace.
992 */
993 assert(hue != (double *) NULL);
994 assert(whiteness != (double *) NULL);
995 assert(blackness != (double *) NULL);
996 w=MagickMin(red,MagickMin(green,blue));
997 v=MagickMax(red,MagickMax(green,blue));
998 *blackness=1.0-QuantumScale*v;
999 *whiteness=QuantumScale*w;
1000 if (fabs(v-w) < MagickEpsilon)
1001 {
1002 *hue=(-1.0);
1003 return;
1004 }
1005 f=(fabs(red-w) < MagickEpsilon) ? green-blue :
1006 ((fabs(green-w) < MagickEpsilon) ? blue-red : red-green);
1007 p=(fabs(red-w) < MagickEpsilon) ? 3.0 :
1008 ((fabs(green-w) < MagickEpsilon) ? 5.0 : 1.0);
1009 *hue=(p-f/(v-1.0*w))/6.0;
1010}
1011
1012static inline void ConvertXYZToLab(const double X,const double Y,const double Z,
1013 const IlluminantType illuminant,double *L,double *a,double *b)
1014{
1015 double
1016 x,
1017 y,
1018 z;
1019
1020 if ((X/illuminant_tristimulus[illuminant].x) > CIEEpsilon)
1021 x=pow(X/illuminant_tristimulus[illuminant].x,1.0/3.0);
1022 else
1023 x=(CIEK*X/illuminant_tristimulus[illuminant].x+16.0)/116.0;
1024 if ((Y/illuminant_tristimulus[illuminant].y) > CIEEpsilon)
1025 y=pow(Y/illuminant_tristimulus[illuminant].y,1.0/3.0);
1026 else
1027 y=(CIEK*Y/illuminant_tristimulus[illuminant].y+16.0)/116.0;
1028 if ((Z/illuminant_tristimulus[illuminant].z) > CIEEpsilon)
1029 z=pow(Z/illuminant_tristimulus[illuminant].z,1.0/3.0);
1030 else
1031 z=(CIEK*Z/illuminant_tristimulus[illuminant].z+16.0)/116.0;
1032 *L=((116.0*y)-16.0)/100.0;
1033 *a=(500.0*(x-y))/255.0+0.5;
1034 *b=(200.0*(y-z))/255.0+0.5;
1035}
1036
1037static inline void ConvertRGBToLab(const double red,const double green,
1038 const double blue,const IlluminantType illuminant,double *L,double *a,
1039 double *b)
1040{
1041 double
1042 X,
1043 Y,
1044 Z;
1045
1046 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1047 ConvertXYZToLab(X,Y,Z,illuminant,L,a,b);
1048}
1049
1050static inline void ConvertXYZToLCHab(const double X,const double Y,
1051 const double Z,const IlluminantType illuminant,double *luma,double *chroma,
1052 double *hue)
1053{
1054 double
1055 a,
1056 b;
1057
1058 ConvertXYZToLab(X,Y,Z,illuminant,luma,&a,&b);
1059 *chroma=hypot(a-0.5,b-0.5)/1.0+0.5;
1060 *hue=180.0*atan2(b-0.5,a-0.5)/MagickPI/360.0;
1061 if (*hue < 0.0)
1062 *hue+=1.0;
1063}
1064
1065static inline void ConvertRGBToLCHab(const double red,const double green,
1066 const double blue,const IlluminantType illuminant,double *luma,double *chroma,
1067 double *hue)
1068{
1069 double
1070 X,
1071 Y,
1072 Z;
1073
1074 /*
1075 Convert RGB to LCHab colorspace.
1076 */
1077 assert(luma != (double *) NULL);
1078 assert(chroma != (double *) NULL);
1079 assert(hue != (double *) NULL);
1080 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1081 ConvertXYZToLCHab(X,Y,Z,illuminant,luma,chroma,hue);
1082}
1083
1084static inline void ConvertXYZToLuv(const double X,const double Y,const double Z,
1085 const IlluminantType illuminant,double *L,double *u,double *v)
1086{
1087 double
1088 alpha;
1089
1090 if ((Y/illuminant_tristimulus[illuminant].y) > CIEEpsilon)
1091 *L=(double) (116.0*pow(Y/illuminant_tristimulus[illuminant].y,
1092 1.0/3.0)-16.0);
1093 else
1094 *L=CIEK*(Y/illuminant_tristimulus[illuminant].y);
1095 alpha=PerceptibleReciprocal(X+15.0*Y+3.0*Z);
1096 *u=13.0*(*L)*((4.0*alpha*X)-(4.0*illuminant_tristimulus[illuminant].x/
1097 (illuminant_tristimulus[illuminant].x+15.0*
1098 illuminant_tristimulus[illuminant].y+3.0*
1099 illuminant_tristimulus[illuminant].z)));
1100 *v=13.0*(*L)*((9.0*alpha*Y)-(9.0*illuminant_tristimulus[illuminant].y/
1101 (illuminant_tristimulus[illuminant].x+15.0*
1102 illuminant_tristimulus[illuminant].y+3.0*
1103 illuminant_tristimulus[illuminant].z)));
1104 *L/=100.0;
1105 *u=(*u+134.0)/354.0;
1106 *v=(*v+140.0)/262.0;
1107}
1108
1109static inline void ConvertXYZToLCHuv(const double X,const double Y,
1110 const double Z,const IlluminantType illuminant,double *luma,double *chroma,
1111 double *hue)
1112{
1113 double
1114 u,
1115 v;
1116
1117 ConvertXYZToLuv(X,Y,Z,illuminant,luma,&u,&v);
1118 *chroma=hypot(354.0*u-134.0,262.0*v-140.0)/255.0+0.5;
1119 *hue=180.0*atan2(262.0*v-140.0,354.0*u-134.0)/MagickPI/360.0;
1120 if (*hue < 0.0)
1121 *hue+=1.0;
1122}
1123
1124static inline void ConvertRGBToLCHuv(const double red,const double green,
1125 const double blue,const IlluminantType illuminant,double *luma,double *chroma,
1126 double *hue)
1127{
1128 double
1129 X,
1130 Y,
1131 Z;
1132
1133 /*
1134 Convert RGB to LCHuv colorspace.
1135 */
1136 assert(luma != (double *) NULL);
1137 assert(chroma != (double *) NULL);
1138 assert(hue != (double *) NULL);
1139 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1140 ConvertXYZToLCHuv(X,Y,Z,illuminant,luma,chroma,hue);
1141}
1142
1143static inline void ConvertXYZToProPhoto(const double X,const double Y,
1144 const double Z,double *red,double *green,double *blue)
1145{
1146 double
1147 b,
1148 g,
1149 r;
1150
1151 r=1.3457989731028281*X-0.25558010007997534*Y-0.05110628506753401*Z;
1152 g=(-0.5446224939028347)*X+1.50823274131327810*Y+0.02053603239147973*Z;
1153 b=0.0000000000000000*X+0.0000000000000000*Y+1.21196754563894540*Z;
1154 *red=QuantumScale*EncodePixelGamma((double) QuantumRange*r);
1155 *green=QuantumScale*EncodePixelGamma((double) QuantumRange*g);
1156 *blue=QuantumScale*EncodePixelGamma((double) QuantumRange*b);
1157}
1158
1159static inline void ConvertRGBToProPhoto(const double red,const double green,
1160 const double blue,double *r,double *g,double *b)
1161{
1162 double
1163 X,
1164 Y,
1165 Z;
1166
1167 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1168 ConvertXYZToProPhoto(X,Y,Z,r,g,b);
1169}
1170
1171static inline void ConvertXYZToLMS(const double x,const double y,
1172 const double z,double *L,double *M,double *S)
1173{
1174 *L=0.7328*x+0.4296*y-0.1624*z;
1175 *M=(-0.7036*x+1.6975*y+0.0061*z);
1176 *S=0.0030*x+0.0136*y+0.9834*z;
1177}
1178
1179static inline void ConvertRGBToLMS(const double red,const double green,
1180 const double blue,double *L,double *M,double *S)
1181{
1182 double
1183 X,
1184 Y,
1185 Z;
1186
1187 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1188 ConvertXYZToLMS(X,Y,Z,L,M,S);
1189}
1190
1191static inline void ConvertRGBToLuv(const double red,const double green,
1192 const double blue,const IlluminantType illuminant,double *L,double *u,
1193 double *v)
1194{
1195 double
1196 X,
1197 Y,
1198 Z;
1199
1200 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1201 ConvertXYZToLuv(X,Y,Z,illuminant,L,u,v);
1202}
1203
1204static inline void ConvertRGBToxyY(const double red,const double green,
1205 const double blue,double *low_x,double *low_y,double *cap_Y)
1206{
1207 double
1208 gamma,
1209 X,
1210 Y,
1211 Z;
1212
1213 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1214 gamma=PerceptibleReciprocal(X+Y+Z);
1215 *low_x=gamma*X;
1216 *low_y=gamma*Y;
1217 *cap_Y=Y;
1218}
1219
1220static inline void ConvertXYZToJzazbz(const double X,const double Y,
1221 const double Z,const double white_luminance,double *Jz,double *az,double *bz)
1222{
1223#define Jzazbz_b 1.15 /* https://observablehq.com/@jrus/jzazbz */
1224#define Jzazbz_g 0.66
1225#define Jzazbz_c1 (3424.0/4096.0)
1226#define Jzazbz_c2 (2413.0/128.0)
1227#define Jzazbz_c3 (2392.0/128.0)
1228#define Jzazbz_n (2610.0/16384.0)
1229#define Jzazbz_p (1.7*2523.0/32.0)
1230#define Jzazbz_d (-0.56)
1231#define Jzazbz_d0 (1.6295499532821566e-11)
1232
1233 double
1234 gamma,
1235 Iz,
1236 L,
1237 Lp,
1238 M,
1239 Mp,
1240 S,
1241 Sp,
1242 Xp,
1243 Yp,
1244 Zp;
1245
1246 Xp=(Jzazbz_b*X-(Jzazbz_b-1)*Z);
1247 Yp=(Jzazbz_g*Y-(Jzazbz_g-1)*X);
1248 Zp=Z;
1249 L=0.41478972*Xp+0.579999*Yp+0.0146480*Zp;
1250 M=(-0.2015100)*Xp+1.120649*Yp+0.0531008*Zp;
1251 S=(-0.0166008)*Xp+0.264800*Yp+0.6684799*Zp;
1252 gamma=pow(L*PerceptibleReciprocal(white_luminance),Jzazbz_n);
1253 Lp=pow((Jzazbz_c1+Jzazbz_c2*gamma)/(1.0+Jzazbz_c3*gamma),Jzazbz_p);
1254 gamma=pow(M*PerceptibleReciprocal(white_luminance),Jzazbz_n);
1255 Mp=pow((Jzazbz_c1+Jzazbz_c2*gamma)/(1.0+Jzazbz_c3*gamma),Jzazbz_p);
1256 gamma=pow(S*PerceptibleReciprocal(white_luminance),Jzazbz_n);
1257 Sp=pow((Jzazbz_c1+Jzazbz_c2*gamma)/(1.0+Jzazbz_c3*gamma),Jzazbz_p);
1258 Iz=0.5*Lp+0.5*Mp;
1259 *az=3.52400*Lp-4.066708*Mp+0.542708*Sp+0.5;
1260 *bz=0.199076*Lp+1.096799*Mp-1.295875*Sp+0.5;
1261 *Jz=((Jzazbz_d+1.0)*Iz)/(Jzazbz_d*Iz+1.0)-Jzazbz_d0;
1262}
1263
1264static inline void ConvertJzazbzToXYZ(const double Jz,const double az,
1265 const double bz,const double white_luminance,double *X,double *Y,double *Z)
1266{
1267 double
1268 azz,
1269 bzz,
1270 gamma,
1271 Iz,
1272 L,
1273 Lp,
1274 M,
1275 Mp,
1276 S,
1277 Sp,
1278 Xp,
1279 Yp,
1280 Zp;
1281
1282 gamma=Jz+Jzazbz_d0;
1283 Iz=gamma/(Jzazbz_d-Jzazbz_d*gamma+1.0);
1284 azz=az-0.5;
1285 bzz=bz-0.5;
1286 Lp=Iz+0.138605043271539*azz+0.0580473161561189*bzz;
1287 Mp=Iz-0.138605043271539*azz-0.0580473161561189*bzz;
1288 Sp=Iz-0.0960192420263189*azz-0.811891896056039*bzz;
1289 gamma=pow(Lp,1.0/Jzazbz_p);
1290 L=white_luminance*pow((Jzazbz_c1-gamma)/(Jzazbz_c3*gamma-Jzazbz_c2),1.0/
1291 Jzazbz_n);
1292 gamma=pow(Mp,1.0/Jzazbz_p);
1293 M=white_luminance*pow((Jzazbz_c1-gamma)/(Jzazbz_c3*gamma-Jzazbz_c2),1.0/
1294 Jzazbz_n);
1295 gamma=pow(Sp,1.0/Jzazbz_p);
1296 S=white_luminance*pow((Jzazbz_c1-gamma)/(Jzazbz_c3*gamma-Jzazbz_c2),1.0/
1297 Jzazbz_n);
1298 Xp=1.92422643578761*L-1.00479231259537*M+0.037651404030618*S;
1299 Yp=0.350316762094999*L+0.726481193931655*M-0.065384422948085*S;
1300 Zp=(-0.0909828109828476)*L-0.312728290523074*M+1.52276656130526*S;
1301 *X=(Xp+(Jzazbz_b-1.0)*Zp)/Jzazbz_b;
1302 *Y=(Yp+(Jzazbz_g-1.0)**X)/Jzazbz_g;
1303 *Z=Zp;
1304}
1305
1306static inline void ConvertRGBToJzazbz(const double red,const double green,
1307 const double blue,const double white_luminance,double *Jz,double *az,
1308 double *bz)
1309{
1310 double
1311 X,
1312 Y,
1313 Z;
1314
1315 ConvertRGBToXYZ(red,blue,green,&X,&Y,&Z);
1316 ConvertXYZToJzazbz(X,Y,Z,white_luminance,Jz,az,bz);
1317}
1318
1319static inline void ConvertJzazbzToRGB(const double Jz,const double az,
1320 const double bz,const double white_luminance,double *red,double *green,
1321 double *blue)
1322{
1323 double
1324 X,
1325 Y,
1326 Z;
1327
1328 ConvertJzazbzToXYZ(Jz,az,bz,white_luminance,&X,&Y,&Z);
1329 ConvertXYZToRGB(X,Y,Z,red,blue,green);
1330}
1331
1332static inline void ConvertOklabToRGB(const double L,const double a,
1333 const double b,double *red,double *green,double *blue)
1334{
1335 double
1336 B,
1337 G,
1338 l,
1339 m,
1340 R,
1341 s;
1342
1343 l=L+0.3963377774*(a-0.5)+0.2158037573*(b-0.5);
1344 m=L-0.1055613458*(a-0.5)-0.0638541728*(b-0.5);
1345 s=L-0.0894841775*(a-0.5)-1.2914855480*(b-0.5);
1346 l*=l*l;
1347 m*=m*m;
1348 s*=s*s;
1349 R=4.0767416621*l-3.3077115913*m+0.2309699292*s;
1350 G=(-1.2684380046)*l+2.6097574011*m-0.3413193965*s;
1351 B=(-0.0041960863)*l-0.7034186147*m+1.7076147010*s;
1352 *red=EncodePixelGamma((double) QuantumRange*R);
1353 *green=EncodePixelGamma((double) QuantumRange*G);
1354 *blue=EncodePixelGamma((double) QuantumRange*B);
1355}
1356
1357static inline void ConvertRGBToOklab(const double red,const double green,
1358 const double blue,double *L,double *a,double *b)
1359{
1360 double
1361 B,
1362 G,
1363 l,
1364 m,
1365 R,
1366 s;
1367
1368 R=QuantumScale*DecodePixelGamma(red);
1369 G=QuantumScale*DecodePixelGamma(green);
1370 B=QuantumScale*DecodePixelGamma(blue);
1371 l=cbrt(0.4122214708*R+0.5363325363*G+0.0514459929*B);
1372 m=cbrt(0.2119034982*R+0.6806995451*G+0.1073969566*B);
1373 s=cbrt(0.0883024619*R+0.2817188376*G+0.6299787005*B);
1374 *L=0.2104542553*l+0.7936177850*m-0.0040720468*s;
1375 *a=1.9779984951*l-2.4285922050*m+0.4505937099*s+0.5;
1376 *b=0.0259040371*l+0.7827717662*m-0.8086757660*s+0.5;
1377}
1378
1379static inline void ConvertOklchToRGB(const double L,const double C,
1380 const double h,double *red,double *green,double *blue)
1381{
1382 double
1383 a,
1384 b;
1385
1386 a=C*cos(2.0*MagickPI*h);
1387 b=C*sin(2.0*MagickPI*h);
1388 ConvertOklabToRGB(L,a,b,red,green,blue);
1389}
1390
1391static inline void ConvertRGBToOklch(const double red,const double green,
1392 const double blue,double *L,double *C,double *h)
1393{
1394 double
1395 a,
1396 b;
1397
1398 ConvertRGBToOklab(red,green,blue,L,&a,&b);
1399 *C=sqrt(a*a+b*b);
1400 *h=0.5+0.5*atan2(-b,-a)/MagickPI;
1401}
1402
1403static inline void ConvertRGBToYDbDr(const double red,const double green,
1404 const double blue,double *Y,double *Db,double *Dr)
1405{
1406 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1407 *Db=QuantumScale*(-0.450*red-0.883*green+1.333*blue)+0.5;
1408 *Dr=QuantumScale*(-1.333*red+1.116*green+0.217*blue)+0.5;
1409}
1410
1411static inline void ConvertRGBToYIQ(const double red,const double green,
1412 const double blue,double *Y,double *I,double *Q)
1413{
1414 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1415 *I=QuantumScale*(0.595716*red-0.274453*green-0.321263*blue)+0.5;
1416 *Q=QuantumScale*(0.211456*red-0.522591*green+0.311135*blue)+0.5;
1417}
1418
1419static inline void ConvertRGBToYPbPr(const double red,const double green,
1420 const double blue,double *Y,double *Pb,double *Pr)
1421{
1422 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1423 *Pb=QuantumScale*((-0.1687367)*red-0.331264*green+0.5*blue)+0.5;
1424 *Pr=QuantumScale*(0.5*red-0.418688*green-0.081312*blue)+0.5;
1425}
1426
1427static inline void ConvertRGBToYCbCr(const double red,const double green,
1428 const double blue,double *Y,double *Cb,double *Cr)
1429{
1430 ConvertRGBToYPbPr(red,green,blue,Y,Cb,Cr);
1431}
1432
1433static inline void ConvertRGBToYUV(const double red,const double green,
1434 const double blue,double *Y,double *U,double *V)
1435{
1436 *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1437 *U=QuantumScale*((-0.147)*red-0.289*green+0.436*blue)+0.5;
1438 *V=QuantumScale*(0.615*red-0.515*green-0.100*blue)+0.5;
1439}
1440
1441static inline void ConvertRGBToCMYK(PixelInfo *pixel)
1442{
1443 MagickRealType
1444 black,
1445 blue,
1446 cyan,
1447 green,
1448 magenta,
1449 red,
1450 yellow;
1451
1452 if (pixel->colorspace != sRGBColorspace)
1453 {
1454 red=QuantumScale*pixel->red;
1455 green=QuantumScale*pixel->green;
1456 blue=QuantumScale*pixel->blue;
1457 }
1458 else
1459 {
1460 red=QuantumScale*DecodePixelGamma(pixel->red);
1461 green=QuantumScale*DecodePixelGamma(pixel->green);
1462 blue=QuantumScale*DecodePixelGamma(pixel->blue);
1463 }
1464 if ((fabs((double) red) < MagickEpsilon) &&
1465 (fabs((double) green) < MagickEpsilon) &&
1466 (fabs((double) blue) < MagickEpsilon))
1467 {
1468 pixel->black=(MagickRealType) QuantumRange;
1469 return;
1470 }
1471 cyan=(MagickRealType) (1.0-red);
1472 magenta=(MagickRealType) (1.0-green);
1473 yellow=(MagickRealType) (1.0-blue);
1474 black=cyan;
1475 if (magenta < black)
1476 black=magenta;
1477 if (yellow < black)
1478 black=yellow;
1479 cyan=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(cyan-black));
1480 magenta=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(magenta-black));
1481 yellow=(MagickRealType) (PerceptibleReciprocal(1.0-black)*(yellow-black));
1482 pixel->colorspace=CMYKColorspace;
1483 pixel->red=(MagickRealType) QuantumRange*cyan;
1484 pixel->green=(MagickRealType) QuantumRange*magenta;
1485 pixel->blue=(MagickRealType) QuantumRange*yellow;
1486 pixel->black=(MagickRealType) QuantumRange*black;
1487}
1488
1489static inline void ConvertYPbPrToRGB(const double Y,const double Pb,
1490 const double Pr,double *red,double *green,double *blue)
1491{
1492 *red=(double) QuantumRange*(0.99999999999914679361*Y-1.2188941887145875e-06*
1493 (Pb-0.5)+1.4019995886561440468*(Pr-0.5));
1494 *green=(double) QuantumRange*(0.99999975910502514331*Y-0.34413567816504303521*
1495 (Pb-0.5)-0.71413649331646789076*(Pr-0.5));
1496 *blue=(double) QuantumRange*(1.00000124040004623180*Y+1.77200006607230409200*
1497 (Pb-0.5)+2.1453384174593273e-06*(Pr-0.5));
1498}
1499
1500static inline void ConvertYCbCrToRGB(const double Y,const double Cb,
1501 const double Cr,double *red,double *green,double *blue)
1502{
1503 ConvertYPbPrToRGB(Y,Cb,Cr,red,green,blue);
1504}
1505
1506static inline void ConvertYDbDrToRGB(const double Y,const double Db,
1507 const double Dr,double *red,double *green,double *blue)
1508{
1509 *red=(double) QuantumRange*(Y+9.2303716147657e-05*(Db-0.5)-
1510 0.52591263066186533*(Dr-0.5));
1511 *green=(double) QuantumRange*(Y-0.12913289889050927*(Db-0.5)+
1512 0.26789932820759876*(Dr-0.5));
1513 *blue=(double) QuantumRange*(Y+0.66467905997895482*(Db-0.5)-
1514 7.9202543533108e-05*(Dr-0.5));
1515}
1516
1517static inline void ConvertYIQToRGB(const double Y,const double I,const double Q,
1518 double *red,double *green,double *blue)
1519{
1520 *red=(double) QuantumRange*(Y+0.9562957197589482261*(I-0.5)+
1521 0.6210244164652610754*(Q-0.5));
1522 *green=(double) QuantumRange*(Y-0.2721220993185104464*(I-0.5)-
1523 0.6473805968256950427*(Q-0.5));
1524 *blue=(double) QuantumRange*(Y-1.1069890167364901945*(I-0.5)+
1525 1.7046149983646481374*(Q-0.5));
1526}
1527
1528static inline void ConvertxyYToRGB(const double low_x,const double low_y,
1529 const double cap_Y,double *red,double *green,double *blue)
1530{
1531 double
1532 gamma,
1533 X,
1534 Y,
1535 Z;
1536
1537 gamma=PerceptibleReciprocal(low_y);
1538 X=gamma*cap_Y*low_x;
1539 Y=cap_Y;
1540 Z=gamma*cap_Y*(1.0-low_x-low_y);
1541 ConvertXYZToRGB(X,Y,Z,red,green,blue);
1542}
1543
1544static inline void ConvertYUVToRGB(const double Y,const double U,const double V,
1545 double *red,double *green,double *blue)
1546{
1547 *red=(double) QuantumRange*(Y-3.945707070708279e-05*(U-0.5)+
1548 1.1398279671717170825*(V-0.5));
1549 *green=(double) QuantumRange*(Y-0.3946101641414141437*(U-0.5)-
1550 0.5805003156565656797*(V-0.5));
1551 *blue=(double) QuantumRange*(Y+2.0319996843434342537*(U-0.5)-
1552 4.813762626262513e-04*(V-0.5));
1553}
1554
1555static inline MagickBooleanType IsCMYKColorspace(
1556 const ColorspaceType colorspace)
1557{
1558 if (colorspace == CMYKColorspace)
1559 return(MagickTrue);
1560 return(MagickFalse);
1561}
1562
1563static inline MagickBooleanType IsGrayColorspace(
1564 const ColorspaceType colorspace)
1565{
1566 if ((colorspace == LinearGRAYColorspace) || (colorspace == GRAYColorspace))
1567 return(MagickTrue);
1568 return(MagickFalse);
1569}
1570
1571static inline MagickBooleanType IsGrayImageType(const ImageType type)
1572{
1573 if ((type == GrayscaleType) || (type == GrayscaleAlphaType) ||
1574 (type == BilevelType))
1575 return(MagickTrue);
1576 return(MagickFalse);
1577}
1578
1579static inline MagickBooleanType IsHueCompatibleColorspace(
1580 const ColorspaceType colorspace)
1581{
1582 if ((colorspace == HCLColorspace) || (colorspace == HCLpColorspace) ||
1583 (colorspace == HSBColorspace) || (colorspace == HSIColorspace) ||
1584 (colorspace == HSLColorspace) || (colorspace == HSVColorspace))
1585 return(MagickTrue);
1586 return(MagickFalse);
1587}
1588
1589static inline MagickBooleanType IsLabCompatibleColorspace(
1590 const ColorspaceType colorspace)
1591{
1592 if ((colorspace == LabColorspace) || (colorspace == LCHColorspace) ||
1593 (colorspace == LCHabColorspace) || (colorspace == LCHuvColorspace) ||
1594 (colorspace == OklabColorspace) || (colorspace == OklchColorspace))
1595 return(MagickTrue);
1596 return(MagickFalse);
1597}
1598
1599static inline MagickBooleanType IsRGBColorspace(const ColorspaceType colorspace)
1600{
1601 if ((colorspace == RGBColorspace) || (colorspace == scRGBColorspace) ||
1602 (colorspace == LinearGRAYColorspace))
1603 return(MagickTrue);
1604 return(MagickFalse);
1605}
1606
1607static inline MagickBooleanType IssRGBColorspace(
1608 const ColorspaceType colorspace)
1609{
1610 if ((colorspace == sRGBColorspace) || (colorspace == TransparentColorspace))
1611 return(MagickTrue);
1612 return(MagickFalse);
1613}
1614
1615static inline MagickBooleanType IssRGBCompatibleColorspace(
1616 const ColorspaceType colorspace)
1617{
1618 if ((colorspace == sRGBColorspace) || (colorspace == RGBColorspace) ||
1619 (colorspace == Adobe98Colorspace) || (colorspace == ProPhotoColorspace) ||
1620 (colorspace == DisplayP3Colorspace) || (colorspace == scRGBColorspace) ||
1621 (colorspace == TransparentColorspace) || (colorspace == GRAYColorspace) ||
1622 (colorspace == LinearGRAYColorspace))
1623 return(MagickTrue);
1624 return(MagickFalse);
1625}
1626
1627static inline MagickBooleanType IsYCbCrCompatibleColorspace(
1628 const ColorspaceType colorspace)
1629{
1630 if ((colorspace == YCbCrColorspace) ||
1631 (colorspace == Rec709YCbCrColorspace) ||
1632 (colorspace == Rec601YCbCrColorspace))
1633 return(MagickTrue);
1634 return(MagickFalse);
1635}
1636
1637extern MagickPrivate void
1638 ConvertGenericToRGB(const ColorspaceType,const double,const double,
1639 const double,const double,const IlluminantType,double *,double *,double *),
1640 ConvertRGBToGeneric(const ColorspaceType,const double,const double,
1641 const double,const double,const IlluminantType,double *,double *,double *);
1642
1643#if defined(__cplusplus) || defined(c_plusplus)
1644}
1645#endif
1646
1647#endif