MagickCore  7.1.2-29
Convert, Edit, Or Compose Bitmap Images
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/license/
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 
32 static 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)
49 extern "C" {
50 #endif
51 
52 
53 static 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 
72 static 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 
96 static 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 
108 static 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 
119 static 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 
131 static 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 
141 static 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 
149 static 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 
214 static 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 
292 static 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 
368 static 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 
414 static 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 
483 static 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=CastDoubleToSsizeT(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 
531 static 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 
559 static 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 
572 static 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 
580 static 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 
600 static 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=MagickSafeReciprocal((((52.0*L*MagickSafeReciprocal(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*MagickSafeReciprocal(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*MagickSafeReciprocal(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 
627 static 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 
635 static 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 
655 static 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 
663 static 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 
675 static 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 
694 static 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 
706 static 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 
719 static 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 
739 static 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 
751 static 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 
759 static 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 
781 static 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 
793 static 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 
801 static 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 
834 static 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 
867 static 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 
909 static 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 
938 static 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 
954 static 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 
966 static 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 
982 static 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 
994 static 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*MagickSafeReciprocal(max);
1033 }
1034 
1035 static 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 
1066 static 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 
1091 static 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 
1104 static 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 
1119 static 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 
1138 static 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=MagickSafeReciprocal(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 
1163 static 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 
1178 static 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 
1197 static 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 
1213 static 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 
1225 static 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 
1233 static 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 
1245 static 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 
1258 static 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=MagickSafeReciprocal(X+Y+Z);
1269  *low_x=gamma*X;
1270  *low_y=gamma*Y;
1271  *cap_Y=Y;
1272 }
1273 
1274 static inline void ConvertXYZToJzazbz(const double X,const double Y,
1275  const double Z,const double white_luminance,double *Jz,double *az,double *bz)
1276 {
1277 #define Jzazbz_b (1.15) /* https://observablehq.com/@jrus/jzazbz */
1278 #define Jzazbz_g (0.66) /* refactored by N. Robidoux */
1279 #define Jzazbz_c1 (3424.0/4096.0)
1280 #define Jzazbz_c2 (2413.0/128.0)
1281 #define Jzazbz_c3 (2392.0/128.0)
1282 #define Jzazbz_n (2610.0/16384.0)
1283 #define Jzazbz_p (1.7*2523.0/32.0)
1284 #define Jzazbz_d (-0.56)
1285 #define Jzazbz_d0 (1.6295499532821566e-11)
1286 #define Jzazbz_LX (0.41478972)
1287 #define Jzazbz_LY (0.579999)
1288 #define Jzazbz_LZ (0.0146480)
1289 #define Jzazbz_MX (-0.2015100)
1290 #define Jzazbz_MY (1.120649)
1291 #define Jzazbz_MZ (0.0531008)
1292 #define Jzazbz_SX (-0.0166008)
1293 #define Jzazbz_SY (0.264800)
1294 #define Jzazbz_SZ (0.6684799)
1295 #define Jzazbz_aL (3.52400)
1296 #define Jzazbz_aM (-4.066708)
1297 #define Jzazbz_aS (0.542708)
1298 #define Jzazbz_bL (0.199076)
1299 #define Jzazbz_bM (1.096799)
1300 #define Jzazbz_bS (-1.295875)
1301 
1302  double
1303  a,
1304  b,
1305  dL,
1306  dM,
1307  dS,
1308  gL,
1309  gM,
1310  gS,
1311  nL,
1312  nM,
1313  nS,
1314  Iz,
1315  J,
1316  JdI,
1317  L,
1318  Lp,
1319  M,
1320  Mp,
1321  S,
1322  Sp,
1323  WLr,
1324  Xp,
1325  Yp;
1326 
1327  WLr=MagickSafeReciprocal(white_luminance);
1328  Xp=Z+Jzazbz_b*(X-Z); /* If X and Z are vectorized, better done */
1329  Yp=X+Jzazbz_g*(Y-X); /* as Xp=Jzazbz_b*X+(1.0-Jzazbz_b)*Z; etc. */
1330  L=Jzazbz_LZ*Z;
1331  M=Jzazbz_MZ*Z;
1332  S=Jzazbz_SZ*Z;
1333  L+=Jzazbz_LX*Xp;
1334  M+=Jzazbz_MX*Xp;
1335  S+=Jzazbz_SX*Xp;
1336  L+=Jzazbz_LY*Yp;
1337  M+=Jzazbz_MY*Yp;
1338  S+=Jzazbz_SY*Yp;
1339  gL=pow(L*WLr,Jzazbz_n);
1340  gM=pow(M*WLr,Jzazbz_n);
1341  gS=pow(S*WLr,Jzazbz_n);
1342  nL=Jzazbz_c1+Jzazbz_c2*gL;
1343  nM=Jzazbz_c1+Jzazbz_c2*gM;
1344  nS=Jzazbz_c1+Jzazbz_c2*gS;
1345  dL=1.0+Jzazbz_c3*gL;
1346  dM=1.0+Jzazbz_c3*gM;
1347  dS=1.0+Jzazbz_c3*gS;
1348  Lp=pow(nL/dL,Jzazbz_p);
1349  Mp=pow(nM/dM,Jzazbz_p);
1350  Sp=pow(nS/dS,Jzazbz_p);
1351  Iz=(Lp+Mp)*0.5;
1352  JdI=Jzazbz_d*Iz;
1353  J=(JdI+Iz)/(JdI+1.0)-Jzazbz_d0;
1354  a=0.5+Jzazbz_aL*Lp;
1355  b=0.5+Jzazbz_bL*Lp;
1356  a+=Jzazbz_aM*Mp;
1357  b+=Jzazbz_bM*Mp;
1358  a+=Jzazbz_aS*Sp;
1359  b+=Jzazbz_bS*Sp;
1360  *Jz=IsNaN(J) != 0 ? 0.0 : J;
1361  *az=IsNaN(a) != 0 ? 0.5 : a;
1362  *bz=IsNaN(b) != 0 ? 0.5 : b;
1363 }
1364 
1365 static inline void ConvertRGBToJzazbz(const double red,const double green,
1366  const double blue,const double white_luminance,double *Jz,double *az,
1367  double *bz)
1368 {
1369  double
1370  X,
1371  Y,
1372  Z;
1373 
1374  ConvertRGBToXYZ(red,blue,green,&X,&Y,&Z);
1375  ConvertXYZToJzazbz(X,Y,Z,white_luminance,Jz,az,bz);
1376 }
1377 
1378 static inline void ConvertJzazbzToXYZ(const double Jz,const double az,
1379  const double bz,const double white_luminance,double *X,double *Y,double *Z)
1380 {
1381 #define Jzazbz_Ca (0.138605043271539)
1382 #define Jzazbz_Cb (0.0580473161561189)
1383 #define Jzazbz_Sa (-0.0960192420263189)
1384 #define Jzazbz_Sb (-0.811891896056039)
1385 #define Jzazbz_XL (1.92422643578761)
1386 #define Jzazbz_XM (-1.00479231259537)
1387 #define Jzazbz_XS (0.037651404030618)
1388 #define Jzazbz_YL (0.350316762094999)
1389 #define Jzazbz_YM (0.726481193931655)
1390 #define Jzazbz_YS (-0.065384422948085)
1391 #define Jzazbz_ZL (-0.0909828109828476)
1392 #define Jzazbz_ZM (-0.312728290523074)
1393 #define Jzazbz_ZS (1.52276656130526)
1394 #define mJzazbz_c3 (-2392.0/128.0)
1395 
1396  double
1397  azz,
1398  bzz,
1399  C,
1400  dL,
1401  dM,
1402  dS,
1403  g,
1404  gL,
1405  gM,
1406  gS,
1407  Jnr,
1408  Jpr,
1409  L,
1410  Lp,
1411  M,
1412  Mp,
1413  S,
1414  Sp,
1415  nL,
1416  nM,
1417  nS,
1418  Xp,
1419  Zp,
1420  Yp;
1421 
1422  g=Jz+Jzazbz_d0;
1423  azz=az-0.5;
1424  bzz=bz-0.5;
1425  C=Jzazbz_Ca*azz+Jzazbz_Cb*bzz;
1426  Sp=g/(1.0+Jzazbz_d*(1.0-g));
1427  Lp=Sp+C;
1428  Mp=Sp-C;
1429  Sp+=Jzazbz_Sa*azz;
1430  Sp+=Jzazbz_Sb*bzz;
1431  Jpr=1.0/Jzazbz_p;
1432  gL=pow(Lp,Jpr);
1433  gM=pow(Mp,Jpr);
1434  gS=pow(Sp,Jpr);
1435  Jnr=1.0/Jzazbz_n;
1436  nL=gL-Jzazbz_c1;
1437  nM=gM-Jzazbz_c1;
1438  nS=gS-Jzazbz_c1;
1439  dL=Jzazbz_c2+mJzazbz_c3*gL;
1440  dM=Jzazbz_c2+mJzazbz_c3*gM;
1441  dS=Jzazbz_c2+mJzazbz_c3*gS;
1442  L=pow(nL/dL,Jnr);
1443  M=pow(nM/dM,Jnr);
1444  S=pow(nS/dS,Jnr);
1445  L*=white_luminance;
1446  M*=white_luminance;
1447  S*=white_luminance;
1448  Zp=Jzazbz_ZL*L;
1449  Xp=Jzazbz_XL*L;
1450  Yp=Jzazbz_YL*L;
1451  Zp+=Jzazbz_ZM*M;
1452  Xp+=Jzazbz_XM*M;
1453  Yp+=Jzazbz_YM*M;
1454  Zp+=Jzazbz_ZS*S;
1455  Xp+=Jzazbz_XS*S;
1456  Yp+=Jzazbz_YS*S;
1457  Zp=IsNaN(Zp) != 0 ? 0.0 : Zp;
1458  Xp=Zp+(Xp-Zp)/Jzazbz_b; /* If Xp and Zp are vectorized, better done */
1459  Xp=IsNaN(Xp) != 0 ? 0.0 : Xp; /* as rJzazbz_b = 1.0/Jzazbz_b; */
1460  Yp=Xp+(Yp-Xp)/Jzazbz_g; /* Xp=rJzazbz_b*Xp+(1.0-rJzazbz_b)*Zp; etc. */
1461  Yp=IsNaN(Yp) != 0 ? 0.0 : Yp;
1462  *Z=Zp;
1463  *X=Xp;
1464  *Y=Yp;
1465 }
1466 
1467 static inline void ConvertJzazbzToRGB(const double Jz,const double az,
1468  const double bz,const double white_luminance,double *red,double *green,
1469  double *blue)
1470 {
1471  double
1472  X,
1473  Y,
1474  Z;
1475 
1476  ConvertJzazbzToXYZ(Jz,az,bz,white_luminance,&X,&Y,&Z);
1477  ConvertXYZToRGB(X,Y,Z,red,blue,green);
1478 }
1479 
1480 static inline void ConvertOklabToRGB(const double L,const double a,
1481  const double b,double *red,double *green,double *blue)
1482 {
1483 #define Oklab_la (0.3963377774)
1484 #define Oklab_lb (0.2158037573)
1485 #define Oklab_ma (-0.1055613458)
1486 #define Oklab_mb (-0.0638541728)
1487 #define Oklab_sa (-0.0894841775)
1488 #define Oklab_sb (-1.2914855480)
1489 #define Oklab_Rl (4.0767416621)
1490 #define Oklab_Rm (-3.3077115913)
1491 #define Oklab_Rs (0.2309699292)
1492 #define Oklab_Gl (-1.2684380046)
1493 #define Oklab_Gm (2.6097574011)
1494 #define Oklab_Gs (-0.3413193965)
1495 #define Oklab_Bl (-0.0041960863)
1496 #define Oklab_Bm (-0.7034186147)
1497 #define Oklab_Bs (1.7076147010)
1498 
1499  double
1500  aa,
1501  B,
1502  bb,
1503  Bl,
1504  Bm,
1505  Bs,
1506  G,
1507  Gl,
1508  Gm,
1509  Gs,
1510  l,
1511  m,
1512  R,
1513  Rl,
1514  Rm,
1515  Rs,
1516  s;
1517 
1518  aa=a-0.5;
1519  bb=b-0.5;
1520  l=L+Oklab_la*aa+Oklab_lb*bb;
1521  m=L+Oklab_ma*aa+Oklab_mb*bb;
1522  s=L+Oklab_sa*aa+Oklab_sb*bb;
1523  l*=l*l;
1524  m*=m*m;
1525  s*=s*s;
1526  Rl=Oklab_Rl*(double) QuantumRange; /* constant folding */
1527  Rm=Oklab_Rm*(double) QuantumRange;
1528  Rs=Oklab_Rs*(double) QuantumRange;
1529  Gl=Oklab_Gl*(double) QuantumRange;
1530  Gm=Oklab_Gm*(double) QuantumRange;
1531  Gs=Oklab_Gs*(double) QuantumRange;
1532  Bl=Oklab_Bl*(double) QuantumRange;
1533  Bm=Oklab_Bm*(double) QuantumRange;
1534  Bs=Oklab_Bs*(double) QuantumRange;
1535  R=Rl*l+Rm*m+Rs*s;
1536  G=Gl*l+Gm*m+Gs*s;
1537  B=Bl*l+Bm*m+Bs*s;
1538  *red=EncodePixelGamma(R);
1539  *green=EncodePixelGamma(G);
1540  *blue=EncodePixelGamma(B);
1541 }
1542 
1543 static inline void ConvertOklchToRGB(const double L,const double C,
1544  const double H,double *red,double *green,double *blue)
1545 {
1546  /* OKLCH → OKLab */
1547  const double a = C*cos(H);
1548  const double b_ = C*sin(H);
1549 
1550  /* OKLab → nonlinear LMS */
1551  const double l_ = L+Oklab_la*a+Oklab_lb*b_;
1552  const double m_ = L+Oklab_ma*a+Oklab_mb*b_;
1553  const double s_ = L+Oklab_sa*a+Oklab_sb*b_;
1554 
1555  /* Undo nonlinear transform */
1556  const double l = l_*l_*l_;
1557  const double m = m_*m_*m_;
1558  const double s = s_*s_*s_;
1559 
1560  /* LMS → linear RGB */
1561  *red=EncodePixelGamma((double) QuantumRange*(Oklab_Rl*l+Oklab_Rm*m+
1562  Oklab_Rs*s));
1563  *green=EncodePixelGamma((double) QuantumRange*(Oklab_Gl*l+Oklab_Gm*m+
1564  Oklab_Gs*s));
1565  *blue=EncodePixelGamma((double) QuantumRange*(Oklab_Bl*l+Oklab_Bm*m+
1566  Oklab_Bs*s));
1567 }
1568 
1569 static inline void ConvertRGBToOklab(const double red,const double green,
1570  const double blue,double *L,double *a,double *b)
1571 {
1572 #define Oklab_lR (0.4122214708)
1573 #define Oklab_lG (0.5363325363)
1574 #define Oklab_lB (0.0514459929)
1575 #define Oklab_mR (0.2119034982)
1576 #define Oklab_mG (0.6806995451)
1577 #define Oklab_mB (0.1073969566)
1578 #define Oklab_sR (0.0883024619)
1579 #define Oklab_sG (0.2817188376)
1580 #define Oklab_sB (0.6299787005)
1581 #define Oklab_Ll (0.2104542553)
1582 #define Oklab_Lm (0.7936177850)
1583 #define Oklab_Ls (-0.0040720468)
1584 #define Oklab_al (1.9779984951)
1585 #define Oklab_am (-2.4285922050)
1586 #define Oklab_as (0.4505937099)
1587 #define Oklab_bl (0.0259040371)
1588 #define Oklab_bm (0.7827717662)
1589 #define Oklab_bs (-0.8086757660)
1590 
1591  double
1592  B,
1593  G,
1594  l,
1595  lR,
1596  lG,
1597  lB,
1598  m,
1599  mR,
1600  mG,
1601  mB,
1602  R,
1603  s,
1604  sR,
1605  sG,
1606  sB;
1607 
1608  R=DecodePixelGamma(red);
1609  G=DecodePixelGamma(green);
1610  B=DecodePixelGamma(blue);
1611  lR=Oklab_lR*QuantumScale; /* constant folding */
1612  lG=Oklab_lG*QuantumScale;
1613  lB=Oklab_lB*QuantumScale;
1614  mR=Oklab_mR*QuantumScale;
1615  mG=Oklab_mG*QuantumScale;
1616  mB=Oklab_mB*QuantumScale;
1617  sR=Oklab_sR*QuantumScale;
1618  sG=Oklab_sG*QuantumScale;
1619  sB=Oklab_sB*QuantumScale;
1620  l=lR*R+lG*G+lB*B;
1621  m=mR*R+mG*G+mB*B;
1622  s=sR*R+sG*G+sB*B;
1623  l=cbrt(l);
1624  m=cbrt(m);
1625  s=cbrt(s);
1626  *L=Oklab_Ll*l+Oklab_Lm*m+Oklab_Ls*s;
1627  *a=Oklab_al*l+Oklab_am*m+Oklab_as*s+0.5;
1628  *b=Oklab_bl*l+Oklab_bm*m+Oklab_bs*s+0.5;
1629 }
1630 
1631 static inline void ConvertRGBToOklch(const double red,const double green,
1632  const double blue,double *L,double *C,double *H)
1633 {
1634  /* Linear RGB → LMS */
1635  const double l = Oklab_lR*QuantumScale*DecodePixelGamma(red)+
1636  Oklab_lG*QuantumScale*DecodePixelGamma(green)+
1637  Oklab_lB*QuantumScale*DecodePixelGamma(blue);
1638  const double m = Oklab_mR*QuantumScale*DecodePixelGamma(red)+
1639  Oklab_mG*QuantumScale*DecodePixelGamma(green)+
1640  Oklab_mB*QuantumScale*DecodePixelGamma(blue);
1641  const double s = Oklab_sR*QuantumScale*DecodePixelGamma(red)+
1642  Oklab_sG*QuantumScale*DecodePixelGamma(green)+
1643  Oklab_sB*QuantumScale*DecodePixelGamma(blue);
1644 
1645  /* Nonlinear transform */
1646  const double l_ = cbrt(l);
1647  const double m_ = cbrt(m);
1648  const double s_ = cbrt(s);
1649 
1650  /* LMS → OKLab */
1651  const double L_ok = Oklab_Ll*l_+Oklab_Lm*m_+Oklab_Ls*s_;
1652  const double a_ok = Oklab_al*l_+Oklab_am*m_+Oklab_as*s_;
1653  const double b_ok = Oklab_bl*l_+Oklab_bm*m_+Oklab_bs*s_;
1654 
1655  /* OKLab → OKLCH */
1656  const double C_ok = sqrt(a_ok*a_ok+b_ok*b_ok);
1657  double H_ok = atan2(b_ok,a_ok); /* radians */
1658 
1659  if (H_ok < 0.0)
1660  H_ok+=2.0*MagickPI;
1661  *L=L_ok;
1662  *C=C_ok;
1663  *H=H_ok;
1664 }
1665 
1666 static inline void ConvertRGBToYDbDr(const double red,const double green,
1667  const double blue,double *Y,double *Db,double *Dr)
1668 {
1669  *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1670  *Db=QuantumScale*(-0.450*red-0.883*green+1.333*blue)+0.5;
1671  *Dr=QuantumScale*(-1.333*red+1.116*green+0.217*blue)+0.5;
1672 }
1673 
1674 static inline void ConvertRGBToYIQ(const double red,const double green,
1675  const double blue,double *Y,double *I,double *Q)
1676 {
1677  *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1678  *I=QuantumScale*(0.595716*red-0.274453*green-0.321263*blue)+0.5;
1679  *Q=QuantumScale*(0.211456*red-0.522591*green+0.311135*blue)+0.5;
1680 }
1681 
1682 static inline void ConvertRGBToYPbPr(const double red,const double green,
1683  const double blue,double *Y,double *Pb,double *Pr)
1684 {
1685  *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1686  *Pb=QuantumScale*((-0.1687367)*red-0.331264*green+0.5*blue)+0.5;
1687  *Pr=QuantumScale*(0.5*red-0.418688*green-0.081312*blue)+0.5;
1688 }
1689 
1690 static inline void ConvertRGBToYCbCr(const double red,const double green,
1691  const double blue,double *Y,double *Cb,double *Cr)
1692 {
1693  ConvertRGBToYPbPr(red,green,blue,Y,Cb,Cr);
1694 }
1695 
1696 static inline void ConvertRGBToYUV(const double red,const double green,
1697  const double blue,double *Y,double *U,double *V)
1698 {
1699  *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
1700  *U=QuantumScale*((-0.147)*red-0.289*green+0.436*blue)+0.5;
1701  *V=QuantumScale*(0.615*red-0.515*green-0.100*blue)+0.5;
1702 }
1703 
1704 static inline void ConvertRGBToCMYK(PixelInfo *pixel)
1705 {
1706  MagickRealType
1707  black,
1708  blue,
1709  cyan,
1710  green,
1711  magenta,
1712  red,
1713  yellow;
1714 
1715  if (pixel->colorspace != sRGBColorspace)
1716  {
1717  red=QuantumScale*pixel->red;
1718  green=QuantumScale*pixel->green;
1719  blue=QuantumScale*pixel->blue;
1720  }
1721  else
1722  {
1723  red=QuantumScale*DecodePixelGamma(pixel->red);
1724  green=QuantumScale*DecodePixelGamma(pixel->green);
1725  blue=QuantumScale*DecodePixelGamma(pixel->blue);
1726  }
1727  if ((fabs((double) red) < MagickEpsilon) &&
1728  (fabs((double) green) < MagickEpsilon) &&
1729  (fabs((double) blue) < MagickEpsilon))
1730  {
1731  pixel->black=(MagickRealType) QuantumRange;
1732  return;
1733  }
1734  cyan=(MagickRealType) (1.0-red);
1735  magenta=(MagickRealType) (1.0-green);
1736  yellow=(MagickRealType) (1.0-blue);
1737  black=cyan;
1738  if (magenta < black)
1739  black=magenta;
1740  if (yellow < black)
1741  black=yellow;
1742  cyan=(MagickRealType) (MagickSafeReciprocal(1.0-black)*(cyan-black));
1743  magenta=(MagickRealType) (MagickSafeReciprocal(1.0-black)*(magenta-black));
1744  yellow=(MagickRealType) (MagickSafeReciprocal(1.0-black)*(yellow-black));
1745  pixel->colorspace=CMYKColorspace;
1746  pixel->red=(MagickRealType) QuantumRange*cyan;
1747  pixel->green=(MagickRealType) QuantumRange*magenta;
1748  pixel->blue=(MagickRealType) QuantumRange*yellow;
1749  pixel->black=(MagickRealType) QuantumRange*black;
1750 }
1751 
1752 static inline void ConvertYPbPrToRGB(const double Y,const double Pb,
1753  const double Pr,double *red,double *green,double *blue)
1754 {
1755  *red=(double) QuantumRange*(0.99999999999914679361*Y-1.2188941887145875e-06*
1756  (Pb-0.5)+1.4019995886561440468*(Pr-0.5));
1757  *green=(double) QuantumRange*(0.99999975910502514331*Y-0.34413567816504303521*
1758  (Pb-0.5)-0.71413649331646789076*(Pr-0.5));
1759  *blue=(double) QuantumRange*(1.00000124040004623180*Y+1.77200006607230409200*
1760  (Pb-0.5)+2.1453384174593273e-06*(Pr-0.5));
1761 }
1762 
1763 static inline void ConvertYCbCrToRGB(const double Y,const double Cb,
1764  const double Cr,double *red,double *green,double *blue)
1765 {
1766  ConvertYPbPrToRGB(Y,Cb,Cr,red,green,blue);
1767 }
1768 
1769 static inline void ConvertYDbDrToRGB(const double Y,const double Db,
1770  const double Dr,double *red,double *green,double *blue)
1771 {
1772  *red=(double) QuantumRange*(Y+9.2303716147657e-05*(Db-0.5)-
1773  0.52591263066186533*(Dr-0.5));
1774  *green=(double) QuantumRange*(Y-0.12913289889050927*(Db-0.5)+
1775  0.26789932820759876*(Dr-0.5));
1776  *blue=(double) QuantumRange*(Y+0.66467905997895482*(Db-0.5)-
1777  7.9202543533108e-05*(Dr-0.5));
1778 }
1779 
1780 static inline void ConvertYIQToRGB(const double Y,const double I,const double Q,
1781  double *red,double *green,double *blue)
1782 {
1783  *red=(double) QuantumRange*(Y+0.9562957197589482261*(I-0.5)+
1784  0.6210244164652610754*(Q-0.5));
1785  *green=(double) QuantumRange*(Y-0.2721220993185104464*(I-0.5)-
1786  0.6473805968256950427*(Q-0.5));
1787  *blue=(double) QuantumRange*(Y-1.1069890167364901945*(I-0.5)+
1788  1.7046149983646481374*(Q-0.5));
1789 }
1790 
1791 static inline void ConvertxyYToRGB(const double low_x,const double low_y,
1792  const double cap_Y,double *red,double *green,double *blue)
1793 {
1794  double
1795  gamma,
1796  X,
1797  Y,
1798  Z;
1799 
1800  gamma=MagickSafeReciprocal(low_y);
1801  X=gamma*cap_Y*low_x;
1802  Y=cap_Y;
1803  Z=gamma*cap_Y*(1.0-low_x-low_y);
1804  ConvertXYZToRGB(X,Y,Z,red,green,blue);
1805 }
1806 
1807 static inline void ConvertYUVToRGB(const double Y,const double U,const double V,
1808  double *red,double *green,double *blue)
1809 {
1810  *red=(double) QuantumRange*(Y-3.945707070708279e-05*(U-0.5)+
1811  1.1398279671717170825*(V-0.5));
1812  *green=(double) QuantumRange*(Y-0.3946101641414141437*(U-0.5)-
1813  0.5805003156565656797*(V-0.5));
1814  *blue=(double) QuantumRange*(Y+2.0319996843434342537*(U-0.5)-
1815  4.813762626262513e-04*(V-0.5));
1816 }
1817 
1818 static inline MagickBooleanType IsCMYKColorspace(
1819  const ColorspaceType colorspace)
1820 {
1821  if (colorspace == CMYKColorspace)
1822  return(MagickTrue);
1823  return(MagickFalse);
1824 }
1825 
1826 static inline MagickBooleanType IsGrayColorspace(
1827  const ColorspaceType colorspace)
1828 {
1829  if ((colorspace == LinearGRAYColorspace) || (colorspace == GRAYColorspace))
1830  return(MagickTrue);
1831  return(MagickFalse);
1832 }
1833 
1834 static inline MagickBooleanType IsGrayImageType(const ImageType type)
1835 {
1836  if ((type == GrayscaleType) || (type == GrayscaleAlphaType) ||
1837  (type == BilevelType))
1838  return(MagickTrue);
1839  return(MagickFalse);
1840 }
1841 
1842 static inline MagickBooleanType IsHueCompatibleColorspace(
1843  const ColorspaceType colorspace)
1844 {
1845  if ((colorspace == HCLColorspace) || (colorspace == HCLpColorspace) ||
1846  (colorspace == HSBColorspace) || (colorspace == HSIColorspace) ||
1847  (colorspace == HSLColorspace) || (colorspace == HSVColorspace))
1848  return(MagickTrue);
1849  return(MagickFalse);
1850 }
1851 
1852 static inline MagickBooleanType IsLabCompatibleColorspace(
1853  const ColorspaceType colorspace)
1854 {
1855  if ((colorspace == LabColorspace) || (colorspace == LCHColorspace) ||
1856  (colorspace == LCHabColorspace) || (colorspace == LCHuvColorspace) ||
1857  (colorspace == OklabColorspace))
1858  return(MagickTrue);
1859  return(MagickFalse);
1860 }
1861 
1862 static inline MagickBooleanType IsRGBColorspace(const ColorspaceType colorspace)
1863 {
1864  if ((colorspace == RGBColorspace) || (colorspace == scRGBColorspace) ||
1865  (colorspace == LinearGRAYColorspace))
1866  return(MagickTrue);
1867  return(MagickFalse);
1868 }
1869 
1870 static inline MagickBooleanType IssRGBColorspace(
1871  const ColorspaceType colorspace)
1872 {
1873  if ((colorspace == sRGBColorspace) || (colorspace == TransparentColorspace))
1874  return(MagickTrue);
1875  return(MagickFalse);
1876 }
1877 
1878 static inline MagickBooleanType IssRGBCompatibleColorspace(
1879  const ColorspaceType colorspace)
1880 {
1881  if ((colorspace == sRGBColorspace) || (colorspace == RGBColorspace) ||
1882  (colorspace == Adobe98Colorspace) || (colorspace == ProPhotoColorspace) ||
1883  (colorspace == DisplayP3Colorspace) || (colorspace == scRGBColorspace) ||
1884  (colorspace == TransparentColorspace) || (colorspace == GRAYColorspace) ||
1885  (colorspace == LinearGRAYColorspace))
1886  return(MagickTrue);
1887  return(MagickFalse);
1888 }
1889 
1890 static inline MagickBooleanType IsYCbCrCompatibleColorspace(
1891  const ColorspaceType colorspace)
1892 {
1893  if ((colorspace == YCbCrColorspace) ||
1894  (colorspace == Rec709YCbCrColorspace) ||
1895  (colorspace == Rec601YCbCrColorspace))
1896  return(MagickTrue);
1897  return(MagickFalse);
1898 }
1899 
1900 extern MagickPrivate void
1901  ConvertGenericToRGB(const ColorspaceType,const double,const double,
1902  const double,const double,const IlluminantType,double *,double *,double *),
1903  ConvertRGBToGeneric(const ColorspaceType,const double,const double,
1904  const double,const double,const IlluminantType,double *,double *,double *);
1905 
1906 #if defined(__cplusplus) || defined(c_plusplus)
1907 }
1908 #endif
1909 
1910 #endif