% MagickCore Image Colorspace Methods %
% %
% Software Design %
-% John Cristy %
+% Cristy %
% July 1992 %
% %
% %
-% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
+% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
%
*/
+static inline void ConvertRGBToCMY(const double red,const double green,
+ const double blue,double *cyan,double *magenta,double *yellow)
+{
+ *cyan=QuantumScale*(QuantumRange-red);
+ *magenta=QuantumScale*(QuantumRange-green);
+ *yellow=QuantumScale*(QuantumRange-blue);
+}
+
static inline void ConvertXYZToLMS(const double x,const double y,
const double z,double *L,double *M,double *S)
{
- /*
- Convert XYZ to LMS colorspace.
- */
- assert(L != (double *) NULL);
- assert(M != (double *) NULL);
- assert(S != (double *) NULL);
*L=0.7328*x+0.4296*y-0.1624*z;
*M=(-0.7036*x+1.6975*y+0.0061*z);
*S=0.0030*x+0.0136*y+0.9834*z;
ConvertXYZToLuv(X,Y,Z,L,u,v);
}
+static void ConvertRGBToYDbDr(const double red,const double green,
+ const double blue,double *Y,double *Db,double *Dr)
+{
+ *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
+ *Db=QuantumScale*(-0.450*red-0.883*green+1.333*blue)+0.5;
+ *Dr=QuantumScale*(-1.333*red+1.116*green+0.217*blue)+0.5;
+}
+
static void ConvertRGBToYIQ(const double red,const double green,
const double blue,double *Y,double *I,double *Q)
{
- /*
- Convert RGB to YIQ colorspace.
- */
- assert(Y != (double *) NULL);
- assert(I != (double *) NULL);
- assert(Q != (double *) NULL);
- *Y=0.298839*red+0.586811*green+0.114350*blue;
- *I=0.595716*red-0.274453*green-0.321263*blue+0.5;
- *Q=0.211456*red-0.522591*green+0.311135*blue+0.5;
+ *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
+ *I=QuantumScale*(0.595716*red-0.274453*green-0.321263*blue)+0.5;
+ *Q=QuantumScale*(0.211456*red-0.522591*green+0.311135*blue)+0.5;
}
static void ConvertRGBToYPbPr(const double red,const double green,
const double blue,double *Y,double *Pb,double *Pr)
{
- /*
- Convert RGB to YPbPr colorspace.
- */
- assert(Y != (double *) NULL);
- assert(Pb != (double *) NULL);
- assert(Pr != (double *) NULL);
- *Y=0.298839*QuantumScale*red+0.586811*QuantumScale*green+0.114350*
- QuantumScale*blue;
- *Pb=(-0.1687367)*QuantumScale*red-0.331264*QuantumScale*green+0.5*
- QuantumScale*blue+0.5;
- *Pr=0.5*QuantumScale*red-0.418688*QuantumScale*green-0.081312*
- QuantumScale*blue+0.5;
+ *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
+ *Pb=QuantumScale*((-0.1687367)*red-0.331264*green+0.5*blue)+0.5;
+ *Pr=QuantumScale*(0.5*red-0.418688*green-0.081312*blue)+0.5;
}
static void ConvertRGBToYCbCr(const double red,const double green,
const double blue,double *Y,double *Cb,double *Cr)
{
- /*
- Convert RGB to -YCbCr colorspace.
- */
- assert(Y != (double *) NULL);
- assert(Cb != (double *) NULL);
- assert(Cr != (double *) NULL);
ConvertRGBToYPbPr(red,green,blue,Y,Cb,Cr);
}
static void ConvertRGBToYUV(const double red,const double green,
const double blue,double *Y,double *U,double *V)
{
- /*
- Convert RGB to YUV colorspace.
- */
- assert(Y != (double *) NULL);
- assert(U != (double *) NULL);
- assert(V != (double *) NULL);
- *Y=0.298839*red+0.586811*green+0.114350*blue;
- *U=(-0.147)*red-0.289*green+0.436*blue+0.5;
- *V=0.615*red-0.515*green-0.100*blue+0.5;
+ *Y=QuantumScale*(0.298839*red+0.586811*green+0.114350*blue);
+ *U=QuantumScale*((-0.147)*red-0.289*green+0.436*blue)+0.5;
+ *V=QuantumScale*(0.615*red-0.515*green-0.100*blue)+0.5;
}
static MagickBooleanType sRGBTransformImage(Image *image,
progress=0;
switch (colorspace)
{
- case CMYColorspace:
- {
- /*
- Convert RGB to CMY colorspace.
- */
- if (image->storage_class == PseudoClass)
- {
- if (SyncImage(image,exception) == MagickFalse)
- return(MagickFalse);
- if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
- return(MagickFalse);
- }
- image_view=AcquireAuthenticCacheView(image,exception);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static,4) shared(status) \
- magick_threads(image,image,image->rows,1)
-#endif
- for (y=0; y < (ssize_t) image->rows; y++)
- {
- MagickBooleanType
- sync;
-
- register ssize_t
- x;
-
- register Quantum
- *restrict q;
-
- if (status == MagickFalse)
- continue;
- q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
- exception);
- if (q == (Quantum *) NULL)
- {
- status=MagickFalse;
- continue;
- }
- for (x=0; x < (ssize_t) image->columns; x++)
- {
- double
- cyan,
- magenta,
- yellow;
-
- cyan=(MagickRealType) GetPixelCyan(image,q);
- magenta=(MagickRealType) GetPixelMagenta(image,q);
- yellow=(MagickRealType) GetPixelYellow(image,q);
- SetPixelCyan(image,ClampToQuantum(QuantumRange-cyan),q);
- SetPixelMagenta(image,ClampToQuantum(QuantumRange-magenta),q);
- SetPixelYellow(image,ClampToQuantum(QuantumRange-yellow),q);
- q+=GetPixelChannels(image);
- }
- sync=SyncCacheViewAuthenticPixels(image_view,exception);
- if (sync == MagickFalse)
- status=MagickFalse;
- }
- image_view=DestroyCacheView(image_view);
- image->type=image->alpha_trait != BlendPixelTrait ? ColorSeparationType :
- ColorSeparationMatteType;
- if (SetImageColorspace(image,colorspace,exception) == MagickFalse)
- return(MagickFalse);
- return(status);
- }
case CMYKColorspace:
{
PixelInfo
image->type=GrayscaleType;
return(status);
}
+ case CMYColorspace:
case HCLColorspace:
case HCLpColorspace:
case HSBColorspace:
case LuvColorspace:
case XYZColorspace:
case YCbCrColorspace:
+ case YDbDrColorspace:
case YIQColorspace:
case YPbPrColorspace:
case YUVColorspace:
blue=(double) GetPixelBlue(image,q);
switch (colorspace)
{
+ case CMYColorspace:
+ {
+ ConvertRGBToCMY(red,green,blue,&X,&Y,&Z);
+ break;
+ }
case HCLColorspace:
{
ConvertRGBToHCL(red,green,blue,&X,&Y,&Z);
ConvertRGBToYCbCr(red,green,blue,&X,&Y,&Z);
break;
}
+ case YDbDrColorspace:
+ {
+ ConvertRGBToYDbDr(red,green,blue,&X,&Y,&Z);
+ break;
+ }
case YIQColorspace:
{
ConvertRGBToYIQ(red,green,blue,&X,&Y,&Z);
break;
}
default:
+ {
+ X=QuantumScale*red;
+ Y=QuantumScale*green;
+ Z=QuantumScale*blue;
break;
+ }
}
SetPixelRed(image,ClampToQuantum(QuantumRange*X),q);
SetPixelGreen(image,ClampToQuantum(QuantumRange*Y),q);
green,
red;
- red=(double) GetPixelRed(image,q);
- green=(double) GetPixelGreen(image,q);
- blue=(double) GetPixelBlue(image,q);
+ red=(double) DecodePixelGamma((MagickRealType)
+ GetPixelRed(image,q));
+ green=(double) DecodePixelGamma((MagickRealType)
+ GetPixelGreen(image,q));
+ blue=(double) DecodePixelGamma((MagickRealType)
+ GetPixelBlue(image,q));
SetPixelRed(image,logmap[ScaleQuantumToMap(ClampToQuantum(red))],q);
SetPixelGreen(image,logmap[ScaleQuantumToMap(ClampToQuantum(green))],
q);
/*
Initialize YCbCr tables (ITU-R BT.709):
- Y = 0.212600*R+0.715200*G+0.072200*B
+ Y = 0.212656*R+0.715158*G+0.072186*B
Cb= -0.114572*R-0.385428*G+0.500000*B
Cr= 0.500000*R-0.454153*G-0.045847*B
#endif
for (i=0; i <= (ssize_t) MaxMap; i++)
{
- x_map[i].x=(MagickRealType) (0.212600*(double) i);
- y_map[i].x=(MagickRealType) (0.715200*(double) i);
- z_map[i].x=(MagickRealType) (0.072200*(double) i);
+ x_map[i].x=(MagickRealType) (0.212656*(double) i);
+ y_map[i].x=(MagickRealType) (0.715158*(double) i);
+ z_map[i].x=(MagickRealType) (0.072186*(double) i);
x_map[i].y=(MagickRealType) (-0.114572*(double) i);
y_map[i].y=(MagickRealType) (-0.385428*(double) i);
z_map[i].y=(MagickRealType) (0.500000*(double) i);
MagickExport MagickBooleanType SetImageColorspace(Image *image,
const ColorspaceType colorspace,ExceptionInfo *exception)
{
+ ImageType
+ type;
+
+ MagickBooleanType
+ status;
+
if (image->colorspace == colorspace)
return(MagickTrue);
image->colorspace=colorspace;
image->rendering_intent=UndefinedIntent;
- image->gamma=1.000;
+ image->gamma=1.000/2.200;
(void) ResetMagickMemory(&image->chromaticity,0,sizeof(image->chromaticity));
+ type=image->type;
if (IsGrayColorspace(colorspace) != MagickFalse)
{
- if ((image->intensity != Rec601LuminancePixelIntensityMethod) &&
- (image->intensity != Rec709LuminancePixelIntensityMethod) &&
- (image->intensity != UndefinedPixelIntensityMethod))
- image->gamma=1.000/2.200;
- image->type=GrayscaleType;
+ if ((image->intensity == Rec601LuminancePixelIntensityMethod) ||
+ (image->intensity == Rec709LuminancePixelIntensityMethod))
+ image->gamma=1.000;
+ type=GrayscaleType;
}
else
- if (IssRGBColorspace(colorspace) != MagickFalse)
- image->gamma=1.000/2.200;
- if (image->gamma == (1.000/2.200))
- {
- image->rendering_intent=PerceptualIntent;
- image->gamma=1.000/2.200;
- image->chromaticity.red_primary.x=0.6400;
- image->chromaticity.red_primary.y=0.3300;
- image->chromaticity.red_primary.z=0.0300;
- image->chromaticity.green_primary.x=0.3000;
- image->chromaticity.green_primary.y=0.6000;
- image->chromaticity.green_primary.z=0.1000;
- image->chromaticity.blue_primary.x=0.1500;
- image->chromaticity.blue_primary.y=0.0600;
- image->chromaticity.blue_primary.z=0.7900;
- image->chromaticity.white_point.x=0.3127;
- image->chromaticity.white_point.y=0.3290;
- image->chromaticity.white_point.z=0.3583;
- }
- if (IsGrayColorspace(colorspace) != MagickFalse)
- image->type=GrayscaleType;
- return(SyncImagePixelCache(image,exception));
+ if ((IsRGBColorspace(colorspace) != MagickFalse) ||
+ (colorspace == XYZColorspace))
+ image->gamma=1.000;
+ else
+ {
+ image->rendering_intent=PerceptualIntent;
+ image->chromaticity.red_primary.x=0.6400;
+ image->chromaticity.red_primary.y=0.3300;
+ image->chromaticity.red_primary.z=0.0300;
+ image->chromaticity.green_primary.x=0.3000;
+ image->chromaticity.green_primary.y=0.6000;
+ image->chromaticity.green_primary.z=0.1000;
+ image->chromaticity.blue_primary.x=0.1500;
+ image->chromaticity.blue_primary.y=0.0600;
+ image->chromaticity.blue_primary.z=0.7900;
+ image->chromaticity.white_point.x=0.3127;
+ image->chromaticity.white_point.y=0.3290;
+ image->chromaticity.white_point.z=0.3583;
+ }
+ status=SyncImagePixelCache(image,exception);
+ image->type=type;
+ return(status);
}
\f
/*
assert(image->signature == MagickSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->colorspace == colorspace)
+ return(MagickTrue);
+ if ((image->colorspace == GRAYColorspace) && (image->gamma != 1.0) &&
+ (colorspace == sRGBColorspace))
+ return(MagickTrue);
if (colorspace == UndefinedColorspace)
return(SetImageColorspace(image,colorspace,exception));
- if (image->colorspace == colorspace)
- return(MagickTrue); /* same colorspace: no op */
/*
Convert the reference image from an alternate colorspace to sRGB.
*/
%
*/
+static inline void ConvertCMYToRGB(const double cyan,const double magenta,
+ const double yellow,double *red,double *green,double *blue)
+{
+ *red=QuantumRange*(1.0-cyan);
+ *green=QuantumRange*(1.0-magenta);
+ *blue=QuantumRange*(1.0-yellow);
+}
+
static inline void ConvertLMSToXYZ(const double L,const double M,const double S,
double *X,double *Y,double *Z)
{
- assert(X != (double *) NULL);
- assert(Y != (double *) NULL);
- assert(Z != (double *) NULL);
*X=1.096123820835514*L-0.278869000218287*M+0.182745179382773*S;
*Y=0.454369041975359*L+0.473533154307412*M+0.072097803717229*S;
*Z=(-0.009627608738429)*L-0.005698031216113*M+1.015325639954543*S;
Y,
Z;
- ConvertLuvToXYZ(L,u,v,&X,&Y,&Z);
+ ConvertLuvToXYZ(100.0*L,354.0*u-134.0,262.0*v-140.0,&X,&Y,&Z);
ConvertXYZToRGB(X,Y,Z,red,green,blue);
}
Y,
Z;
- ConvertLabToXYZ(L,a,b,&X,&Y,&Z);
+ ConvertLabToXYZ(100.0*L,255.0*(a-0.5),255.0*(b-0.5),&X,&Y,&Z);
ConvertXYZToRGB(X,Y,Z,red,green,blue);
}
static void ConvertYPbPrToRGB(const double Y,const double Pb,const double Pr,
double *red,double *green,double *blue)
{
- /*
- Convert YPbPr to RGB colorspace.
- */
- assert(red != (double *) NULL);
- assert(green != (double *) NULL);
- assert(blue != (double *) NULL);
*red=QuantumRange*(0.99999999999914679361*Y-1.2188941887145875e-06*(Pb-0.5)+
1.4019995886561440468*(Pr-0.5));
*green=QuantumRange*(0.99999975910502514331*Y-0.34413567816504303521*(Pb-0.5)-
static void ConvertYCbCrToRGB(const double Y,const double Cb,
const double Cr,double *red,double *green,double *blue)
{
- /*
- Convert -YCbCr to RGB colorspace.
- */
- assert(red != (double *) NULL);
- assert(green != (double *) NULL);
- assert(blue != (double *) NULL);
ConvertYPbPrToRGB(Y,Cb,Cr,red,green,blue);
}
static void ConvertYIQToRGB(const double Y,const double I,const double Q,
double *red,double *green,double *blue)
{
- /*
- Convert YIQ to RGB colorspace.
- */
- assert(red != (double *) NULL);
- assert(green != (double *) NULL);
- assert(blue != (double *) NULL);
- *red=Y+0.9562957197589482261*(I-0.5)+0.6210244164652610754*(Q-0.5);
- *green=Y-0.2721220993185104464*(I-0.5)-0.6473805968256950427*(Q-0.5);
- *blue=Y-1.1069890167364901945*(I-0.5)+1.7046149983646481374*(Q-0.5);
+ *red=QuantumRange*(Y+0.9562957197589482261*(I-0.5)+0.6210244164652610754*
+ (Q-0.5));
+ *green=QuantumRange*(Y-0.2721220993185104464*(I-0.5)-0.6473805968256950427*
+ (Q-0.5));
+ *blue=QuantumRange*(Y-1.1069890167364901945*(I-0.5)+1.7046149983646481374*
+ (Q-0.5));
+}
+
+static void ConvertYDbDrToRGB(const double Y,const double Db,const double Dr,
+ double *red,double *green,double *blue)
+{
+ *red=QuantumRange*(Y+9.2303716147657e-05*(Db-0.5)-
+ 0.52591263066186533*(Dr-0.5));
+ *green=QuantumRange*(Y-0.12913289889050927*(Db-0.5)+
+ 0.26789932820759876*(Dr-0.5));
+ *blue=QuantumRange*(Y+0.66467905997895482*(Db-0.5)-
+ 7.9202543533108e-05*(Dr-0.5));
}
static void ConvertYUVToRGB(const double Y,const double U,const double V,
double *red,double *green,double *blue)
{
- /*
- Convert YUV to RGB colorspace.
- */
- assert(red != (double *) NULL);
- assert(green != (double *) NULL);
- assert(blue != (double *) NULL);
- *red=Y-3.945707070708279e-05*(U-0.5)+1.1398279671717170825*(V-0.5);
- *green=Y-0.3946101641414141437*(U-0.5)-0.5805003156565656797*(V-0.5);
- *blue=Y+2.0319996843434342537*(U-0.5)-4.813762626262513e-04*(V-0.5);
+ *red=QuantumRange*(Y-3.945707070708279e-05*(U-0.5)+1.1398279671717170825*
+ (V-0.5));
+ *green=QuantumRange*(Y-0.3946101641414141437*(U-0.5)-0.5805003156565656797*
+ (V-0.5));
+ *blue=QuantumRange*(Y+2.0319996843434342537*(U-0.5)-4.813762626262513e-04*
+ (V-0.5));
}
static MagickBooleanType TransformsRGBImage(Image *image,
progress=0;
switch (image->colorspace)
{
- case CMYColorspace:
- {
- /*
- Transform image from CMY to sRGB.
- */
- if (image->storage_class == PseudoClass)
- {
- if (SyncImage(image,exception) == MagickFalse)
- return(MagickFalse);
- if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
- return(MagickFalse);
- }
- image_view=AcquireAuthenticCacheView(image,exception);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static,4) shared(status) \
- magick_threads(image,image,image->rows,1)
-#endif
- for (y=0; y < (ssize_t) image->rows; y++)
- {
- MagickBooleanType
- sync;
-
- register ssize_t
- x;
-
- register Quantum
- *restrict q;
-
- if (status == MagickFalse)
- continue;
- q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
- exception);
- if (q == (Quantum *) NULL)
- {
- status=MagickFalse;
- continue;
- }
- for (x=0; x < (ssize_t) image->columns; x++)
- {
- double
- cyan,
- magenta,
- yellow;
-
- cyan=(MagickRealType) (QuantumRange-GetPixelCyan(image,q));
- magenta=(MagickRealType) (QuantumRange-GetPixelMagenta(image,q));
- yellow=(MagickRealType) (QuantumRange-GetPixelYellow(image,q));
- SetPixelCyan(image,ClampToQuantum(cyan),q);
- SetPixelMagenta(image,ClampToQuantum(magenta),q);
- SetPixelYellow(image,ClampToQuantum(yellow),q);
- q+=GetPixelChannels(image);
- }
- sync=SyncCacheViewAuthenticPixels(image_view,exception);
- if (sync == MagickFalse)
- status=MagickFalse;
- }
- image_view=DestroyCacheView(image_view);
- if (SetImageColorspace(image,sRGBColorspace,exception) == MagickFalse)
- return(MagickFalse);
- return(status);
- }
case CMYKColorspace:
{
PixelInfo
}
for (x=(ssize_t) image->columns; x != 0; x--)
{
- double
+ MagickRealType
gray;
- gray=EncodePixelGamma((MagickRealType) GetPixelGray(image,q));
+ gray=(MagickRealType) GetPixelGray(image,q);
+ if ((image->intensity == Rec601LuminancePixelIntensityMethod) ||
+ (image->intensity == Rec709LuminancePixelIntensityMethod))
+ gray=EncodePixelGamma(gray);
SetPixelRed(image,ClampToQuantum(gray),q);
SetPixelGreen(image,ClampToQuantum(gray),q);
SetPixelBlue(image,ClampToQuantum(gray),q);
return(MagickFalse);
return(status);
}
+ case CMYColorspace:
case HCLColorspace:
case HCLpColorspace:
case HSBColorspace:
case LuvColorspace:
case XYZColorspace:
case YCbCrColorspace:
+ case YDbDrColorspace:
case YIQColorspace:
case YPbPrColorspace:
case YUVColorspace:
Z=QuantumScale*GetPixelBlue(image,q);
switch (image->colorspace)
{
+ case CMYColorspace:
+ {
+ ConvertCMYToRGB(X,Y,Z,&red,&green,&blue);
+ break;
+ }
case HCLColorspace:
{
ConvertHCLToRGB(X,Y,Z,&red,&green,&blue);
ConvertYCbCrToRGB(X,Y,Z,&red,&green,&blue);
break;
}
+ case YDbDrColorspace:
+ {
+ ConvertYDbDrToRGB(X,Y,Z,&red,&green,&blue);
+ break;
+ }
case YIQColorspace:
{
ConvertYIQToRGB(X,Y,Z,&red,&green,&blue);
break;
}
default:
+ {
+ red=QuantumRange*X;
+ green=QuantumRange*Y;
+ blue=QuantumRange*Z;
break;
+ }
}
SetPixelRed(image,ClampToQuantum(red),q);
SetPixelGreen(image,ClampToQuantum(green),q);
red=(double) logmap[ScaleQuantumToMap(GetPixelRed(image,q))];
green=(double) logmap[ScaleQuantumToMap(GetPixelGreen(image,q))];
blue=(double) logmap[ScaleQuantumToMap(GetPixelBlue(image,q))];
- SetPixelRed(image,ClampToQuantum(red),q);
- SetPixelGreen(image,ClampToQuantum(green),q);
- SetPixelBlue(image,ClampToQuantum(blue),q);
+ SetPixelRed(image,ClampToQuantum(EncodePixelGamma((MagickRealType)
+ red)),q);
+ SetPixelGreen(image,ClampToQuantum(EncodePixelGamma((MagickRealType)
+ green)),q);
+ SetPixelBlue(image,ClampToQuantum(EncodePixelGamma((MagickRealType)
+ blue)),q);
q+=GetPixelChannels(image);
}
sync=SyncCacheViewAuthenticPixels(image_view,exception);
/*
Initialize OHTA tables:
+ I1 = 0.33333*R+0.33334*G+0.33333*B
+ I2 = 0.50000*R+0.00000*G-0.50000*B
+ I3 =-0.25000*R+0.50000*G-0.25000*B
R = I1+1.00000*I2-0.66668*I3
G = I1+0.00000*I2+1.33333*I3
B = I1-1.00000*I2-0.66668*I3