18#ifndef MAGICKCORE_COLORSPACE_PRIVATE_H
19#define MAGICKCORE_COLORSPACE_PRIVATE_H
21#include "MagickCore/image.h"
22#include "MagickCore/image-private.h"
23#include "MagickCore/pixel.h"
24#include "MagickCore/pixel-accessor.h"
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)
33 illuminant_tristimulus[] =
35 { 1.09850, 1.00000, 0.35585 },
36 { 0.99072, 1.00000, 0.85223 },
37 { 0.98074, 1.00000, 1.18232 },
38 { 0.96422, 1.00000, 0.82521 },
39 { 0.95682, 1.00000, 0.92149 },
40 { 0.95047, 1.00000, 1.08883 },
41 { 0.94972, 1.00000, 1.22638 },
42 { 1.00000, 1.00000, 1.00000 },
43 { 0.99186, 1.00000, 0.67393 },
44 { 0.95041, 1.00000, 1.08747 },
45 { 1.00962, 1.00000, 0.64350 }
48#if defined(__cplusplus) || defined(c_plusplus)
53static inline void ConvertAdobe98ToXYZ(
const double red,
const double green,
54 const double blue,
double *X,
double *Y,
double *Z)
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;
72static inline void ConvertXYZToRGB(
const double X,
const double Y,
const double Z,
73 double *red,
double *green,
double *blue)
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);
88static inline void ConvertAdobe98ToRGB(
const double r,
const double g,
89 const double b,
double *red,
double *green,
double *blue)
96 ConvertAdobe98ToXYZ(r,g,b,&X,&Y,&Z);
97 ConvertXYZToRGB(X,Y,Z,red,green,blue);
100static inline void ConvertCMYKToRGB(
PixelInfo *pixel)
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)));
110static inline void ConvertCMYToRGB(
const double cyan,
const double magenta,
111 const double yellow,
double *red,
double *green,
double *blue)
113 *red=(double) QuantumRange*(1.0-cyan);
114 *green=(double) QuantumRange*(1.0-magenta);
115 *blue=(double) QuantumRange*(1.0-yellow);
118static inline void ConvertHCLToRGB(
const double hue,
const double chroma,
119 const double luma,
double *red,
double *green,
double *blue)
133 assert(red != (
double *) NULL);
134 assert(green != (
double *) NULL);
135 assert(blue != (
double *) NULL);
138 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
142 if ((0.0 <= h) && (h < 1.0))
148 if ((1.0 <= h) && (h < 2.0))
154 if ((2.0 <= h) && (h < 3.0))
160 if ((3.0 <= h) && (h < 4.0))
166 if ((4.0 <= h) && (h < 5.0))
172 if ((5.0 <= h) && (h < 6.0))
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);
183static inline void ConvertHCLpToRGB(
const double hue,
const double chroma,
184 const double luma,
double *red,
double *green,
double *blue)
199 assert(red != (
double *) NULL);
200 assert(green != (
double *) NULL);
201 assert(blue != (
double *) NULL);
204 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
208 if ((0.0 <= h) && (h < 1.0))
214 if ((1.0 <= h) && (h < 2.0))
220 if ((2.0 <= h) && (h < 3.0))
226 if ((3.0 <= h) && (h < 4.0))
232 if ((4.0 <= h) && (h < 5.0))
238 if ((5.0 <= h) && (h < 6.0))
243 m=luma-(0.298839*r+0.586811*g+0.114350*b);
253 z=(1.0-luma)/(m+c-luma);
256 *red=(double) QuantumRange*(z*r+m);
257 *green=(double) QuantumRange*(z*g+m);
258 *blue=(double) QuantumRange*(z*b+m);
261static inline void ConvertHSBToRGB(
const double hue,
const double saturation,
262 const double brightness,
double *red,
double *green,
double *blue)
274 assert(red != (
double *) NULL);
275 assert(green != (
double *) NULL);
276 assert(blue != (
double *) NULL);
277 if (fabs(saturation) < MagickEpsilon)
279 *red=(double) QuantumRange*brightness;
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)));
294 *red=(double) QuantumRange*brightness;
295 *green=(double) QuantumRange*t;
296 *blue=(double) QuantumRange*p;
301 *red=(double) QuantumRange*q;
302 *green=(double) QuantumRange*brightness;
303 *blue=(double) QuantumRange*p;
308 *red=(double) QuantumRange*p;
309 *green=(double) QuantumRange*brightness;
310 *blue=(double) QuantumRange*t;
315 *red=(double) QuantumRange*p;
316 *green=(double) QuantumRange*q;
317 *blue=(double) QuantumRange*brightness;
322 *red=(double) QuantumRange*t;
323 *green=(double) QuantumRange*p;
324 *blue=(double) QuantumRange*brightness;
329 *red=(double) QuantumRange*brightness;
330 *green=(double) QuantumRange*p;
331 *blue=(double) QuantumRange*q;
337static inline void ConvertHSIToRGB(
const double hue,
const double saturation,
338 const double intensity,
double *red,
double *green,
double *blue)
349 assert(red != (
double *) NULL);
350 assert(green != (
double *) NULL);
351 assert(blue != (
double *) NULL);
353 h-=360.0*floor(h/360.0);
356 b=intensity*(1.0-saturation);
357 r=intensity*(1.0+saturation*cos(h*(MagickPI/180.0))/cos((60.0-h)*
365 r=intensity*(1.0-saturation);
366 g=intensity*(1.0+saturation*cos(h*(MagickPI/180.0))/cos((60.0-h)*
373 g=intensity*(1.0-saturation);
374 b=intensity*(1.0+saturation*cos(h*(MagickPI/180.0))/cos((60.0-h)*
378 *red=(double) QuantumRange*r;
379 *green=(double) QuantumRange*g;
380 *blue=(double) QuantumRange*b;
383static inline void ConvertHSVToRGB(
const double hue,
const double saturation,
384 const double value,
double *red,
double *green,
double *blue)
395 assert(red != (
double *) NULL);
396 assert(green != (
double *) NULL);
397 assert(blue != (
double *) NULL);
401 h-=360.0*floor(h/360.0);
403 x=c*(1.0-fabs(h-2.0*floor(h/2.0)-1.0));
404 switch ((
int) floor(h))
409 *red=(double) QuantumRange*(min+c);
410 *green=(double) QuantumRange*(min+x);
411 *blue=(double) QuantumRange*min;
416 *red=(double) QuantumRange*(min+x);
417 *green=(double) QuantumRange*(min+c);
418 *blue=(double) QuantumRange*min;
423 *red=(double) QuantumRange*min;
424 *green=(double) QuantumRange*(min+c);
425 *blue=(double) QuantumRange*(min+x);
430 *red=(double) QuantumRange*min;
431 *green=(double) QuantumRange*(min+x);
432 *blue=(double) QuantumRange*(min+c);
437 *red=(double) QuantumRange*(min+x);
438 *green=(double) QuantumRange*min;
439 *blue=(double) QuantumRange*(min+c);
444 *red=(double) QuantumRange*(min+c);
445 *green=(double) QuantumRange*min;
446 *blue=(double) QuantumRange*(min+x);
452static inline void ConvertHWBToRGB(
const double hue,
const double whiteness,
453 const double blackness,
double *red,
double *green,
double *blue)
469 assert(red != (
double *) NULL);
470 assert(green != (
double *) NULL);
471 assert(blue != (
double *) NULL);
473 if (fabs(hue-(-1.0)) < MagickEpsilon)
475 *red=(double) QuantumRange*v;
476 *green=(double) QuantumRange*v;
477 *blue=(double) QuantumRange*v;
480 i=CastDoubleToLong(floor(6.0*hue));
484 n=whiteness+f*(v-whiteness);
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;
495 *red=(double) QuantumRange*r;
496 *green=(double) QuantumRange*g;
497 *blue=(double) QuantumRange*b;
500static inline void ConvertLabToXYZ(
const double L,
const double a,
const double b,
501 const IlluminantType illuminant,
double *X,
double *Y,
double *Z)
511 if ((x*x*x) > CIEEpsilon)
514 x=(116.0*x-16.0)/CIEK;
515 if (L > (CIEK*CIEEpsilon))
519 if ((z*z*z) > CIEEpsilon)
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;
528static inline void ConvertLabToRGB(
const double L,
const double a,
529 const double b,
const IlluminantType illuminant,
double *red,
double *green,
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);
541static inline void ConvertLCHabToXYZ(
const double luma,
const double chroma,
542 const double hue,
const IlluminantType illuminant,
double *X,
double *Y,
545 ConvertLabToXYZ(luma,chroma*cos(DegreesToRadians(hue)),chroma*
546 sin(DegreesToRadians(hue)),illuminant,X,Y,Z);
549static inline void ConvertLCHabToRGB(
const double luma,
const double chroma,
550 const double hue,
const IlluminantType illuminant,
double *red,
double *green,
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,
566 ConvertXYZToRGB(X,Y,Z,red,green,blue);
569static inline void ConvertLuvToXYZ(
const double L,
const double u,
const double v,
570 const IlluminantType illuminant,
double *X,
double *Y,
double *Z)
575 if (L > (CIEK*CIEEpsilon))
576 *Y=(double) pow((L+16.0)/116.0,3.0);
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);
596static inline void ConvertLCHuvToXYZ(
const double luma,
const double chroma,
597 const double hue,
const IlluminantType illuminant,
double *X,
double *Y,
600 ConvertLuvToXYZ(luma,chroma*cos(DegreesToRadians(hue)),chroma*
601 sin(DegreesToRadians(hue)),illuminant,X,Y,Z);
604static inline void ConvertLCHuvToRGB(
const double luma,
const double chroma,
605 const double hue,
const IlluminantType illuminant,
double *red,
double *green,
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,
621 ConvertXYZToRGB(X,Y,Z,red,green,blue);
624static inline void ConvertLMSToXYZ(
const double L,
const double M,
const double S,
625 double *X,
double *Y,
double *Z)
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;
632static inline void ConvertLMSToRGB(
const double L,
const double M,
633 const double S,
double *red,
double *green,
double *blue)
640 ConvertLMSToXYZ(L,M,S,&X,&Y,&Z);
641 ConvertXYZToRGB(X,Y,Z,red,green,blue);
644static inline void ConvertDisplayP3ToXYZ(
const double red,
const double green,
645 const double blue,
double *X,
double *Y,
double *Z)
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;
663static inline void ConvertDisplayP3ToRGB(
const double r,
const double g,
664 const double b,
double *red,
double *green,
double *blue)
671 ConvertDisplayP3ToXYZ(r,g,b,&X,&Y,&Z);
672 ConvertXYZToRGB(X,Y,Z,red,green,blue);
675static inline void ConvertLuvToRGB(
const double L,
const double u,
676 const double v,
const IlluminantType illuminant,
double *red,
double *green,
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);
688static inline void ConvertProPhotoToXYZ(
const double red,
const double green,
689 const double blue,
double *X,
double *Y,
double *Z)
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;
708static inline void ConvertProPhotoToRGB(
const double r,
const double g,
709 const double b,
double *red,
double *green,
double *blue)
716 ConvertProPhotoToXYZ(r,g,b,&X,&Y,&Z);
717 ConvertXYZToRGB(X,Y,Z,red,green,blue);
720static inline void ConvertRGBToCMY(
const double red,
const double green,
721 const double blue,
double *cyan,
double *magenta,
double *yellow)
723 *cyan=QuantumScale*((double) QuantumRange-red);
724 *magenta=QuantumScale*((double) QuantumRange-green);
725 *yellow=QuantumScale*((double) QuantumRange-blue);
728static inline void ConvertRGBToHCL(
const double red,
const double green,
729 const double blue,
double *hue,
double *chroma,
double *luma)
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));
745 if (fabs(c) < MagickEpsilon)
748 if (fabs(red-max) < MagickEpsilon)
749 h=fmod((green-blue)/c+6.0,6.0);
751 if (fabs(green-max) < MagickEpsilon)
752 h=((blue-red)/c)+2.0;
754 if (fabs(blue-max) < MagickEpsilon)
755 h=((red-green)/c)+4.0;
757 *chroma=QuantumScale*c;
758 *luma=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
761static inline void ConvertRGBToHCLp(
const double red,
const double green,
762 const double blue,
double *hue,
double *chroma,
double *luma)
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));
778 if (fabs(c) < MagickEpsilon)
781 if (fabs(red-max) < MagickEpsilon)
782 h=fmod((green-blue)/c+6.0,6.0);
784 if (fabs(green-max) < MagickEpsilon)
785 h=((blue-red)/c)+2.0;
787 if (fabs(blue-max) < MagickEpsilon)
788 h=((red-green)/c)+4.0;
790 *chroma=QuantumScale*c;
791 *luma=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
794static inline void ConvertRGBToHSB(
const double red,
const double green,
795 const double blue,
double *hue,
double *saturation,
double *brightness)
805 assert(hue != (
double *) NULL);
806 assert(saturation != (
double *) NULL);
807 assert(brightness != (
double *) NULL);
811 min=red < green ? red : green;
814 max=red > green ? red : green;
817 if (fabs(max) < MagickEpsilon)
820 *saturation=delta/max;
821 *brightness=QuantumScale*max;
822 if (fabs(delta) < MagickEpsilon)
824 if (fabs(red-max) < MagickEpsilon)
825 *hue=(green-blue)/delta;
827 if (fabs(green-max) < MagickEpsilon)
828 *hue=2.0+(blue-red)/delta;
830 *hue=4.0+(red-green)/delta;
836static inline void ConvertRGBToHSI(
const double red,
const double green,
837 const double blue,
double *hue,
double *saturation,
double *intensity)
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)
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;
865static inline void ConvertRGBToXYZ(
const double red,
const double green,
866 const double blue,
double *X,
double *Y,
double *Z)
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;
884static inline void ConvertXYZToAdobe98(
const double X,
const double Y,
885 const double Z,
double *red,
double *green,
double *blue)
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);
900static inline void ConvertRGBToAdobe98(
const double red,
const double green,
901 const double blue,
double *r,
double *g,
double *b)
908 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
909 ConvertXYZToAdobe98(X,Y,Z,r,g,b);
912static inline void ConvertXYZToDisplayP3(
const double X,
const double Y,
913 const double Z,
double *red,
double *green,
double *blue)
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);
928static inline void ConvertRGBToDisplayP3(
const double red,
const double green,
929 const double blue,
double *r,
double *g,
double *b)
936 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
937 ConvertXYZToDisplayP3(X,Y,Z,r,g,b);
940static inline void ConvertRGBToHSV(
const double red,
const double green,
941 const double blue,
double *hue,
double *saturation,
double *value)
951 assert(hue != (
double *) NULL);
952 assert(saturation != (
double *) NULL);
953 assert(value != (
double *) NULL);
954 max=MagickMax(QuantumScale*red,MagickMax(QuantumScale*green,
956 min=MagickMin(QuantumScale*red,MagickMin(QuantumScale*green,
966 if (fabs(max-QuantumScale*red) < MagickEpsilon)
968 *hue=(QuantumScale*green-QuantumScale*blue)/c;
969 if ((QuantumScale*green) < (QuantumScale*blue))
973 if (fabs(max-QuantumScale*green) < MagickEpsilon)
974 *hue=2.0+(QuantumScale*blue-QuantumScale*red)/c;
976 *hue=4.0+(QuantumScale*red-QuantumScale*green)/c;
978 *saturation=c*PerceptibleReciprocal(max);
981static inline void ConvertRGBToHWB(
const double red,
const double green,
982 const double blue,
double *hue,
double *whiteness,
double *blackness)
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)
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;
1012static inline void ConvertXYZToLab(
const double X,
const double Y,
const double Z,
1013 const IlluminantType illuminant,
double *L,
double *a,
double *b)
1020 if ((X/illuminant_tristimulus[illuminant].x) > CIEEpsilon)
1021 x=pow(X/illuminant_tristimulus[illuminant].x,1.0/3.0);
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);
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);
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;
1037static inline void ConvertRGBToLab(
const double red,
const double green,
1038 const double blue,
const IlluminantType illuminant,
double *L,
double *a,
1046 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1047 ConvertXYZToLab(X,Y,Z,illuminant,L,a,b);
1050static inline void ConvertXYZToLCHab(
const double X,
const double Y,
1051 const double Z,
const IlluminantType illuminant,
double *luma,
double *chroma,
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;
1065static inline void ConvertRGBToLCHab(
const double red,
const double green,
1066 const double blue,
const IlluminantType illuminant,
double *luma,
double *chroma,
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);
1084static inline void ConvertXYZToLuv(
const double X,
const double Y,
const double Z,
1085 const IlluminantType illuminant,
double *L,
double *u,
double *v)
1090 if ((Y/illuminant_tristimulus[illuminant].y) > CIEEpsilon)
1091 *L=(double) (116.0*pow(Y/illuminant_tristimulus[illuminant].y,
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)));
1105 *u=(*u+134.0)/354.0;
1106 *v=(*v+140.0)/262.0;
1109static inline void ConvertXYZToLCHuv(
const double X,
const double Y,
1110 const double Z,
const IlluminantType illuminant,
double *luma,
double *chroma,
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;
1124static inline void ConvertRGBToLCHuv(
const double red,
const double green,
1125 const double blue,
const IlluminantType illuminant,
double *luma,
double *chroma,
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);
1143static inline void ConvertXYZToProPhoto(
const double X,
const double Y,
1144 const double Z,
double *red,
double *green,
double *blue)
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);
1159static inline void ConvertRGBToProPhoto(
const double red,
const double green,
1160 const double blue,
double *r,
double *g,
double *b)
1167 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1168 ConvertXYZToProPhoto(X,Y,Z,r,g,b);
1171static inline void ConvertXYZToLMS(
const double x,
const double y,
1172 const double z,
double *L,
double *M,
double *S)
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;
1179static inline void ConvertRGBToLMS(
const double red,
const double green,
1180 const double blue,
double *L,
double *M,
double *S)
1187 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1188 ConvertXYZToLMS(X,Y,Z,L,M,S);
1191static inline void ConvertRGBToLuv(
const double red,
const double green,
1192 const double blue,
const IlluminantType illuminant,
double *L,
double *u,
1200 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1201 ConvertXYZToLuv(X,Y,Z,illuminant,L,u,v);
1204static inline void ConvertRGBToxyY(
const double red,
const double green,
1205 const double blue,
double *low_x,
double *low_y,
double *cap_Y)
1213 ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
1214 gamma=PerceptibleReciprocal(X+Y+Z);
1220static inline void ConvertXYZToJzazbz(
const double X,
const double Y,
1221 const double Z,
const double white_luminance,
double *Jz,
double *az,
double *bz)
1223#define Jzazbz_b 1.15
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)
1246 Xp=(Jzazbz_b*X-(Jzazbz_b-1)*Z);
1247 Yp=(Jzazbz_g*Y-(Jzazbz_g-1)*X);
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);
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;
1264static inline void ConvertJzazbzToXYZ(
const double Jz,
const double az,
1265 const double bz,
const double white_luminance,
double *X,
double *Y,
double *Z)
1283 Iz=gamma/(Jzazbz_d-Jzazbz_d*gamma+1.0);
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/
1292 gamma=pow(Mp,1.0/Jzazbz_p);
1293 M=white_luminance*pow((Jzazbz_c1-gamma)/(Jzazbz_c3*gamma-Jzazbz_c2),1.0/
1295 gamma=pow(Sp,1.0/Jzazbz_p);
1296 S=white_luminance*pow((Jzazbz_c1-gamma)/(Jzazbz_c3*gamma-Jzazbz_c2),1.0/
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;
1306static inline void ConvertRGBToJzazbz(
const double red,
const double green,
1307 const double blue,
const double white_luminance,
double *Jz,
double *az,
1315 ConvertRGBToXYZ(red,blue,green,&X,&Y,&Z);
1316 ConvertXYZToJzazbz(X,Y,Z,white_luminance,Jz,az,bz);
1319static inline void ConvertJzazbzToRGB(
const double Jz,
const double az,
1320 const double bz,
const double white_luminance,
double *red,
double *green,
1328 ConvertJzazbzToXYZ(Jz,az,bz,white_luminance,&X,&Y,&Z);
1329 ConvertXYZToRGB(X,Y,Z,red,blue,green);
1332static inline void ConvertOklabToRGB(
const double L,
const double a,
1333 const double b,
double *red,
double *green,
double *blue)
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);
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);
1357static inline void ConvertRGBToOklab(
const double red,
const double green,
1358 const double blue,
double *L,
double *a,
double *b)
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;
1379static inline void ConvertOklchToRGB(
const double L,
const double C,
1380 const double h,
double *red,
double *green,
double *blue)
1386 a=C*cos(2.0*MagickPI*h);
1387 b=C*sin(2.0*MagickPI*h);
1388 ConvertOklabToRGB(L,a,b,red,green,blue);
1391static inline void ConvertRGBToOklch(
const double red,
const double green,
1392 const double blue,
double *L,
double *C,
double *h)
1398 ConvertRGBToOklab(red,green,blue,L,&a,&b);
1400 *h=0.5+0.5*atan2(-b,-a)/MagickPI;
1403static inline void ConvertRGBToYDbDr(
const double red,
const double green,
1404 const double blue,
double *Y,
double *Db,
double *Dr)
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;
1411static inline void ConvertRGBToYIQ(
const double red,
const double green,
1412 const double blue,
double *Y,
double *I,
double *Q)
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;
1419static inline void ConvertRGBToYPbPr(
const double red,
const double green,
1420 const double blue,
double *Y,
double *Pb,
double *Pr)
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;
1427static inline void ConvertRGBToYCbCr(
const double red,
const double green,
1428 const double blue,
double *Y,
double *Cb,
double *Cr)
1430 ConvertRGBToYPbPr(red,green,blue,Y,Cb,Cr);
1433static inline void ConvertRGBToYUV(
const double red,
const double green,
1434 const double blue,
double *Y,
double *U,
double *V)
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;
1441static inline void ConvertRGBToCMYK(
PixelInfo *pixel)
1452 if (pixel->colorspace != sRGBColorspace)
1454 red=QuantumScale*pixel->red;
1455 green=QuantumScale*pixel->green;
1456 blue=QuantumScale*pixel->blue;
1460 red=QuantumScale*DecodePixelGamma(pixel->red);
1461 green=QuantumScale*DecodePixelGamma(pixel->green);
1462 blue=QuantumScale*DecodePixelGamma(pixel->blue);
1464 if ((fabs((
double) red) < MagickEpsilon) &&
1465 (fabs((
double) green) < MagickEpsilon) &&
1466 (fabs((
double) blue) < MagickEpsilon))
1468 pixel->black=(MagickRealType) QuantumRange;
1471 cyan=(MagickRealType) (1.0-red);
1472 magenta=(MagickRealType) (1.0-green);
1473 yellow=(MagickRealType) (1.0-blue);
1475 if (magenta < black)
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;
1489static inline void ConvertYPbPrToRGB(
const double Y,
const double Pb,
1490 const double Pr,
double *red,
double *green,
double *blue)
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));
1500static inline void ConvertYCbCrToRGB(
const double Y,
const double Cb,
1501 const double Cr,
double *red,
double *green,
double *blue)
1503 ConvertYPbPrToRGB(Y,Cb,Cr,red,green,blue);
1506static inline void ConvertYDbDrToRGB(
const double Y,
const double Db,
1507 const double Dr,
double *red,
double *green,
double *blue)
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));
1517static inline void ConvertYIQToRGB(
const double Y,
const double I,
const double Q,
1518 double *red,
double *green,
double *blue)
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));
1528static inline void ConvertxyYToRGB(
const double low_x,
const double low_y,
1529 const double cap_Y,
double *red,
double *green,
double *blue)
1537 gamma=PerceptibleReciprocal(low_y);
1538 X=gamma*cap_Y*low_x;
1540 Z=gamma*cap_Y*(1.0-low_x-low_y);
1541 ConvertXYZToRGB(X,Y,Z,red,green,blue);
1544static inline void ConvertYUVToRGB(
const double Y,
const double U,
const double V,
1545 double *red,
double *green,
double *blue)
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));
1555static inline MagickBooleanType IsCMYKColorspace(
1556 const ColorspaceType colorspace)
1558 if (colorspace == CMYKColorspace)
1560 return(MagickFalse);
1563static inline MagickBooleanType IsGrayColorspace(
1564 const ColorspaceType colorspace)
1566 if ((colorspace == LinearGRAYColorspace) || (colorspace == GRAYColorspace))
1568 return(MagickFalse);
1571static inline MagickBooleanType IsGrayImageType(
const ImageType type)
1573 if ((type == GrayscaleType) || (type == GrayscaleAlphaType) ||
1574 (type == BilevelType))
1576 return(MagickFalse);
1579static inline MagickBooleanType IsHueCompatibleColorspace(
1580 const ColorspaceType colorspace)
1582 if ((colorspace == HCLColorspace) || (colorspace == HCLpColorspace) ||
1583 (colorspace == HSBColorspace) || (colorspace == HSIColorspace) ||
1584 (colorspace == HSLColorspace) || (colorspace == HSVColorspace))
1586 return(MagickFalse);
1589static inline MagickBooleanType IsLabCompatibleColorspace(
1590 const ColorspaceType colorspace)
1592 if ((colorspace == LabColorspace) || (colorspace == LCHColorspace) ||
1593 (colorspace == LCHabColorspace) || (colorspace == LCHuvColorspace) ||
1594 (colorspace == OklabColorspace) || (colorspace == OklchColorspace))
1596 return(MagickFalse);
1599static inline MagickBooleanType IsRGBColorspace(
const ColorspaceType colorspace)
1601 if ((colorspace == RGBColorspace) || (colorspace == scRGBColorspace) ||
1602 (colorspace == LinearGRAYColorspace))
1604 return(MagickFalse);
1607static inline MagickBooleanType IssRGBColorspace(
1608 const ColorspaceType colorspace)
1610 if ((colorspace == sRGBColorspace) || (colorspace == TransparentColorspace))
1612 return(MagickFalse);
1615static inline MagickBooleanType IssRGBCompatibleColorspace(
1616 const ColorspaceType colorspace)
1618 if ((colorspace == sRGBColorspace) || (colorspace == RGBColorspace) ||
1619 (colorspace == Adobe98Colorspace) || (colorspace == ProPhotoColorspace) ||
1620 (colorspace == DisplayP3Colorspace) || (colorspace == scRGBColorspace) ||
1621 (colorspace == TransparentColorspace) || (colorspace == GRAYColorspace) ||
1622 (colorspace == LinearGRAYColorspace))
1624 return(MagickFalse);
1627static inline MagickBooleanType IsYCbCrCompatibleColorspace(
1628 const ColorspaceType colorspace)
1630 if ((colorspace == YCbCrColorspace) ||
1631 (colorspace == Rec709YCbCrColorspace) ||
1632 (colorspace == Rec601YCbCrColorspace))
1634 return(MagickFalse);
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 *);
1643#if defined(__cplusplus) || defined(c_plusplus)