From: cristy Date: Thu, 22 Dec 2011 02:56:19 +0000 (+0000) Subject: (no commit message) X-Git-Tag: 7.0.1-0~6513 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e4a404711bf20d9062d11ab28bc12e5acd307f9c;p=imagemagick --- diff --git a/Magick++/lib/Magick++/Include.h b/Magick++/lib/Magick++/Include.h index 90af63604..9ee18f256 100644 --- a/Magick++/lib/Magick++/Include.h +++ b/Magick++/lib/Magick++/Include.h @@ -191,7 +191,7 @@ namespace Magick using MagickCore::CopyCyanCompositeOp; using MagickCore::CopyGreenCompositeOp; using MagickCore::CopyMagentaCompositeOp; - using MagickCore::CopyOpacityCompositeOp; + using MagickCore::CopyAlphaCompositeOp; using MagickCore::CopyRedCompositeOp; using MagickCore::CopyYellowCompositeOp; using MagickCore::DarkenCompositeOp; diff --git a/MagickCore/composite.c b/MagickCore/composite.c index abc561ce8..f064f1ab3 100644 --- a/MagickCore/composite.c +++ b/MagickCore/composite.c @@ -135,482 +135,13 @@ % */ -static inline double MagickMin(const double x,const double y) -{ - if (x < y) - return(x); - return(y); -} -static inline double MagickMax(const double x,const double y) -{ - if (x > y) - return(x); - return(y); -} - -/* - Programmers notes on SVG specification. - - A Composition is defined by... - Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors - Blending areas : X = 1 for area of overlap ie: f(Sc,Dc) - Y = 1 for source preserved - Z = 1 for destination preserved - - Conversion to transparency (then optimized) - Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa) - Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa) - - Where... - Sca = Sc*Sa normalized Source color divided by Source alpha - Dca = Dc*Da normalized Dest color divided by Dest alpha - Dc' = Dca'/Da' the desired color value for this channel. - - Da' (alpha result) is stored as 'gamma' in the functions. - - The compose functions defined is just simplifications of the above - formula on a case by case bases. - - - - The above SVG definitions also defines that Mathematical Composition - methods should use a 'Over' blending mode for Alpha Channel. - It however was not applied for composition modes of 'Plus', 'Minus', - the modulus versions of 'Add' and 'Subtract'. - - Mathematical operator changes to be applied from IM v6.7... - - 1/ Modulus modes 'Add' and 'Subtract' are obsoleted and renamed - 'ModulusAdd' and 'ModulusSubtract' for clarity. - - 2/ All mathematical compositions work as per the SVG specification - with regard to blending. This now includes 'ModulusAdd' and - 'ModulusSubtract'. - - 3/ When the special channel flag 'sync' (syncronize channel updates) - is turned off (enabled by default) then mathematical compositions are - only performed on the channels specified, and are applied - independantally of each other. In other words the mathematics is - performed as 'pure' mathematical operations, rather than as image - operations. -*/ - -static inline MagickRealType Atop(const MagickRealType p, - const MagickRealType Sa,const MagickRealType q, - const MagickRealType magick_unused(Da)) -{ - return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */ -} - -static inline void CompositeAtop(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ - MagickRealType - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - composite->alpha=q->alpha; /* optimized Da = 1.0-Gamma */ - composite->red=Atop(p->red,Sa,q->red,1.0); - composite->green=Atop(p->green,Sa,q->green,1.0); - composite->blue=Atop(p->blue,Sa,q->blue,1.0); - if (q->colorspace == CMYKColorspace) - composite->black=Atop(p->black,Sa,q->black,1.0); -} - -/* - Bumpmap: Multiply by overlay intensity - What is this Composition actually method for? Can't find any specification! - - I think this was meant to be a 'HardLight effect' using a Shaded Image! - That is a Embossing, using a height map! Better to do it piecemeal. -*/ -static inline void CompositeBumpmap(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ - MagickRealType - intensity; - - intensity=(MagickRealType) GetPixelInfoIntensity(p); - composite->red=QuantumScale*intensity*q->red; - composite->green=QuantumScale*intensity*q->green; - composite->blue=QuantumScale*intensity*q->blue; - composite->alpha=(MagickRealType) QuantumScale*intensity*p->alpha; - if (q->colorspace == CMYKColorspace) - composite->black=QuantumScale*intensity*q->black; -} - -static inline void CompositeClear(const PixelInfo *q,PixelInfo *composite) -{ - composite->alpha=(MagickRealType) TransparentAlpha; - composite->red=0.0; - composite->green=0.0; - composite->blue=0.0; - composite->black=1.0; -} - -static MagickRealType ColorBurn(const MagickRealType Sca, - const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da) -{ - if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon)) - return(Sa*Da+Dca*(1.0-Sa)); - if (Sca < MagickEpsilon) - return(Dca*(1.0-Sa)); - return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa)); -} - -static inline void CompositeColorBurn(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*ColorBurn(QuantumScale*p->black*Sa,Sa,QuantumScale* - q->black*Da,Da); -} - - -static MagickRealType ColorDodge(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da) -{ - /* - Working from first principles using the original formula: - - f(Sc,Dc) = Dc/(1-Sc) - - This works correctly! Looks like the 2004 SVG model was right but just - required a extra condition for correct handling. - */ - if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon)) - return(Sca*(1.0-Da)+Dca*(1.0-Sa)); - if (fabs(Sca-Sa) < MagickEpsilon) - return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); - return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa)); -} - -static inline void CompositeColorDodge(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*ColorDodge(QuantumScale*p->black*Sa,Sa,QuantumScale* - q->black*Da,Da); -} - -static inline MagickRealType Darken(const MagickRealType p, - const MagickRealType alpha,const MagickRealType q,const MagickRealType beta) -{ - if (p < q) - return(MagickOver_(p,alpha,q,beta)); /* src-over */ - return(MagickOver_(q,beta,p,alpha)); /* dst-over */ -} - -static inline void CompositeDarken(const Image *image,const PixelInfo *p, - const PixelInfo *q,PixelInfo *composite) -{ - MagickRealType - gamma; - - /* - Darken is equivalent to a 'Minimum' method OR a greyscale version of a - binary 'Or' OR the 'Intersection' of pixel sets. - */ - if (image->channel_mask != DefaultChannels) - { - /* - Handle channels as separate grayscale channels. - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=MagickMin(p->red,q->red); - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=MagickMin(p->green,q->green); - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=MagickMin(p->blue,q->blue); - if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 && - (q->colorspace == CMYKColorspace)) - composite->black=MagickMin(p->black,q->black); - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=MagickMax(p->alpha,q->alpha); - return; - } - composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */ - gamma=1.0-QuantumScale*composite->alpha; - gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Darken(p->red,p->alpha,q->red,q->alpha); - composite->green=gamma*Darken(p->green,p->alpha,q->green,q->alpha); - composite->blue=gamma*Darken(p->blue,p->alpha,q->blue,q->alpha); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Darken(p->black,p->alpha,q->black,q->alpha); -} - -static inline void CompositeDarkenIntensity(const Image *image, - const PixelInfo *p,const PixelInfo *q,PixelInfo *composite) -{ - MagickRealType - Da, - Sa; - - /* - Select the pixel based on the intensity level. - If 'Sync' flag select whole pixel based on alpha weighted intensity. - Otherwise use intensity only, but restrict copy according to channel. - */ - if (image->channel_mask != DefaultChannels) - { - MagickBooleanType - from_p; - - from_p=GetPixelInfoIntensity(p) < GetPixelInfoIntensity(q) ? MagickTrue : - MagickFalse; - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=from_p != MagickFalse ? p->red : q->red; - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=from_p != MagickFalse ? p->green : q->green; - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=from_p != MagickFalse ? p->blue : q->blue; - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=from_p != MagickFalse ? p->black : q->black; - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha; - return; - } - Sa=QuantumScale*p->alpha; - Da=QuantumScale*q->alpha; - *composite=(Sa*GetPixelInfoIntensity(p) < Da*GetPixelInfoIntensity(q)) ? - *p : *q; -} - -static inline MagickRealType Difference(const MagickRealType p, - const MagickRealType Sa,const MagickRealType q,const MagickRealType Da) -{ - /* - Optimized by Multipling by QuantumRange (taken from gamma). - */ - return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q)); -} - -static inline void CompositeDifference(const Image *image,const PixelInfo *p, - const PixelInfo *q,PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - if (image->channel_mask != DefaultChannels) - { - /* - Handle channels as separate grayscale channels. - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=fabs((double) (p->red-q->red)); - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=fabs((double) (p->green-q->green)); - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=fabs((double) (p->blue-q->blue)); - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=fabs((double) (p->black-q->black)); - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=fabs((double) (p->alpha-q->alpha)); - return; - } - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Difference(p->red,Sa,q->red,Da); - composite->green=gamma*Difference(p->green,Sa,q->green,Da); - composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Difference(p->black,Sa,q->black,Da); -} - -static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa, - const MagickRealType Dca,const MagickRealType Da) -{ - /* - Divide Source by Destination - - f(Sc,Dc) = Sc / Dc - - But with appropriate handling for special case of Dc == 0 specifically - so that f(Black,Black)=Black and f(non-Black,Black)=White. - It is however also important to correctly do 'over' alpha blending which - is why the formula becomes so complex. - */ - if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon)) - return(Sca*(1.0-Da)+Dca*(1.0-Sa)); - if (fabs(Dca) < MagickEpsilon) - return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); - return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); -} - -static inline void CompositeDivide(const Image *image,const PixelInfo *p, - const PixelInfo *q,PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - if (image->channel_mask != DefaultChannels) - { - /* - Handle channels as separate grayscale channels. - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=QuantumRange*Divide(QuantumScale*p->red,1.0, - QuantumScale*q->red,1.0); - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=QuantumRange*Divide(QuantumScale*p->green,1.0, - QuantumScale*q->green,1.0); - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=QuantumRange*Divide(QuantumScale*p->blue,1.0, - QuantumScale*q->blue,1.0); - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=QuantumRange*Divide(QuantumScale*p->black,1.0, - QuantumScale*q->black,1.0); - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=QuantumRange*(1.0-Divide(Sa,1.0,Da,1.0)); - return; - } - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Divide(QuantumScale*p->black*Sa,Sa,QuantumScale* - q->black*Da,Da); -} - -static MagickRealType Exclusion(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da) -{ - return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); -} - -static inline void CompositeExclusion(const Image *image,const PixelInfo *p, - const PixelInfo *q,PixelInfo *composite) -{ - MagickRealType - gamma, - Sa, - Da; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - if (image->channel_mask != DefaultChannels) - { - /* - Handle channels as separate grayscale channels. - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=QuantumRange*Exclusion(QuantumScale*p->red,1.0, - QuantumScale*q->red,1.0); - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=QuantumRange*Exclusion(QuantumScale*p->green,1.0, - QuantumScale*q->green,1.0); - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=QuantumRange*Exclusion(QuantumScale*p->blue,1.0, - QuantumScale*q->blue,1.0); - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=QuantumRange*Exclusion(QuantumScale*p->black,1.0, - QuantumScale*q->black,1.0); - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=QuantumRange*(1.0-Exclusion(Sa,1.0,Da,1.0)); - return; - } - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Exclusion(QuantumScale*p->black*Sa,Sa, - QuantumScale*q->black*Da,Da); -} - -static MagickRealType HardLight(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da) +static void CompositeHSB(const Quantum red,const Quantum green, + const Quantum blue,double *hue,double *saturation,double *brightness) { - if ((2.0*Sca) < Sa) - return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); - return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa)); -} - -static inline void CompositeHardLight(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*HardLight(QuantumScale*p->black*Sa,Sa,QuantumScale* - q->black*Da,Da); -} + double + delta; -static void CompositeHSB(const MagickRealType red,const MagickRealType green, - const MagickRealType blue,double *hue,double *saturation,double *brightness) -{ - MagickRealType - delta, + Quantum max, min; @@ -629,996 +160,445 @@ static void CompositeHSB(const MagickRealType red,const MagickRealType green, *hue=0.0; *saturation=0.0; *brightness=(double) (QuantumScale*max); - if (fabs(max) < MagickEpsilon) + if (fabs((double) max) < MagickEpsilon) return; *saturation=(double) (1.0-min/max); - delta=max-min; + delta=(MagickRealType) max-min; if (fabs(delta) < MagickEpsilon) return; - if (fabs(red-max) < MagickEpsilon) + if (fabs((double) red-max) < MagickEpsilon) *hue=(double) ((green-blue)/delta); else - if (fabs(green-max) < MagickEpsilon) + if (fabs((double) green-max) < MagickEpsilon) *hue=(double) (2.0+(blue-red)/delta); else - if (fabs(blue-max) < MagickEpsilon) + if (fabs((double) blue-max) < MagickEpsilon) *hue=(double) (4.0+(red-green)/delta); *hue/=6.0; if (*hue < 0.0) *hue+=1.0; } -#if 0 -static inline MagickRealType In(const MagickRealType p,const MagickRealType Sa, - const MagickRealType magick_unused(q),const MagickRealType Da) -{ - return(Sa*p*Da); -} -#endif - -static inline void CompositeIn(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ -#if 0 - MagickRealType - gamma, - Sa, - Da; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=Sa*Da; - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - /* really this just preserves the src or p color as is! */ - composite->red=gamma*In(p->red,Sa,q->red,Da); - composite->green=gamma*In(p->green,Sa,q->green,Da); - composite->blue=gamma*In(p->blue,Sa,q->blue,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*In(p->black,Sa,q->black,Da); -#else - /* Simplified to a multiply of the Alpha Channel */ - *composite=*p; /* structure copy */ - composite->alpha=QuantumScale*p->alpha*q->alpha; -#endif -} - -static inline MagickRealType Lighten(const MagickRealType p, - const MagickRealType alpha,const MagickRealType q,const MagickRealType beta) -{ - if (p > q) - return(MagickOver_(p,alpha,q,beta)); /* src-over */ - return(MagickOver_(q,beta,p,alpha)); /* dst-over */ -} - -static inline void CompositeLighten(const Image *image,const PixelInfo *p, - const PixelInfo *q,PixelInfo *composite) +static void HSBComposite(const double hue,const double saturation, + const double brightness,double *red,double *green,double *blue) { - MagickRealType - gamma; + double + f, + h, + p, + q, + t; /* - Lighten is also equvalent to a 'Maximum' method OR a greyscale version of a - binary 'And' OR the 'Union' of pixel sets. + Convert HSB to RGB colorspace. */ - if (image->channel_mask != DefaultChannels) + assert(red != (double *) NULL); + assert(green != (double *) NULL); + assert(blue != (double *) NULL); + if (saturation == 0.0) { - /* - Handle channels as separate grayscale channels - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=MagickMax(p->red,q->red); - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=MagickMax(p->green,q->green); - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=MagickMax(p->blue,q->blue); - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=MagickMax(p->black,q->black); - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=MagickMin(p->alpha,q->alpha); + *red=(double) QuantumRange*brightness; + *green=(*red); + *blue=(*red); return; } - composite->alpha=QuantumScale*p->alpha*q->alpha; /* Over Blend */ - gamma=1.0-QuantumScale*composite->alpha; - gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Lighten(p->red,p->alpha,q->red,q->alpha); - composite->green=gamma*Lighten(p->green,p->alpha,q->green,q->alpha); - composite->blue=gamma*Lighten(p->blue,p->alpha,q->blue,q->alpha); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Lighten(p->black,p->alpha,q->black,q->alpha); -} - -static inline void CompositeLightenIntensity(const Image *image, - const PixelInfo *p,const PixelInfo *q,PixelInfo *composite) -{ - MagickRealType - Da, - Sa; - - /* - Select the pixel based on the intensity level. - If 'Sync' flag select whole pixel based on alpha weighted intensity. - Otherwise use Intenisty only, but restrict copy according to channel. - */ - if (image->channel_mask != DefaultChannels) + h=6.0*(hue-floor(hue)); + f=h-floor((double) h); + p=brightness*(1.0-saturation); + q=brightness*(1.0-saturation*f); + t=brightness*(1.0-saturation*(1.0-f)); + switch ((int) h) + { + case 0: + default: { - MagickBooleanType - from_p; - - from_p=GetPixelInfoIntensity(p) > GetPixelInfoIntensity(q) ? MagickTrue : - MagickFalse; - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=from_p != MagickFalse ? p->red : q->red; - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=from_p != MagickFalse ? p->green : q->green; - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=from_p != MagickFalse ? p->blue : q->blue; - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=from_p != MagickFalse ? p->black : q->black; - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=from_p != MagickFalse ? p->alpha : q->alpha; - return; + *red=(double) QuantumRange*brightness; + *green=(double) QuantumRange*t; + *blue=(double) QuantumRange*p; + break; } - Sa=QuantumScale*p->alpha; - Da=QuantumScale*q->alpha; - *composite=(Sa*GetPixelInfoIntensity(p) > Da*GetPixelInfoIntensity(q)) ? - *p : *q; + case 1: + { + *red=(double) QuantumRange*q; + *green=(double) QuantumRange*brightness; + *blue=(double) QuantumRange*p; + break; + } + case 2: + { + *red=(double) QuantumRange*p; + *green=(double) QuantumRange*brightness; + *blue=(double) QuantumRange*t; + break; + } + case 3: + { + *red=(double) QuantumRange*p; + *green=(double) QuantumRange*q; + *blue=(double) QuantumRange*brightness; + break; + } + case 4: + { + *red=(double) QuantumRange*t; + *green=(double) QuantumRange*p; + *blue=(double) QuantumRange*brightness; + break; + } + case 5: + { + *red=(double) QuantumRange*brightness; + *green=(double) QuantumRange*p; + *blue=(double) QuantumRange*q; + break; + } + } } -static inline void CompositeLinearDodge(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) +static inline double MagickMin(const double x,const double y) { - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*(p->red*Sa+q->red*Da); - composite->green=gamma*(p->green*Sa+q->green*Da); - composite->blue=gamma*(p->blue*Sa+q->blue*Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*(p->black*Sa+q->black*Da); + if (x < y) + return(x); + return(y); } - - -static inline MagickRealType LinearBurn(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da) +static inline double MagickMax(const double x,const double y) { - /* - LinearBurn: as defined by Abode Photoshop, according to - http://www.simplefilter.de/en/basics/mixmods.html is: - - f(Sc,Dc) = Sc + Dc - 1 - */ - return(Sca+Dca-Sa*Da); + if (x > y) + return(x); + return(y); } -static inline void CompositeLinearBurn(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) +static MagickBooleanType CompositeOverImage(Image *image, + const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset, + ExceptionInfo *exception) { - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*LinearBurn(QuantumScale*p->black*Sa,Sa,QuantumScale* - q->black*Da,Da); -} +#define CompositeImageTag "Composite/Image" -static inline MagickRealType LinearLight(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da) -{ - /* - LinearLight: as defined by Abode Photoshop, according to - http://www.simplefilter.de/en/basics/mixmods.html is: + CacheView + *composite_view, + *image_view; - f(Sc,Dc) = Dc + 2*Sc - 1 - */ - return((Sca-Sa)*Da+Sca+Dca); -} + const char + *value; -static inline void CompositeLinearLight(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*LinearLight(QuantumScale*p->black*Sa,Sa,QuantumScale* - q->black*Da,Da); -} - -static inline MagickRealType Mathematics(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da, - const GeometryInfo *geometry_info) -{ - MagickRealType - gamma; - - /* - 'Mathematics' a free form user control mathematical composition is defined - as... - - f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D - - Where the arguments A,B,C,D are (currently) passed to composite as - a command separated 'geometry' string in "compose:args" image artifact. - - A = a->rho, B = a->sigma, C = a->xi, D = a->psi - - Applying the SVG transparency formula (see above), we get... - - Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa) + MagickBooleanType + modify_outside_overlay, + status; - Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) + - Dca*(1.0-Sa) - */ - gamma=geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+ - geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+ - Dca*(1.0-Sa); - return(gamma); -} + MagickOffsetType + progress; -static inline void CompositeMathematics(const Image *image,const PixelInfo *p, - const PixelInfo *q,const GeometryInfo *args,PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; + ssize_t + y; - Sa=QuantumScale*p->alpha; /* ??? - AT */ - Da=QuantumScale*q->alpha; - if (image->channel_mask != DefaultChannels) - { - /* - Handle channels as separate grayscale channels. - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=QuantumRange*Mathematics(QuantumScale*p->red,1.0, - QuantumScale*q->red,1.0,args); - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=QuantumRange*Mathematics(QuantumScale*p->green,1.0, - QuantumScale*q->green,1.0,args); - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=QuantumRange*Mathematics(QuantumScale*p->blue,1.0, - QuantumScale*q->blue,1.0,args); - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=QuantumRange*Mathematics(QuantumScale*p->black,1.0, - QuantumScale*q->black,1.0,args); - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args)); - return; - } - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da,args); - composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa, - QuantumScale*q->green*Da,Da,args); - composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da,args); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Mathematics(QuantumScale*p->black*Sa,Sa, - QuantumScale*q->black*Da,Da,args); -} + size_t + channels; -static inline void CompositePlus(const Image *image,const PixelInfo *p, - const PixelInfo *q,PixelInfo *composite) -{ /* - NOTE: "Plus" does not use 'over' alpha-blending but uses a special - 'plus' form of alph-blending. It is the ONLY mathematical operator to - do this. this is what makes it different to the otherwise equivalent - "LinearDodge" composition method. - - Note however that color channels are still effected by the alpha channel - as a result of the blending, making it just as useless for independant - channel maths, just like all other mathematical composition methods. - - As such the removal of the 'sync' flag, is still a usful convention. - - The CompositePixelInfoPlus() function is defined in - "composite-private.h" so it can also be used for Image Blending. + Prepare composite image. */ - if (image->channel_mask != DefaultChannels) - { - /* - Handle channels as separate grayscale channels. - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=p->red+q->red; - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=p->green+q->green; - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=p->blue+q->blue; - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=p->black+q->black; - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=p->alpha+q->alpha-QuantumRange; - return; - } - CompositePixelInfoPlus(p,p->alpha,q,q->alpha,composite); -} - -static inline MagickRealType Minus(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca, - const MagickRealType magick_unused(Da)) -{ + modify_outside_overlay=MagickFalse; + value=GetImageArtifact(composite_image,"compose:outside-overlay"); + if (value != (const char *) NULL) + modify_outside_overlay=IsMagickTrue(value); /* - Minus Source from Destination - - f(Sc,Dc) = Sc - Dc + Composite image. */ - return(Sca+Dca-2.0*Dca*Sa); -} - -static inline void CompositeMinus(const Image *image,const PixelInfo *p, - const PixelInfo *q,PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - if (image->channel_mask != DefaultChannels) - { - /* - Handle channels as separate grayscale channels. - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=p->red-q->red; - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=p->green-q->green; - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=p->blue-q->blue; - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=p->black-q->black; - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=QuantumRange*(1.0-(Sa-Da)); - return; - } - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Minus(p->red*Sa,Sa,q->red*Da,Da); - composite->green=gamma*Minus(p->green*Sa,Sa,q->green*Da,Da); - composite->blue=gamma*Minus(p->blue*Sa,Sa,q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Minus(p->black*Sa,Sa,q->black*Da,Da); -} + status=MagickTrue; + progress=0; + image_view=AcquireCacheView(image); + composite_view=AcquireCacheView(composite_image); +#if defined(MAGICKCORE_OPENMP_SUPPORT) + #pragma omp parallel for schedule(dynamic,4) shared(progress,status) +#endif + for (y=0; y < (ssize_t) image->rows; y++) + { + const Quantum + *pixels; -static inline MagickRealType ModulusAdd(const MagickRealType p, - const MagickRealType Sa,const MagickRealType q, const MagickRealType Da) -{ - MagickRealType - pixel; + register const Quantum + *restrict p; - pixel=p+q; - if (pixel > QuantumRange) - pixel-=(QuantumRange+1.0); - return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa)); -} + register Quantum + *restrict q; -static inline void CompositeModulusAdd(const Image *image,const PixelInfo *p, - const PixelInfo *q,PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; + register ssize_t + x; - if (image->channel_mask != DefaultChannels) + if (status == MagickFalse) + continue; + if (modify_outside_overlay == MagickFalse) + { + if (y < y_offset) + continue; + if ((y-y_offset) >= (ssize_t) composite_image->rows) + continue; + } + /* + If pixels is NULL, y is outside overlay region. + */ + pixels=(Quantum *) NULL; + p=(Quantum *) NULL; + if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows)) + { + p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset, + composite_image->columns,1,exception); + if (p == (const Quantum *) NULL) + { + status=MagickFalse; + continue; + } + pixels=p; + if (x_offset < 0) + p-=x_offset*GetPixelChannels(composite_image); + } + 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++) { - /* - Handle channels as separate grayscale channels. - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=ModulusAdd(p->red,1.0,q->red,1.0); - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=ModulusAdd(p->green,1.0,q->green,1.0); - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0); - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=ModulusAdd(p->black,1.0,q->black,1.0); - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=ModulusAdd(p->alpha,1.0,q->alpha,1.0); - return; - } - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=ModulusAdd(p->red,Sa,q->red,Da); - composite->green=ModulusAdd(p->green,Sa,q->green,Da); - composite->blue=ModulusAdd(p->blue,Sa,q->blue,Da); - if (q->colorspace == CMYKColorspace) - composite->black=ModulusAdd(p->black,Sa,q->black,Da); -} - -static inline MagickRealType ModulusSubtract(const MagickRealType p, - const MagickRealType Sa,const MagickRealType q, const MagickRealType Da) -{ - MagickRealType - pixel; + MagickRealType + alpha, + Da, + Dc, + gamma, + Sa, + Sc; - pixel=p-q; - if (pixel < 0.0) - pixel+=(QuantumRange+1.0); - return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa)); -} + register ssize_t + i; -static inline void CompositeModulusSubtract(const Image *image, - const PixelInfo *p,const PixelInfo *q,PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; + if (modify_outside_overlay == MagickFalse) + { + if (x < x_offset) + { + q+=GetPixelChannels(image); + continue; + } + if ((x-x_offset) >= (ssize_t) composite_image->columns) + break; + } + if ((pixels == (Quantum *) NULL) || (x < x_offset) || + ((x-x_offset) >= (ssize_t) composite_image->columns)) + { + Quantum + source[MaxPixelChannels]; - if (image->channel_mask != DefaultChannels) - { + /* + Virtual composite: + Sc: source color. + Dc: destination color. + */ + (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset, + source,exception); + for (i=0; i < (ssize_t) GetPixelChannels(image); i++) + { + PixelChannel + channel; + + PixelTrait + composite_traits, + traits; + + channel=GetPixelChannelMapChannel(image,i); + traits=GetPixelChannelMapTraits(image,channel); + composite_traits=GetPixelChannelMapTraits(composite_image, + channel); + if ((traits == UndefinedPixelTrait) || + (composite_traits == UndefinedPixelTrait)) + continue; + q[i]=source[channel]; + } + q+=GetPixelChannels(image); + continue; + } /* - Handle channels as separate grayscale channels, + Authentic composite: + Sa: normalized source alpha. + Da: normalized destination alpha. */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=ModulusSubtract(p->red,1.0,q->red,1.0); - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=ModulusSubtract(p->green,1.0,q->green,1.0); - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0); - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=ModulusSubtract(p->black,1.0,q->black,1.0); - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=ModulusSubtract(p->alpha,1.0,q->alpha,1.0); - return; + Sa=QuantumScale*GetPixelAlpha(composite_image,p); + Da=QuantumScale*GetPixelAlpha(image,q); + alpha=Sa*(-Da)+Sa+Da; + for (i=0; i < (ssize_t) GetPixelChannels(image); i++) + { + PixelChannel + channel; + + PixelTrait + composite_traits, + traits; + + channel=GetPixelChannelMapChannel(image,i); + traits=GetPixelChannelMapTraits(image,channel); + composite_traits=GetPixelChannelMapTraits(composite_image,channel); + if ((traits == UndefinedPixelTrait) || + (composite_traits == UndefinedPixelTrait)) + continue; + if ((traits & CopyPixelTrait) != 0) + { + if (channel != AlphaPixelChannel) + { + /* + Copy channel. + */ + q[i]=GetPixelChannel(composite_image,channel,p); + continue; + } + /* + Set alpha channel. + */ + q[i]=ClampToQuantum(QuantumRange*alpha); + continue; + } + /* + Sc: source color. + Dc: destination color. + */ + Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p); + Dc=(MagickRealType) q[i]; + gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha); + q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc)); + } + p+=GetPixelChannels(composite_image); + channels=GetPixelChannels(composite_image); + if (p >= (pixels+channels*composite_image->columns)) + p=pixels; + q+=GetPixelChannels(image); } - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma = RoundToUnity(Sa+Da-Sa*Da); - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=ModulusSubtract(p->red,Sa,q->red,Da); - composite->green=ModulusSubtract(p->green,Sa,q->green,Da); - composite->blue=ModulusSubtract(p->blue,Sa,q->blue,Da); - if (q->colorspace == CMYKColorspace) - composite->black=ModulusSubtract(p->black,Sa,q->black,Da); -} + if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) + status=MagickFalse; + if (image->progress_monitor != (MagickProgressMonitor) NULL) + { + MagickBooleanType + proceed; -static inline MagickRealType Multiply(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da) -{ - return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); +#if defined(MAGICKCORE_OPENMP_SUPPORT) + #pragma omp critical (MagickCore_CompositeImage) +#endif + proceed=SetImageProgress(image,CompositeImageTag,progress++, + image->rows); + if (proceed == MagickFalse) + status=MagickFalse; + } + } + composite_view=DestroyCacheView(composite_view); + image_view=DestroyCacheView(image_view); + return(status); } -static inline void CompositeMultiply(const Image *image,const PixelInfo *p, - const PixelInfo *q,PixelInfo *composite) +MagickExport MagickBooleanType CompositeImage(Image *image, + const CompositeOperator compose,const Image *composite_image, + const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception) { - MagickRealType - Da, - gamma, - Sa; +#define CompositeImageTag "Composite/Image" - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - if (image->channel_mask != DefaultChannels) - { - /* - Handle channels as separate grayscale channels. - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=QuantumScale*p->red*q->red; - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=QuantumScale*p->green*q->green; - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=QuantumScale*p->blue*q->blue; - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=QuantumScale*p->black*q->black; - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=QuantumRange*(1.0-Sa*Da); - return; - } - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Multiply(QuantumScale*p->black*Sa,Sa, - QuantumScale*q->black*Da,Da); -} + CacheView + *composite_view, + *image_view; -#if 0 -static inline MagickRealType Out(const MagickRealType p,const MagickRealType Sa, - const MagickRealType magick_unused(q),const MagickRealType Da) -{ - return(Sa*p*(1.0-Da)); -} -#endif + const char + *value; -static inline void CompositeOut(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ -#if 0 - MagickRealType - Sa, - Da, - gamma; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=Sa*(1.0-Da); - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Out(p->red,Sa,q->red,Da); - composite->green=gamma*Out(p->green,Sa,q->green,Da); - composite->blue=gamma*Out(p->blue,Sa,q->blue,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Out(p->black,Sa,q->black,Da); -#else - /* Simplified to a negated multiply of the Alpha Channel */ - *composite=*p; /* structure copy */ - composite->alpha=p->alpha*(1.0-QuantumScale*q->alpha); -#endif -} + double + sans; -static MagickRealType PegtopLight(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da) -{ - /* - PegTop: A Soft-Light alternative: A continuous version of the Softlight - function, producing very similar results. + GeometryInfo + geometry_info; - f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc + Image + *destination_image; - See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm. - */ - if (fabs(Da) < MagickEpsilon) - return(Sca); - return(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+Dca*(1.0-Sa)); -} + MagickBooleanType + modify_outside_overlay, + status; + + MagickOffsetType + progress; -static inline void CompositePegtopLight(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*PegtopLight(QuantumScale*p->black*Sa,Sa,QuantumScale* - q->black*Da,Da); -} + amount, + destination_dissolve, + midpoint, + percent_brightness, + percent_saturation, + source_dissolve, + threshold; -static MagickRealType PinLight(const MagickRealType Sca,const MagickRealType Sa, - const MagickRealType Dca,const MagickRealType Da) -{ - /* - PinLight: A Photoshop 7 composition method - http://www.simplefilter.de/en/basics/mixmods.html + MagickStatusType + flags; - f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc - */ - if (Dca*Sa < Da*(2.0*Sca-Sa)) - return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa)); - if ((Dca*Sa) > (2.0*Sca*Da)) - return(Sca*Da+Sca+Dca*(1.0-Sa)); - return(Sca*(1.0-Da)+Dca); -} + ssize_t + y; -static inline void CompositePinLight(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*PinLight(QuantumScale*p->black*Sa,Sa,QuantumScale* - q->black*Da,Da); -} + size_t + channels; -static inline MagickRealType Screen(const MagickRealType Sca, - const MagickRealType Dca) -{ /* - Screen: A negated multiply - f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc) + Composition based on the SVG specification: + + A Composition is defined by... + Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors + Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc) + Y = 1 for source preserved + Z = 1 for destination preserved + + Conversion to transparency (then optimized) + Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa) + Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa) + + Where... + Sca = Sc*Sa normalized Source color divided by Source alpha + Dca = Dc*Da normalized Dest color divided by Dest alpha + Dc' = Dca'/Da' the desired color value for this channel. + + Da' in in the follow formula as 'gamma' The resulting alpla value. + + Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in + the following optimizations... + gamma = Sa+Da-Sa*Da; + gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta; + opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma + + The above SVG definitions also definate that Mathematical Composition + methods should use a 'Over' blending mode for Alpha Channel. + It however was not applied for composition modes of 'Plus', 'Minus', + the modulus versions of 'Add' and 'Subtract'. + + Mathematical operator changes to be applied from IM v6.7... + + 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed + 'ModulusAdd' and 'ModulusSubtract' for clarity. + + 2) All mathematical compositions work as per the SVG specification + with regard to blending. This now includes 'ModulusAdd' and + 'ModulusSubtract'. + + 3) When the special channel flag 'sync' (syncronize channel updates) + is turned off (enabled by default) then mathematical compositions are + only performed on the channels specified, and are applied + independantally of each other. In other words the mathematics is + performed as 'pure' mathematical operations, rather than as image + operations. */ - return(Sca+Dca-Sca*Dca); -} - -static inline void CompositeScreen(const Image *image,const PixelInfo *p, - const PixelInfo *q,PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - if (image->channel_mask != DefaultChannels) + assert(image != (Image *) NULL); + assert(image->signature == MagickSignature); + if (image->debug != MagickFalse) + (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); + assert(composite_image != (Image *) NULL); + assert(composite_image->signature == MagickSignature); + if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) + return(MagickFalse); + if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp)) { - /* - Handle channels as separate grayscale channels. - */ - if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) - composite->red=QuantumRange*Screen(QuantumScale*p->red, - QuantumScale*q->red); - if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) - composite->green=QuantumRange*Screen(QuantumScale*p->green, - QuantumScale*q->green); - if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) - composite->blue=QuantumRange*Screen(QuantumScale*p->blue, - QuantumScale*q->blue); - if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && - (q->colorspace == CMYKColorspace)) - composite->black=QuantumRange*Screen(QuantumScale*p->black, - QuantumScale*q->black); - if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) - composite->alpha=QuantumRange*(1.0-Screen(Sa,Da)); - return; - } - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - Sa*=QuantumScale; Da*=QuantumScale; /* optimization */ - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Screen(p->red*Sa,q->red*Da); - composite->green=gamma*Screen(p->green*Sa,q->green*Da); - composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Screen(p->black*Sa,q->black*Da); -} - -static MagickRealType SoftLight(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da) -{ - MagickRealType - alpha, - beta; - - /* - New specification: March 2009 SVG specification. - */ - alpha=Dca/Da; - if ((2.0*Sca) < Sa) - return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa)); - if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da)) - { - beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0* - alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa); - return(beta); - } - beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa); - return(beta); -} - -static inline void CompositeSoftLight(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*SoftLight(QuantumScale*p->black*Sa,Sa,QuantumScale* - q->black*Da,Da); -} - -static inline MagickRealType Threshold(const MagickRealType p, - const MagickRealType q,const MagickRealType threshold, - const MagickRealType amount) -{ - MagickRealType - delta; - - /* - Multiply difference by amount, if differance larger than threshold??? - What use this is is completely unknown. The Opacity calculation appears to - be inverted -- Anthony Thyssen - - Deprecated. - */ - delta=p-q; - if ((MagickRealType) fabs((double) (2.0*delta)) < threshold) - return(q); - return(q+delta*amount); -} - -static inline void CompositeThreshold(const PixelInfo *p,const PixelInfo *q, - const MagickRealType threshold,const MagickRealType amount, - PixelInfo *composite) -{ - composite->red=Threshold(p->red,q->red,threshold,amount); - composite->green=Threshold(p->green,q->green,threshold,amount); - composite->blue=Threshold(p->blue,q->blue,threshold,amount); - composite->alpha=Threshold(p->alpha,q->alpha,threshold,amount); - if (q->colorspace == CMYKColorspace) - composite->black=Threshold(p->black,q->black,threshold,amount); -} - - -static MagickRealType VividLight(const MagickRealType Sca, - const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da) -{ - /* - VividLight: A Photoshop 7 composition method. See - http://www.simplefilter.de/en/basics/mixmods.html. - - f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc)) - */ - if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon)) - return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); - if ((2.0*Sca) <= Sa) - return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa)); - return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa)); -} - -static inline void CompositeVividLight(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale* - q->red*Da,Da); - composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale* - q->green*Da,Da); - composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale* - q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*VividLight(QuantumScale*p->black*Sa,Sa,QuantumScale* - q->black*Da,Da); -} - -static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa, - const MagickRealType Dca,const MagickRealType Da) -{ - return(Sca*(1.0-Da)+Dca*(1.0-Sa)); -} - -static inline void CompositeXor(const PixelInfo *p,const PixelInfo *q, - PixelInfo *composite) -{ - MagickRealType - Da, - gamma, - Sa; - - Sa=QuantumScale*p->alpha; /* simplify and speed up equations */ - Da=QuantumScale*q->alpha; - gamma=Sa+Da-2.0*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */ - composite->alpha=(MagickRealType) QuantumRange*gamma; - gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); - composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da); - composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da); - composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da); - if (q->colorspace == CMYKColorspace) - composite->black=gamma*Xor(p->black*Sa,Sa,q->black*Da,Da); -} - -static void HSBComposite(const double hue,const double saturation, - const double brightness,double *red,double *green, - double *blue) -{ - double - f, - h, - p, - q, - t; - - /* - Convert HSB to RGB colorspace. - */ - assert(red != (double *) NULL); - assert(green != (double *) NULL); - assert(blue != (double *) NULL); - if (saturation == 0.0) - { - *red=(double) QuantumRange*brightness; - *green=(*red); - *blue=(*red); - return; - } - h=6.0*(hue-floor(hue)); - f=h-floor((double) h); - p=brightness*(1.0-saturation); - q=brightness*(1.0-saturation*f); - t=brightness*(1.0-saturation*(1.0-f)); - switch ((int) h) - { - case 0: - default: - { - *red=(double) QuantumRange*brightness; - *green=(double) QuantumRange*t; - *blue=(double) QuantumRange*p; - break; - } - case 1: - { - *red=(double) QuantumRange*q; - *green=(double) QuantumRange*brightness; - *blue=(double) QuantumRange*p; - break; - } - case 2: - { - *red=(double) QuantumRange*p; - *green=(double) QuantumRange*brightness; - *blue=(double) QuantumRange*t; - break; - } - case 3: - { - *red=(double) QuantumRange*p; - *green=(double) QuantumRange*q; - *blue=(double) QuantumRange*brightness; - break; - } - case 4: - { - *red=(double) QuantumRange*t; - *green=(double) QuantumRange*p; - *blue=(double) QuantumRange*brightness; - break; - } - case 5: - { - *red=(double) QuantumRange*brightness; - *green=(double) QuantumRange*p; - *blue=(double) QuantumRange*q; - break; + status=CompositeOverImage(image,composite_image,x_offset,y_offset, + exception); + return(status); } - } -} - -MagickExport MagickBooleanType CompositeImage(Image *image, - const CompositeOperator compose,const Image *composite_image, - const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception) -{ -#define CompositeImageTag "Composite/Image" - - CacheView - *composite_view, - *image_view; - - const char - *value; - - double - sans; - - GeometryInfo - geometry_info; - - Image - *destination_image; - - MagickBooleanType - modify_outside_overlay, - status; - - MagickOffsetType - progress; - - MagickRealType - amount, - destination_dissolve, - midpoint, - percent_brightness, - percent_saturation, - source_dissolve, - threshold; - - MagickStatusType - flags; - - ssize_t - y; - - /* - Prepare composite image. - */ - assert(image != (Image *) NULL); - assert(image->signature == MagickSignature); - if (image->debug != MagickFalse) - (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - assert(composite_image != (Image *) NULL); - assert(composite_image->signature == MagickSignature); - if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) - return(MagickFalse); destination_image=(Image *) NULL; amount=0.5; destination_dissolve=1.0; @@ -1715,7 +695,7 @@ MagickExport MagickBooleanType CompositeImage(Image *image, image_view=DestroyCacheView(image_view); return(status); } - case CopyOpacityCompositeOp: + case CopyAlphaCompositeOp: case ChangeMaskCompositeOp: { /* @@ -2146,7 +1126,7 @@ MagickExport MagickBooleanType CompositeImage(Image *image, image_view=AcquireCacheView(image); composite_view=AcquireCacheView(composite_image); #if defined(MAGICKCORE_OPENMP_SUPPORT) -// #pragma omp parallel for schedule(dynamic,4) shared(progress,status) + #pragma omp parallel for schedule(dynamic,4) shared(progress,status) #endif for (y=0; y < (ssize_t) image->rows; y++) { @@ -2154,8 +1134,11 @@ MagickExport MagickBooleanType CompositeImage(Image *image, *pixels; double + blue, brightness, + green, hue, + red, saturation; register const Quantum @@ -2167,1192 +1150,998 @@ MagickExport MagickBooleanType CompositeImage(Image *image, register ssize_t x; -MagickBooleanType composite_channels; - if (status == MagickFalse) continue; - if (modify_outside_overlay == MagickFalse) - { - if (y < y_offset) - continue; - if ((y-y_offset) >= (ssize_t) composite_image->rows) - continue; - } - /* - If pixels is NULL, y is outside overlay region. - */ - pixels=(Quantum *) NULL; - p=(Quantum *) NULL; - if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows)) - { - p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset, - composite_image->columns,1,exception); - if (p == (const Quantum *) NULL) - { - status=MagickFalse; - continue; - } - pixels=p; - if (x_offset < 0) - p-=x_offset*GetPixelChannels(composite_image); - } - q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); - if (q == (Quantum *) NULL) - { - status=MagickFalse; - continue; - } - hue=0.0; - saturation=0.0; - brightness=0.0; - for (x=0; x < (ssize_t) image->columns; x++) - { - if (modify_outside_overlay == MagickFalse) - { - if (x < x_offset) - { - q+=GetPixelChannels(image); - continue; - } - if ((x-x_offset) >= (ssize_t) composite_image->columns) - break; - } - composite_channels=MagickFalse; - switch (compose) - { - case AtopCompositeOp: - case ClearCompositeOp: - case ColorBurnCompositeOp: - case ColorDodgeCompositeOp: - case CopyCompositeOp: - case DarkenCompositeOp: - case DarkenIntensityCompositeOp: - case DifferenceCompositeOp: - case DivideDstCompositeOp: - case DivideSrcCompositeOp: - case DstAtopCompositeOp: - case DstCompositeOp: - case DstInCompositeOp: - case DstOverCompositeOp: - case DstOutCompositeOp: - case ExclusionCompositeOp: - case HardLightCompositeOp: - case InCompositeOp: - case LightenCompositeOp: - case LightenIntensityCompositeOp: - case LinearBurnCompositeOp: - case LinearDodgeCompositeOp: - case LinearLightCompositeOp: - case MathematicsCompositeOp: - case MinusDstCompositeOp: - case MinusSrcCompositeOp: - case ModulusAddCompositeOp: - case ModulusSubtractCompositeOp: - case MultiplyCompositeOp: - case NoCompositeOp: - case OutCompositeOp: - case OverCompositeOp: - case OverlayCompositeOp: - case PegtopLightCompositeOp: - case PinLightCompositeOp: - case PlusCompositeOp: - case ReplaceCompositeOp: - case ScreenCompositeOp: - case SoftLightCompositeOp: - case SrcAtopCompositeOp: - case SrcCompositeOp: - case SrcInCompositeOp: - case SrcOutCompositeOp: - case SrcOverCompositeOp: - case VividLightCompositeOp: - case XorCompositeOp: - { - composite_channels=MagickTrue; - break; - } - default: - break; - } - if (composite_channels != MagickFalse) { - MagickRealType - alpha, - Da, - Dc, - Dca, - gamma, - Sa, - Sc, - Sca; - - register ssize_t - i; - - if ((pixels == (Quantum *) NULL) || (x < x_offset) || - ((x-x_offset) >= (ssize_t) composite_image->columns)) - { - Quantum - source[MaxPixelChannels]; - - /* - Virtual composite: - Sc: source color. - Dc: destination color. - */ - (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset, - source,exception); - for (i=0; i < (ssize_t) GetPixelChannels(image); i++) - { - MagickRealType - pixel; - - PixelChannel - channel; - - PixelTrait - composite_traits, - traits; - - channel=GetPixelChannelMapChannel(image,i); - traits=GetPixelChannelMapTraits(image,channel); - composite_traits=GetPixelChannelMapTraits(composite_image, - channel); - if ((traits == UndefinedPixelTrait) || - (composite_traits == UndefinedPixelTrait)) - continue; - switch (compose) - { - case ClearCompositeOp: - case CopyCompositeOp: - case ReplaceCompositeOp: - case SrcCompositeOp: - { - pixel=0.0; - if (channel == AlphaPixelChannel) - pixel=(MagickRealType) TransparentAlpha; - break; - } - case DstAtopCompositeOp: - case InCompositeOp: - case OutCompositeOp: - case SrcInCompositeOp: - case SrcOutCompositeOp: - { - pixel=(MagickRealType) q[i]; - if (channel == AlphaPixelChannel) - pixel=(MagickRealType) TransparentAlpha; - break; - } - default: - { - pixel=source[channel]; - break; - } - } - q[i]=ClampToQuantum(pixel); - } - q+=GetPixelChannels(image); - continue; - } - /* - Authentic composite: - Sa: normalized source alpha. - Da: normalized destination alpha. - */ - Sa=QuantumScale*GetPixelAlpha(composite_image,p); - Da=QuantumScale*GetPixelAlpha(image,q); - switch (compose) - { - case DarkenCompositeOp: - case DstAtopCompositeOp: - case DstInCompositeOp: - case InCompositeOp: - case LightenCompositeOp: - case SrcInCompositeOp: - { - alpha=Sa*Da; - break; - } - case ColorBurnCompositeOp: - case ColorDodgeCompositeOp: - case DifferenceCompositeOp: - case DivideDstCompositeOp: - case DivideSrcCompositeOp: - case ExclusionCompositeOp: - case HardLightCompositeOp: - case LinearBurnCompositeOp: - case LinearDodgeCompositeOp: - case LinearLightCompositeOp: - case MathematicsCompositeOp: - case MinusDstCompositeOp: - case MinusSrcCompositeOp: - case ModulusAddCompositeOp: - case ModulusSubtractCompositeOp: - case MultiplyCompositeOp: - case OverlayCompositeOp: - case PegtopLightCompositeOp: - case PinLightCompositeOp: - case ScreenCompositeOp: - case SoftLightCompositeOp: - case VividLightCompositeOp: - { - alpha=RoundToUnity(Sa+Da-Sa*Da); - break; - } - case DstOverCompositeOp: - { - alpha=Da*(-Sa)+Da+Sa; - break; - } - case DstOutCompositeOp: - { - alpha=Da*(1.0-Sa); - break; - } - case OutCompositeOp: - case SrcOutCompositeOp: - { - alpha=Sa*(1.0-Da); - break; - } - case OverCompositeOp: - case SrcOverCompositeOp: - { - alpha=Sa*(-Da)+Sa+Da; - break; - } - case PlusCompositeOp: - { - alpha=RoundToUnity(Sa+Da); - break; - } - case XorCompositeOp: - { - alpha=Sa+Da-2.0*Sa*Da; - break; - } - default: - { - alpha=1.0; - break; - } - } - for (i=0; i < (ssize_t) GetPixelChannels(image); i++) - { - MagickRealType - pixel; - - PixelChannel - channel; - - PixelTrait - composite_traits, - traits; - - channel=GetPixelChannelMapChannel(image,i); - traits=GetPixelChannelMapTraits(image,channel); - composite_traits=GetPixelChannelMapTraits(composite_image,channel); - if ((traits == UndefinedPixelTrait) || - (composite_traits == UndefinedPixelTrait)) - continue; - /* - Sc: source color. - Sca: source normalized color multiplied by alpha. - Dc: destination color. - Dca: normalized destination color multiplied by alpha. - */ - Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p); - Sca=QuantumScale*Sa*Sc; - Dc=(MagickRealType) q[i]; - Dca=QuantumScale*Da*Dc; - if ((traits & CopyPixelTrait) != 0) - { - if (channel != AlphaPixelChannel) - { - /* - Copy channel. - */ - q[i]=Sc; - continue; - } - /* - Set alpha channel. - */ - switch (compose) - { - case AtopCompositeOp: - case SrcAtopCompositeOp: - case DstCompositeOp: - case NoCompositeOp: - { - pixel=QuantumRange*Da; - break; - } - case CopyCompositeOp: - case DstAtopCompositeOp: - case ReplaceCompositeOp: - case SrcCompositeOp: - { - pixel=QuantumRange*Sa; - break; - } - case DarkenIntensityCompositeOp: - { - pixel=Sa*GetPixelIntensity(composite_image,p) < - Da*GetPixelIntensity(image,q) ? Sa : Da; - break; - } - case LightenIntensityCompositeOp: - { - pixel=Sa*GetPixelIntensity(composite_image,p) > - Da*GetPixelIntensity(image,q) ? Sa : Da; - break; - } - default: - { - pixel=QuantumRange*alpha; - break; - } - } - q[i]=ClampToQuantum(pixel); - continue; - } - /* - Porter-Duff compositions. - */ - switch (compose) - { - case DarkenCompositeOp: - case LightenCompositeOp: - { - gamma=1.0-QuantumScale*Dc; - break; - } - default: - break; - } - gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha); - switch (compose) - { - case AtopCompositeOp: - case SrcAtopCompositeOp: - { - pixel=Sc*Sa+Dc*(1.0-Sa); - break; - } - case ColorBurnCompositeOp: - { - if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon)) - { - pixel=gamma*(Sa*Da+Dca*(1.0-Sa)); - break; - } - if (Sca < MagickEpsilon) - { - pixel=gamma*(Dca*(1.0-Sa)); - break; - } - pixel=gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+ - Dca*(1.0-Sa)); - break; - } - case ColorDodgeCompositeOp: - { - if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon)) - { - pixel=gamma*QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa)); - break; - } - if (fabs(Sca-Sa) < MagickEpsilon) - { - pixel=gamma*QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); - break; - } - pixel=gamma*QuantumRange*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca* - (1.0-Sa)); - break; - } - case CopyCompositeOp: - case ReplaceCompositeOp: - case SrcCompositeOp: - { - pixel=Sc; - break; - } - case DarkenCompositeOp: - { - if (Sc < Dc) - { - pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc); - break; - } - pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc); - break; - } - case DarkenIntensityCompositeOp: - { - pixel=Sa*GetPixelIntensity(composite_image,p) < - Da*GetPixelIntensity(image,q) ? Sc : Dc; - break; - } - case DifferenceCompositeOp: - { - pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc)); - break; - } - case DivideDstCompositeOp: - { - if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon)) - { - pixel=gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa)); - break; - } - if (fabs(Dca) < MagickEpsilon) - { - pixel=gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); - break; - } - pixel=gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); - break; - } - case DivideSrcCompositeOp: - { - if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon)) - { - pixel=gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da)); - break; - } - if (fabs(Sca) < MagickEpsilon) - { - pixel=gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da)); - break; - } - pixel=gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da)); - break; - } - case DstAtopCompositeOp: - { - pixel=Dc*Da+Sc*(1.0-Da); - break; - } - case DstCompositeOp: - case NoCompositeOp: - { - pixel=Dc; - break; - } - case DstInCompositeOp: - { - pixel=gamma*(Sa*Dc*Sa); - break; - } - case DstOutCompositeOp: - { - pixel=gamma*(Da*Dc*(1.0-Sa)); - break; - } - case DstOverCompositeOp: - { - pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc); - break; - } - case ExclusionCompositeOp: - { - pixel=gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); - break; - } - case HardLightCompositeOp: - { - if ((2.0*Sca) < Sa) - { - pixel=gamma*QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca* - (1.0-Sa)); - break; - } - pixel=gamma*QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca* - (1.0-Da)+Dca*(1.0-Sa)); - break; - } - case InCompositeOp: - case SrcInCompositeOp: - { - pixel=gamma*(Da*Sc*Da); - break; - } - case LightenIntensityCompositeOp: - { - pixel=Sa*GetPixelIntensity(composite_image,p) > - Da*GetPixelIntensity(image,q) ? Sc : Dc; - break; - } - case LinearBurnCompositeOp: - { - pixel=gamma*(Sca+Dca-Sa*Da); - break; - } - case LinearDodgeCompositeOp: - { - pixel=gamma*(Sa*Sc+Da*Dc); - break; - } - case LinearLightCompositeOp: - { - pixel=gamma*((Sca-Sa)*Da+Sca+ - Dca); - break; - } - case MathematicsCompositeOp: - { - pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma* - Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+ - Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa); - break; - } - case MinusDstCompositeOp: - { - pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa); - break; - } - case MinusSrcCompositeOp: - { - pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da); - break; - } - case LightenCompositeOp: - { - if (Sc > Dc) - { - pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc); - break; - } - pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc); - break; - } - case ModulusAddCompositeOp: - { - pixel=Sc+Dc; - if (pixel > QuantumRange) - pixel-=(QuantumRange+1.0); - pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa)); - break; - } - case ModulusSubtractCompositeOp: - { - pixel=Sc+Dc; - if (pixel < 0.0) - pixel+=(QuantumRange+1.0); - pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa)); - break; - } - case MultiplyCompositeOp: - { - pixel=gamma*QuantumRange*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); - break; - } - case OutCompositeOp: - case SrcOutCompositeOp: - { - pixel=gamma*(Sa*Sc*(1.0-Da)); - break; - } - case OverCompositeOp: - case SrcOverCompositeOp: - { - pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc); - break; - } - case OverlayCompositeOp: - { - if ((2.0*Dca) < Da) - pixel=gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-Da)); - pixel=gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+Sca* - (1.0-Da)); - break; - } - case PegtopLightCompositeOp: - { - if (fabs(Da) < MagickEpsilon) - { - pixel=gamma*(Sca); - break; - } - pixel=gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+Dca* - (1.0-Sa)); - break; - } - case PinLightCompositeOp: - { - if ((Dca*Sa) < (Da*(2.0*Sca-Sa))) - { - pixel=gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa)); - break; - } - if ((Dca*Sa) > (2.0*Sca*Da)) - { - pixel=gamma*(Sca*Da+Sca+Dca*(1.0-Sa)); - break; - } - pixel=gamma*(Sca*(1.0-Da)+Dca); - break; - } - case PlusCompositeOp: - { - pixel=gamma*(Sa*Sc+Da*Dc); - break; - } - case ScreenCompositeOp: - { - pixel=gamma*QuantumRange*(Sca+Dca-Sca*Dca); - break; - } - case SoftLightCompositeOp: - { - if ((2.0*Sca) < Sa) - { - pixel=gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+Sca* - (1.0-Da)+Dca*(1.0-Sa)); - break; - } - if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da)) - { - pixel=gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*(4.0* - (Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+Dca* - (1.0-Sa)); - break; - } - pixel=gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-(Dca/Da))+ - Sca*(1.0-Da)+Dca*(1.0-Sa)); - break; - } - case VividLightCompositeOp: - { - if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon)) - { - pixel=gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); - break; - } - if ((2.0*Sca) <= Sa) - { - pixel=gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca* - (1.0-Sa)); - break; - } - pixel=gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa)); - break; - } - case XorCompositeOp: + if (modify_outside_overlay == MagickFalse) + { + if (y < y_offset) + continue; + if ((y-y_offset) >= (ssize_t) composite_image->rows) + continue; + } + /* + If pixels is NULL, y is outside overlay region. + */ + pixels=(Quantum *) NULL; + p=(Quantum *) NULL; + if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows)) + { + p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset, + composite_image->columns,1,exception); + if (p == (const Quantum *) NULL) + { + status=MagickFalse; + continue; + } + pixels=p; + if (x_offset < 0) + p-=x_offset*GetPixelChannels(composite_image); + } + q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); + if (q == (Quantum *) NULL) + { + status=MagickFalse; + continue; + } + hue=0.0; + saturation=0.0; + brightness=0.0; + for (x=0; x < (ssize_t) image->columns; x++) + { + MagickRealType + alpha, + Da, + Dc, + Dca, + gamma, + Sa, + Sc, + Sca; + + register ssize_t + i; + + if (modify_outside_overlay == MagickFalse) + { + if (x < x_offset) { - pixel=gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa)); - break; + q+=GetPixelChannels(image); + continue; } - default: + if ((x-x_offset) >= (ssize_t) composite_image->columns) + break; + } + if ((pixels == (Quantum *) NULL) || (x < x_offset) || + ((x-x_offset) >= (ssize_t) composite_image->columns)) + { + Quantum + source[MaxPixelChannels]; + + /* + Virtual composite: + Sc: source color. + Dc: destination color. + */ + (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset, + source,exception); + for (i=0; i < (ssize_t) GetPixelChannels(image); i++) + { + MagickRealType + pixel; + + PixelChannel + channel; + + PixelTrait + composite_traits, + traits; + + channel=GetPixelChannelMapChannel(image,i); + traits=GetPixelChannelMapTraits(image,channel); + composite_traits=GetPixelChannelMapTraits(composite_image, + channel); + if ((traits == UndefinedPixelTrait) || + (composite_traits == UndefinedPixelTrait)) + continue; + switch (compose) { - pixel=Sc; - break; + case ChangeMaskCompositeOp: + case CopyAlphaCompositeOp: + case DstAtopCompositeOp: + case DstInCompositeOp: + case InCompositeOp: + case OutCompositeOp: + case SrcInCompositeOp: + case SrcOutCompositeOp: + { + pixel=(MagickRealType) q[i]; + if (channel == AlphaPixelChannel) + pixel=(MagickRealType) TransparentAlpha; + break; + } + case ClearCompositeOp: + case CopyCompositeOp: + case ReplaceCompositeOp: + case SrcCompositeOp: + { + if (channel == AlphaPixelChannel) + { + pixel=(MagickRealType) TransparentAlpha; + break; + } + pixel=0.0; + break; + } + case DissolveCompositeOp: + { + if (channel == AlphaPixelChannel) + { + pixel=destination_dissolve*GetPixelAlpha(composite_image, + source); + break; + } + pixel=(MagickRealType) source[channel]; + break; + } + default: + { + pixel=(MagickRealType) source[channel]; + break; + } } + q[i]=ClampToQuantum(pixel); } - q[i]=ClampToQuantum(pixel); + q+=GetPixelChannels(image); + continue; } - } else { - PixelInfo - composite, - destination, - source, - zero; - - GetPixelInfo(image,&zero); - source=zero; - destination=zero; - destination.red=(MagickRealType) GetPixelRed(image,q); - destination.green=(MagickRealType) GetPixelGreen(image,q); - destination.blue=(MagickRealType) GetPixelBlue(image,q); - if (image->colorspace == CMYKColorspace) - destination.black=(MagickRealType) GetPixelBlack(image,q); - if (image->colorspace == CMYKColorspace) - { - destination.red=(MagickRealType) QuantumRange-destination.red; - destination.green=(MagickRealType) QuantumRange-destination.green; - destination.blue=(MagickRealType) QuantumRange-destination.blue; - destination.black=(MagickRealType) QuantumRange-destination.black; - } - if (image->matte != MagickFalse) - destination.alpha=(MagickRealType) GetPixelAlpha(image,q); + /* + Authentic composite: + Sa: normalized source alpha. + Da: normalized destination alpha. + */ + Sa=QuantumScale*GetPixelAlpha(composite_image,p); + Da=QuantumScale*GetPixelAlpha(image,q); + switch (compose) + { + case BlendCompositeOp: + { + alpha=RoundToUnity(source_dissolve*Sa+destination_dissolve*Da); + break; + } + case BumpmapCompositeOp: + { + alpha=GetPixelIntensity(composite_image,p)*Sa; + break; + } + case ColorBurnCompositeOp: + case ColorDodgeCompositeOp: + case DifferenceCompositeOp: + case DivideDstCompositeOp: + case DivideSrcCompositeOp: + case ExclusionCompositeOp: + case HardLightCompositeOp: + case LinearBurnCompositeOp: + case LinearDodgeCompositeOp: + case LinearLightCompositeOp: + case LuminizeCompositeOp: + case MathematicsCompositeOp: + case MinusDstCompositeOp: + case MinusSrcCompositeOp: + case ModulusAddCompositeOp: + case ModulusSubtractCompositeOp: + case MultiplyCompositeOp: + case OverlayCompositeOp: + case PegtopLightCompositeOp: + case PinLightCompositeOp: + case ScreenCompositeOp: + case SoftLightCompositeOp: + case VividLightCompositeOp: + { + alpha=RoundToUnity(Sa+Da-Sa*Da); + break; + } + case DarkenCompositeOp: + case DstAtopCompositeOp: + case DstInCompositeOp: + case InCompositeOp: + case LightenCompositeOp: + case SrcInCompositeOp: + { + alpha=Sa*Da; + break; + } + case DissolveCompositeOp: + { + alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve* + Sa+destination_dissolve*Da; + break; + } + case DstOverCompositeOp: + { + alpha=Da*(-Sa)+Da+Sa; + break; + } + case DstOutCompositeOp: + { + alpha=Da*(1.0-Sa); + break; + } + case OutCompositeOp: + case SrcOutCompositeOp: + { + alpha=Sa*(1.0-Da); + break; + } + case OverCompositeOp: + case SrcOverCompositeOp: + { + alpha=Sa*(-Da)+Sa+Da; + break; + } + case PlusCompositeOp: + { + alpha=RoundToUnity(Sa+Da); + break; + } + case XorCompositeOp: + { + alpha=Sa+Da-2.0*Sa*Da; + break; + } + default: + { + alpha=1.0; + break; + } + } + for (i=0; i < (ssize_t) GetPixelChannels(image); i++) + { + MagickRealType + pixel; + + PixelChannel + channel; + + PixelTrait + composite_traits, + traits; + + channel=GetPixelChannelMapChannel(image,i); + traits=GetPixelChannelMapTraits(image,channel); + composite_traits=GetPixelChannelMapTraits(composite_image,channel); + if ((traits == UndefinedPixelTrait) || + (composite_traits == UndefinedPixelTrait)) + continue; /* - Handle destination modifications outside overlaid region. + Sc: source color. + Sca: source normalized color multiplied by alpha. + Dc: destination color. + Dca: normalized destination color multiplied by alpha. */ - composite=destination; - if ((pixels == (Quantum *) NULL) || (x < x_offset) || - ((x-x_offset) >= (ssize_t) composite_image->columns)) + Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p); + Sca=QuantumScale*Sa*Sc; + Dc=(MagickRealType) q[i]; + Dca=QuantumScale*Da*Dc; + if ((traits & CopyPixelTrait) != 0) { + if (channel != AlphaPixelChannel) + { + /* + Copy channel. + */ + q[i]=ClampToQuantum(Sc); + continue; + } + /* + Set alpha channel. + */ switch (compose) { - case DissolveCompositeOp: - case BlendCompositeOp: + case AtopCompositeOp: + case SrcAtopCompositeOp: + case DstCompositeOp: + case NoCompositeOp: { - composite.alpha=destination_dissolve*(composite.alpha); + pixel=QuantumRange*Da; break; } - case ClearCompositeOp: - case SrcCompositeOp: + case ChangeMaskCompositeOp: { - CompositeClear(&destination,&composite); + MagickBooleanType + equivalent; + + equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q); + if ((Da > ((MagickRealType) QuantumRange/2.0)) || + (equivalent != MagickFalse)) + { + pixel=(MagickRealType) TransparentAlpha; + break; + } + pixel=(MagickRealType) OpaqueAlpha; break; } - case InCompositeOp: - case SrcInCompositeOp: - case OutCompositeOp: - case SrcOutCompositeOp: - case DstInCompositeOp: + case CopyCompositeOp: case DstAtopCompositeOp: - case CopyOpacityCompositeOp: - case ChangeMaskCompositeOp: + case ReplaceCompositeOp: + case SrcCompositeOp: { - composite.alpha=(MagickRealType) TransparentAlpha; + pixel=QuantumRange*Sa; break; } - default: + case DarkenIntensityCompositeOp: { - (void) GetOneVirtualPixelInfo(composite_image, - GetPixelCacheVirtualMethod(composite_image),x-x_offset,y- - y_offset,&composite,exception); + pixel=Sa*GetPixelIntensity(composite_image,p) < + Da*GetPixelIntensity(image,q) ? Sa : Da; break; } - } - if (image->colorspace == CMYKColorspace) + case LightenIntensityCompositeOp: + { + pixel=Sa*GetPixelIntensity(composite_image,p) > + Da*GetPixelIntensity(image,q) ? Sa : Da; + break; + } + default: { - composite.red=(MagickRealType) QuantumRange-composite.red; - composite.green=(MagickRealType) QuantumRange-composite.green; - composite.blue=(MagickRealType) QuantumRange-composite.blue; - composite.black=(MagickRealType) QuantumRange-composite.black; + pixel=QuantumRange*alpha; + break; } - SetPixelRed(image,ClampToQuantum(composite.red),q); - SetPixelGreen(image,ClampToQuantum(composite.green),q); - SetPixelBlue(image,ClampToQuantum(composite.blue),q); - if (image->matte != MagickFalse) - SetPixelAlpha(image,ClampToQuantum(composite.alpha),q); - if (image->colorspace == CMYKColorspace) - SetPixelBlack(image,ClampToQuantum(composite.black),q); - q+=GetPixelChannels(image); + } + q[i]=ClampToQuantum(pixel); continue; } /* - Handle normal overlay of source onto destination. + Porter-Duff compositions. */ - source.red=(MagickRealType) GetPixelRed(composite_image,p); - source.green=(MagickRealType) GetPixelGreen(composite_image,p); - source.blue=(MagickRealType) GetPixelBlue(composite_image,p); - if (composite_image->colorspace == CMYKColorspace) - source.black=(MagickRealType) GetPixelBlack(composite_image,p); - if (composite_image->colorspace == CMYKColorspace) + switch (compose) + { + case DarkenCompositeOp: + case LightenCompositeOp: { - source.red=(MagickRealType) QuantumRange-source.red; - source.green=(MagickRealType) QuantumRange-source.green; - source.blue=(MagickRealType) QuantumRange-source.blue; - source.black=(MagickRealType) QuantumRange-source.black; + gamma=1.0-QuantumScale*Dc; + break; } - if (composite_image->matte != MagickFalse) - source.alpha=(MagickRealType) GetPixelAlpha(composite_image,p); - /* - Porter-Duff compositions. - */ + default: + break; + } + gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha); switch (compose) { - case ClearCompositeOp: + case AtopCompositeOp: + case SrcAtopCompositeOp: { - CompositeClear(&destination,&composite); + pixel=Sc*Sa+Dc*(1.0-Sa); break; } - case SrcCompositeOp: + case BlendCompositeOp: + { + pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc); + break; + } + case BlurCompositeOp: + case DisplaceCompositeOp: + case DistortCompositeOp: case CopyCompositeOp: case ReplaceCompositeOp: + case SrcCompositeOp: { - composite=source; + pixel=Sc; break; } - case NoCompositeOp: - case DstCompositeOp: + case BumpmapCompositeOp: + { + if ((QuantumRange*Sa) == TransparentAlpha) + { + pixel=Dc; + break; + } + pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc; break; - case OverCompositeOp: - case SrcOverCompositeOp: + } + case ColorBurnCompositeOp: { - CompositePixelInfoOver(&source,source.alpha,&destination, - destination.alpha,&composite); + /* + Refer to the March 2009 SVG specification. + */ + if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon)) + { + pixel=gamma*(Sa*Da+Dca*(1.0-Sa)); + break; + } + if (Sca < MagickEpsilon) + { + pixel=gamma*(Dca*(1.0-Sa)); + break; + } + pixel=gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+ + Dca*(1.0-Sa)); break; } - case DstOverCompositeOp: + case ColorDodgeCompositeOp: { - CompositePixelInfoOver(&destination,destination.alpha,&source, - source.alpha,&composite); + if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon)) + { + pixel=gamma*QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa)); + break; + } + if (fabs(Sca-Sa) < MagickEpsilon) + { + pixel=gamma*QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); + break; + } + pixel=gamma*QuantumRange*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca* + (1.0-Sa)); break; } - case SrcInCompositeOp: - case InCompositeOp: + case ColorizeCompositeOp: { - CompositeIn(&source,&destination,&composite); + if (GetPixelAlpha(composite_image,p) == TransparentAlpha) + { + pixel=Dc; + break; + } + if (GetPixelAlpha(image,q) == TransparentAlpha) + { + pixel=Sc; + break; + } + CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q), + GetPixelBlue(image,q),&hue,&saturation,&brightness); + CompositeHSB(GetPixelRed(composite_image,p), + GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p), + &hue,&saturation,&sans); + HSBComposite(hue,saturation,brightness,&red,&green,&blue); + switch (channel) + { + case RedPixelChannel: pixel=red; break; + case GreenPixelChannel: pixel=green; break; + case BluePixelChannel: pixel=blue; break; + case AlphaPixelChannel: + { + pixel=Dc; + if (Sa < Da) + pixel=(MagickRealType) GetPixelAlpha(composite_image,p); + break; + } + default: pixel=Dc; break; + } break; } - case DstInCompositeOp: + case DarkenCompositeOp: { - CompositeIn(&destination,&source,&composite); + /* + Darken is equivalent to a 'Minimum' method + OR a greyscale version of a binary 'Or' + OR the 'Intersection' of pixel sets. + */ + if (Sc < Dc) + { + pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc); + break; + } + pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc); break; } - case OutCompositeOp: - case SrcOutCompositeOp: + case CopyAlphaCompositeOp: { - CompositeOut(&source,&destination,&composite); + if (channel == AlphaPixelChannel) + { + if (composite_image->matte != MagickFalse) + pixel=(MagickRealType) GetPixelAlpha(composite_image,p); + else + pixel=(MagickRealType) GetPixelIntensity(composite_image,p); + break; + } + pixel=Dc; break; } - case DstOutCompositeOp: + case CopyBlackCompositeOp: { - CompositeOut(&destination,&source,&composite); + pixel=(MagickRealType) GetPixelBlack(composite_image,p); break; } - case AtopCompositeOp: - case SrcAtopCompositeOp: + case CopyBlueCompositeOp: + case CopyYellowCompositeOp: { - CompositeAtop(&source,&destination,&composite); + pixel=(MagickRealType) GetPixelBlue(composite_image,p); break; } - case DstAtopCompositeOp: + case CopyGreenCompositeOp: + case CopyMagentaCompositeOp: { - CompositeAtop(&destination,&source,&composite); + pixel=(MagickRealType) GetPixelGreen(composite_image,p); break; } - case XorCompositeOp: + case CopyRedCompositeOp: + case CopyCyanCompositeOp: { - CompositeXor(&source,&destination,&composite); + pixel=(MagickRealType) GetPixelRed(composite_image,p); break; } - case PlusCompositeOp: + case DarkenIntensityCompositeOp: { - CompositePlus(image,&source,&destination,&composite); + pixel=Sa*GetPixelIntensity(composite_image,p) < + Da*GetPixelIntensity(image,q) ? Sc : Dc; break; } - case MinusDstCompositeOp: + case DifferenceCompositeOp: { - CompositeMinus(image,&source,&destination,&composite); + pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc)); break; } - case MinusSrcCompositeOp: + case DissolveCompositeOp: { - CompositeMinus(image,&destination,&source,&composite); + pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa* + destination_dissolve*Da*Dc+destination_dissolve*Da*Dc); break; } - case ModulusAddCompositeOp: + case DivideDstCompositeOp: { - CompositeModulusAdd(image,&source,&destination,&composite); + if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon)) + { + pixel=gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa)); + break; + } + if (fabs(Dca) < MagickEpsilon) + { + pixel=gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); + break; + } + pixel=gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); break; } - case ModulusSubtractCompositeOp: + case DivideSrcCompositeOp: { - CompositeModulusSubtract(image,&source,&destination,&composite); + if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon)) + { + pixel=gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da)); + break; + } + if (fabs(Sca) < MagickEpsilon) + { + pixel=gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da)); + break; + } + pixel=gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da)); break; } - case DifferenceCompositeOp: + case DstAtopCompositeOp: { - CompositeDifference(image,&source,&destination,&composite); + pixel=Dc*Da+Sc*(1.0-Da); break; } - case ExclusionCompositeOp: + case DstCompositeOp: + case NoCompositeOp: { - CompositeExclusion(image,&source,&destination,&composite); + pixel=Dc; break; } - case MultiplyCompositeOp: + case DstInCompositeOp: { - CompositeMultiply(image,&source,&destination,&composite); + pixel=gamma*(Sa*Dc*Sa); break; } - case ScreenCompositeOp: + case DstOutCompositeOp: { - CompositeScreen(image,&source,&destination,&composite); + pixel=gamma*(Da*Dc*(1.0-Sa)); break; } - case DivideDstCompositeOp: + case DstOverCompositeOp: { - CompositeDivide(image,&source,&destination,&composite); + pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc); break; } - case DivideSrcCompositeOp: + case ExclusionCompositeOp: { - CompositeDivide(image,&destination,&source,&composite); + pixel=gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); break; } - case DarkenCompositeOp: + case HardLightCompositeOp: { - CompositeDarken(image,&source,&destination,&composite); + if ((2.0*Sca) < Sa) + { + pixel=gamma*QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca* + (1.0-Sa)); + break; + } + pixel=gamma*QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+ + Dca*(1.0-Sa)); break; } - case LightenCompositeOp: + case HueCompositeOp: { - CompositeLighten(image,&source,&destination,&composite); + if (GetPixelAlpha(composite_image,p) == TransparentAlpha) + { + pixel=Dc; + break; + } + if (GetPixelAlpha(image,q) == TransparentAlpha) + { + pixel=Sc; + break; + } + CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q), + GetPixelBlue(image,q),&hue,&saturation,&brightness); + CompositeHSB(GetPixelRed(composite_image,p), + GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p), + &hue,&sans,&sans); + HSBComposite(hue,saturation,brightness,&red,&green,&blue); + switch (channel) + { + case RedPixelChannel: pixel=red; break; + case GreenPixelChannel: pixel=green; break; + case BluePixelChannel: pixel=blue; break; + case AlphaPixelChannel: + { + pixel=Dc; + if (Sa < Da) + pixel=(MagickRealType) GetPixelAlpha(composite_image,p); + break; + } + default: pixel=Dc; break; + } break; } - case DarkenIntensityCompositeOp: + case InCompositeOp: + case SrcInCompositeOp: { - CompositeDarkenIntensity(image,&source,&destination,&composite); + pixel=gamma*(Da*Sc*Da); break; } case LightenIntensityCompositeOp: { - CompositeLightenIntensity(image,&source,&destination,&composite); - break; - } - case MathematicsCompositeOp: - { - CompositeMathematics(image,&source,&destination,&geometry_info, - &composite); - break; - } - case ColorDodgeCompositeOp: - { - CompositeColorDodge(&source,&destination,&composite); + /* + Lighten is equivalent to a 'Maximum' method + OR a greyscale version of a binary 'And' + OR the 'Union' of pixel sets. + */ + pixel=Sa*GetPixelIntensity(composite_image,p) > + Da*GetPixelIntensity(image,q) ? Sc : Dc; break; } - case ColorBurnCompositeOp: + case LinearBurnCompositeOp: { - CompositeColorBurn(&source,&destination,&composite); + /* + LinearBurn: as defined by Abode Photoshop, according to + http://www.simplefilter.de/en/basics/mixmods.html is: + + f(Sc,Dc) = Sc + Dc - 1 + */ + pixel=gamma*(Sca+Dca-Sa*Da); break; } case LinearDodgeCompositeOp: { - CompositeLinearDodge(&source,&destination,&composite); + pixel=gamma*(Sa*Sc+Da*Dc); break; } - case LinearBurnCompositeOp: + case LinearLightCompositeOp: { - CompositeLinearBurn(&source,&destination,&composite); + /* + LinearLight: as defined by Abode Photoshop, according to + http://www.simplefilter.de/en/basics/mixmods.html is: + + f(Sc,Dc) = Dc + 2*Sc - 1 + */ + pixel=gamma*((Sca-Sa)*Da+Sca+Dca); break; } - case HardLightCompositeOp: + case LightenCompositeOp: { - CompositeHardLight(&source,&destination,&composite); + if (Sc > Dc) + { + pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc); + break; + } + pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc); break; } - case OverlayCompositeOp: + case LuminizeCompositeOp: { - CompositeHardLight(&destination,&source,&composite); + if (GetPixelAlpha(composite_image,p) == TransparentAlpha) + { + pixel=Dc; + break; + } + if (GetPixelAlpha(image,q) == TransparentAlpha) + { + pixel=Sc; + break; + } + CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q), + GetPixelBlue(image,q),&hue,&saturation,&brightness); + CompositeHSB(GetPixelRed(composite_image,p), + GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p), + &sans,&sans,&brightness); + HSBComposite(hue,saturation,brightness,&red,&green,&blue); + switch (channel) + { + case RedPixelChannel: pixel=red; break; + case GreenPixelChannel: pixel=green; break; + case BluePixelChannel: pixel=blue; break; + case AlphaPixelChannel: + { + pixel=Dc; + if (Sa < Da) + pixel=(MagickRealType) GetPixelAlpha(composite_image,p); + break; + } + default: pixel=Dc; break; + } break; } - case SoftLightCompositeOp: + case MathematicsCompositeOp: { - CompositeSoftLight(&source,&destination,&composite); + /* + 'Mathematics' a free form user control mathematical composition + is defined as... + + f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D + + Where the arguments A,B,C,D are (currently) passed to composite + as a command separated 'geometry' string in "compose:args" image + artifact. + + A = a->rho, B = a->sigma, C = a->xi, D = a->psi + + Applying the SVG transparency formula (see above), we get... + + Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa) + + Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) + + Dca*(1.0-Sa) + */ + pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma* + Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+ + Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa); break; } - case LinearLightCompositeOp: + case MinusDstCompositeOp: { - CompositeLinearLight(&source,&destination,&composite); + pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa); break; } - case PegtopLightCompositeOp: + case MinusSrcCompositeOp: { - CompositePegtopLight(&source,&destination,&composite); + /* + Minus source from destination. + + f(Sc,Dc) = Sc - Dc + */ + pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da); break; } - case VividLightCompositeOp: + case ModulateCompositeOp: { - CompositeVividLight(&source,&destination,&composite); + ssize_t + offset; + + if (GetPixelAlpha(composite_image,p) == TransparentAlpha) + { + pixel=Dc; + break; + } + offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint); + if (offset == 0) + { + pixel=Dc; + break; + } + CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q), + GetPixelBlue(image,q),&hue,&saturation,&brightness); + brightness+=(0.01*percent_brightness*offset)/midpoint; + saturation*=0.01*percent_saturation; + HSBComposite(hue,saturation,brightness,&red,&green,&blue); + switch (channel) + { + case RedPixelChannel: pixel=red; break; + case GreenPixelChannel: pixel=green; break; + case BluePixelChannel: pixel=blue; break; + default: pixel=Dc; break; + } break; } - case PinLightCompositeOp: + case ModulusAddCompositeOp: { - CompositePinLight(&source,&destination,&composite); + pixel=Sc+Dc; + if (pixel > QuantumRange) + pixel-=(QuantumRange+1.0); + pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa)); break; } - case ChangeMaskCompositeOp: + case ModulusSubtractCompositeOp: { - if ((composite.alpha > ((MagickRealType) QuantumRange/2.0)) || - (IsFuzzyEquivalencePixelInfo(&source,&destination) != MagickFalse)) - composite.alpha=(MagickRealType) TransparentAlpha; - else - composite.alpha=(MagickRealType) OpaqueAlpha; + pixel=Sc+Dc; + if (pixel < 0.0) + pixel+=(QuantumRange+1.0); + pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa)); break; } - case BumpmapCompositeOp: + case MultiplyCompositeOp: { - if (source.alpha == TransparentAlpha) - break; - CompositeBumpmap(&source,&destination,&composite); + pixel=gamma*QuantumRange*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa)); break; } - case DissolveCompositeOp: + case OutCompositeOp: + case SrcOutCompositeOp: { - CompositePixelInfoOver(&source,source_dissolve*source.alpha, - &destination,(MagickRealType) (destination_dissolve* - destination.alpha),&composite); + pixel=gamma*(Sa*Sc*(1.0-Da)); break; } - case BlendCompositeOp: + case OverCompositeOp: + case SrcOverCompositeOp: { - CompositePixelInfoBlend(&source,source_dissolve,&destination, - destination_dissolve,&composite); + pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc); break; } - case ThresholdCompositeOp: + case OverlayCompositeOp: { - CompositeThreshold(&source,&destination,threshold,amount,&composite); + if ((2.0*Dca) < Da) + pixel=gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-Da)); + pixel=gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+Sca*(1.0-Da)); break; } - case ModulateCompositeOp: + case PegtopLightCompositeOp: { - double - blue, - green, - red; + /* + PegTop: A Soft-Light alternative: A continuous version of the + Softlight function, producing very similar results. - ssize_t - offset; + f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc - if (source.alpha == TransparentAlpha) - break; - offset=(ssize_t) (GetPixelInfoIntensity(&source)-midpoint); - if (offset == 0) - break; - CompositeHSB(destination.red,destination.green,destination.blue,&hue, - &saturation,&brightness); - brightness+=(0.01*percent_brightness*offset)/midpoint; - saturation*=0.01*percent_saturation; - HSBComposite(hue,saturation,brightness,&red,&green,&blue); - composite.red=red; - composite.green=green; - composite.blue=blue; + http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm. + */ + if (fabs(Da) < MagickEpsilon) + { + pixel=gamma*(Sca); + break; + } + pixel=gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+Dca* + (1.0-Sa)); break; } - case HueCompositeOp: + case PinLightCompositeOp: { - if (source.alpha == TransparentAlpha) - break; - if (destination.alpha == TransparentAlpha) + /* + PinLight: A Photoshop 7 composition method + http://www.simplefilter.de/en/basics/mixmods.html + + f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc + */ + if ((Dca*Sa) < (Da*(2.0*Sca-Sa))) + { + pixel=gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa)); + break; + } + if ((Dca*Sa) > (2.0*Sca*Da)) { - composite=source; + pixel=gamma*(Sca*Da+Sca+Dca*(1.0-Sa)); break; } - CompositeHSB(destination.red,destination.green,destination.blue,&hue, - &saturation,&brightness); - CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans); - HSBComposite(hue,saturation,brightness,&composite.red, - &composite.green,&composite.blue); - if (source.alpha < destination.alpha) - composite.alpha=source.alpha; + pixel=gamma*(Sca*(1.0-Da)+Dca); + break; + } + case PlusCompositeOp: + { + pixel=gamma*(Sa*Sc+Da*Dc); break; } case SaturateCompositeOp: { - if (source.alpha == TransparentAlpha) - break; - if (destination.alpha == TransparentAlpha) + if (GetPixelAlpha(composite_image,p) == TransparentAlpha) { - composite=source; + pixel=Dc; break; } - CompositeHSB(destination.red,destination.green,destination.blue,&hue, - &saturation,&brightness); - CompositeHSB(source.red,source.green,source.blue,&sans,&saturation, - &sans); - HSBComposite(hue,saturation,brightness,&composite.red, - &composite.green,&composite.blue); - if (source.alpha < destination.alpha) - composite.alpha=source.alpha; - break; - } - case LuminizeCompositeOp: - { - if (source.alpha == TransparentAlpha) - break; - if (destination.alpha == TransparentAlpha) + if (GetPixelAlpha(image,q) == TransparentAlpha) { - composite=source; + pixel=Sc; break; } - CompositeHSB(destination.red,destination.green,destination.blue,&hue, - &saturation,&brightness); - CompositeHSB(source.red,source.green,source.blue,&sans,&sans, - &brightness); - HSBComposite(hue,saturation,brightness,&composite.red, - &composite.green,&composite.blue); - if (source.alpha < destination.alpha) - composite.alpha=source.alpha; - break; - } - case ColorizeCompositeOp: - { - if (source.alpha == TransparentAlpha) - break; - if (destination.alpha == TransparentAlpha) + CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q), + GetPixelBlue(image,q),&hue,&saturation,&brightness); + CompositeHSB(GetPixelRed(composite_image,p), + GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p), + &sans,&saturation,&sans); + HSBComposite(hue,saturation,brightness,&red,&green,&blue); + switch (channel) + { + case RedPixelChannel: pixel=red; break; + case GreenPixelChannel: pixel=green; break; + case BluePixelChannel: pixel=blue; break; + case AlphaPixelChannel: { - composite=source; + pixel=Dc; + if (Sa < Da) + pixel=(MagickRealType) GetPixelAlpha(composite_image,p); break; } - CompositeHSB(destination.red,destination.green,destination.blue,&sans, - &sans,&brightness); - CompositeHSB(source.red,source.green,source.blue,&hue,&saturation, - &sans); - HSBComposite(hue,saturation,brightness,&composite.red, - &composite.green,&composite.blue); - if (source.alpha < destination.alpha) - composite.alpha=source.alpha; + default: pixel=Dc; break; + } break; } - case CopyRedCompositeOp: - case CopyCyanCompositeOp: + case ScreenCompositeOp: { - composite.red=source.red; + /* + Screen: a negated multiply: + + f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc) + */ + pixel=gamma*QuantumRange*(Sca+Dca-Sca*Dca); break; } - case CopyGreenCompositeOp: - case CopyMagentaCompositeOp: + case SoftLightCompositeOp: { - composite.green=source.green; + /* + Refer to the March 2009 SVG specification. + */ + if ((2.0*Sca) < Sa) + { + pixel=gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+Sca*(1.0-Da)+ + Dca*(1.0-Sa)); + break; + } + if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da)) + { + pixel=gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*(4.0*(Dca/Da)+ + 1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa)); + break; + } + pixel=gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-(Dca/Da))+ + Sca*(1.0-Da)+Dca*(1.0-Sa)); break; } - case CopyBlueCompositeOp: - case CopyYellowCompositeOp: + case ThresholdCompositeOp: { - composite.blue=source.blue; + MagickRealType + delta; + + delta=Sc-Dc; + if ((MagickRealType) fabs((double) (2.0*delta)) < threshold) + { + pixel=gamma*Dc; + break; + } + pixel=gamma*(Dc+delta*amount); break; } - case CopyOpacityCompositeOp: + case VividLightCompositeOp: { - if (source.matte == MagickFalse) + /* + VividLight: A Photoshop 7 composition method. See + http://www.simplefilter.de/en/basics/mixmods.html. + + f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc)) + */ + if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon)) + { + pixel=gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa)); + break; + } + if ((2.0*Sca) <= Sa) { - composite.alpha=(MagickRealType) GetPixelInfoIntensity(&source); + pixel=gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca* + (1.0-Sa)); break; } - composite.alpha=source.alpha; + pixel=gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa)); break; } - case CopyBlackCompositeOp: + case XorCompositeOp: { - if (source.colorspace != CMYKColorspace) - ConvertRGBToCMYK(&source); - composite.black=source.black; + pixel=gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa)); break; } - case BlurCompositeOp: - case DisplaceCompositeOp: - case DistortCompositeOp: + default: { - composite=source; + pixel=Sc; break; } - default: - break; } - if (image->colorspace == CMYKColorspace) - { - composite.red=(MagickRealType) QuantumRange-composite.red; - composite.green=(MagickRealType) QuantumRange-composite.green; - composite.blue=(MagickRealType) QuantumRange-composite.blue; - composite.black=(MagickRealType) QuantumRange-composite.black; - } - SetPixelRed(image,ClampToQuantum(composite.red),q); - SetPixelGreen(image,ClampToQuantum(composite.green),q); - SetPixelBlue(image,ClampToQuantum(composite.blue),q); - if (image->colorspace == CMYKColorspace) - SetPixelBlack(image,ClampToQuantum(composite.black),q); - SetPixelAlpha(image,ClampToQuantum(composite.alpha),q); + q[i]=ClampToQuantum(pixel); } p+=GetPixelChannels(composite_image); - if (p >= (pixels+composite_image->columns*GetPixelChannels(composite_image))) + channels=GetPixelChannels(composite_image); + if (p >= (pixels+channels*composite_image->columns)) p=pixels; q+=GetPixelChannels(image); } diff --git a/MagickCore/composite.h b/MagickCore/composite.h index 38ad4a0f5..976e9f255 100644 --- a/MagickCore/composite.h +++ b/MagickCore/composite.h @@ -39,7 +39,7 @@ typedef enum CopyCyanCompositeOp, CopyGreenCompositeOp, CopyMagentaCompositeOp, - CopyOpacityCompositeOp, + CopyAlphaCompositeOp, CopyRedCompositeOp, CopyYellowCompositeOp, DarkenCompositeOp, diff --git a/MagickCore/distort.c b/MagickCore/distort.c index ae8a31705..bf3eaceda 100644 --- a/MagickCore/distort.c +++ b/MagickCore/distort.c @@ -1561,7 +1561,7 @@ MagickExport Image *DistortResizeImage(const Image *image, exception); (void) SetImageAlphaChannel(resize_alpha,DeactivateAlphaChannel, exception); - (void) CompositeImage(resize_image,CopyOpacityCompositeOp,resize_alpha, + (void) CompositeImage(resize_image,CopyAlphaCompositeOp,resize_alpha, 0,0,exception); resize_alpha=DestroyImage(resize_alpha); } diff --git a/MagickCore/fx.c b/MagickCore/fx.c index 6bc5238e4..4607dc200 100644 --- a/MagickCore/fx.c +++ b/MagickCore/fx.c @@ -5449,7 +5449,7 @@ MagickExport Image *VignetteImage(const Image *image,const double radius, return((Image *) NULL); } blur_image->matte=MagickFalse; - (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0, + (void) CompositeImage(canvas_image,CopyAlphaCompositeOp,blur_image,0,0, exception); blur_image=DestroyImage(blur_image); vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception); diff --git a/MagickCore/morphology.c b/MagickCore/morphology.c index 0c239f5bc..e8e448c14 100644 --- a/MagickCore/morphology.c +++ b/MagickCore/morphology.c @@ -3864,7 +3864,7 @@ MagickPrivate Image *MorphologyApply(const Image *image, /* Preserve the alpha channel of input image - but turned off */ (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel, exception); - (void) CompositeImage(rslt_image, CopyOpacityCompositeOp, image, 0, 0, + (void) CompositeImage(rslt_image, CopyAlphaCompositeOp, image, 0, 0, exception); (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel, exception); diff --git a/MagickCore/option.c b/MagickCore/option.c index 1dcb76b38..b3d87464f 100644 --- a/MagickCore/option.c +++ b/MagickCore/option.c @@ -734,13 +734,13 @@ static const OptionInfo { "ColorBurn", ColorBurnCompositeOp, UndefinedOptionFlag, MagickFalse }, { "ColorDodge", ColorDodgeCompositeOp, UndefinedOptionFlag, MagickFalse }, { "Colorize", ColorizeCompositeOp, UndefinedOptionFlag, MagickFalse }, + { "CopyAlpha", CopyAlphaCompositeOp, UndefinedOptionFlag, MagickFalse }, { "CopyBlack", CopyBlackCompositeOp, UndefinedOptionFlag, MagickFalse }, { "CopyBlue", CopyBlueCompositeOp, UndefinedOptionFlag, MagickFalse }, { "CopyCyan", CopyCyanCompositeOp, UndefinedOptionFlag, MagickFalse }, { "CopyGreen", CopyGreenCompositeOp, UndefinedOptionFlag, MagickFalse }, { "Copy", CopyCompositeOp, UndefinedOptionFlag, MagickFalse }, { "CopyMagenta", CopyMagentaCompositeOp, UndefinedOptionFlag, MagickFalse }, - { "CopyOpacity", CopyOpacityCompositeOp, UndefinedOptionFlag, MagickFalse }, { "CopyRed", CopyRedCompositeOp, UndefinedOptionFlag, MagickFalse }, { "CopyYellow", CopyYellowCompositeOp, UndefinedOptionFlag, MagickFalse }, { "Darken", DarkenCompositeOp, UndefinedOptionFlag, MagickFalse }, diff --git a/MagickCore/pixel.c b/MagickCore/pixel.c index d98addbee..b845e2a66 100644 --- a/MagickCore/pixel.c +++ b/MagickCore/pixel.c @@ -5170,20 +5170,22 @@ MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image, % % The format of the IsFuzzyEquivalencePixel method is: % -% void IsFuzzyEquivalencePixel(const Image *image,const Quantum *p, -% const Quantum *q) +% void IsFuzzyEquivalencePixel(const Image *source,const Quantum *p, +% const Image *destination,const Quantum *q) % % A description of each parameter follows: % -% o image: the image. +% o source: the source image. % % o p: Pixel p. % +% o destination: the destination image. +% % o q: Pixel q. % */ -MagickExport MagickBooleanType IsFuzzyEquivalencePixel(const Image *image, - const Quantum *p,const Quantum *q) +MagickExport MagickBooleanType IsFuzzyEquivalencePixel(const Image *source, + const Quantum *p,const Image *destination,const Quantum *q) { MagickRealType fuzz, @@ -5193,18 +5195,18 @@ MagickExport MagickBooleanType IsFuzzyEquivalencePixel(const Image *image, distance, scale; - fuzz=MagickMax(image->fuzz,(MagickRealType) MagickSQ1_2)*MagickMax( - image->fuzz,(MagickRealType) MagickSQ1_2); + fuzz=MagickMax(source->fuzz,(MagickRealType) MagickSQ1_2)*MagickMax( + destination->fuzz,(MagickRealType) MagickSQ1_2); scale=1.0; distance=0.0; - if (image->matte != MagickFalse) + if (source->matte != MagickFalse) { /* Transparencies are involved - set alpha distance */ - pixel=(MagickRealType) ((image->matte != MagickFalse ? - GetPixelAlpha(image,p) : OpaqueAlpha)-(image->matte != MagickFalse ? - GetPixelAlpha(image,q) : OpaqueAlpha)); + pixel=(MagickRealType) ((source->matte != MagickFalse ? + GetPixelAlpha(source,p) : OpaqueAlpha)-(source->matte != MagickFalse ? + GetPixelAlpha(destination,q) : OpaqueAlpha)); distance=pixel*pixel; if (distance > fuzz) return(MagickFalse); @@ -5212,8 +5214,8 @@ MagickExport MagickBooleanType IsFuzzyEquivalencePixel(const Image *image, Generate a alpha scaling factor to generate a 4D cone on colorspace Note that if one color is transparent, distance has no color component. */ - scale=QuantumScale*GetPixelAlpha(image,p); - scale*=QuantumScale*GetPixelAlpha(image,q); + scale=QuantumScale*GetPixelAlpha(source,p); + scale*=QuantumScale*GetPixelAlpha(destination,q); if (scale <= MagickEpsilon) return(MagickTrue); } @@ -5222,10 +5224,10 @@ MagickExport MagickBooleanType IsFuzzyEquivalencePixel(const Image *image, */ distance*=3.0; /* rescale appropriately */ fuzz*=3.0; - pixel=GetPixelRed(image,p)-(MagickRealType) GetPixelRed(image,q); - if ((image->colorspace == HSLColorspace) || - (image->colorspace == HSBColorspace) || - (image->colorspace == HWBColorspace)) + pixel=GetPixelRed(source,p)-(MagickRealType) GetPixelRed(destination,q); + if ((source->colorspace == HSLColorspace) || + (source->colorspace == HSBColorspace) || + (source->colorspace == HWBColorspace)) { /* Compute an arc distance for hue. It should be a vector angle of @@ -5238,11 +5240,11 @@ MagickExport MagickBooleanType IsFuzzyEquivalencePixel(const Image *image, distance+=scale*pixel*pixel; if (distance > fuzz) return(MagickFalse); - pixel=GetPixelGreen(image,p)-(MagickRealType) GetPixelGreen(image,q); + pixel=GetPixelGreen(source,p)-(MagickRealType) GetPixelGreen(destination,q); distance+=scale*pixel*pixel; if (distance > fuzz) return(MagickFalse); - pixel=GetPixelBlue(image,p)-(MagickRealType) GetPixelBlue(image,q); + pixel=GetPixelBlue(source,p)-(MagickRealType) GetPixelBlue(destination,q); distance+=scale*pixel*pixel; if (distance > fuzz) return(MagickFalse); diff --git a/MagickCore/pixel.h b/MagickCore/pixel.h index 2679421ce..6a314cec1 100644 --- a/MagickCore/pixel.h +++ b/MagickCore/pixel.h @@ -143,7 +143,7 @@ extern MagickExport MagickBooleanType InterpolatePixelInfo(const Image *,const CacheView_ *, const PixelInterpolateMethod,const double,const double,PixelInfo *, ExceptionInfo *), - IsFuzzyEquivalencePixel(const Image *,const Quantum *, + IsFuzzyEquivalencePixel(const Image *,const Quantum *,const Image *, const Quantum *), IsFuzzyEquivalencePixelInfo(const PixelInfo *,const PixelInfo *); diff --git a/MagickCore/xwindow.c b/MagickCore/xwindow.c index 429621739..624e6d695 100644 --- a/MagickCore/xwindow.c +++ b/MagickCore/xwindow.c @@ -2647,7 +2647,7 @@ MagickPrivate MagickBooleanType XDrawImage(Display *display, draw_view=DestroyCacheView(draw_view); (void) XParseGeometry(draw_info->geometry,&x,&y,&width,&height); if (draw_info->stencil == TransparentStencil) - (void) CompositeImage(image,CopyOpacityCompositeOp,draw_image,(ssize_t) x, + (void) CompositeImage(image,CopyAlphaCompositeOp,draw_image,(ssize_t) x, (ssize_t) y,exception); else { diff --git a/coders/msl.c b/coders/msl.c index 8ca8f8ccb..3fa0a3b1a 100644 --- a/coders/msl.c +++ b/coders/msl.c @@ -1927,7 +1927,7 @@ static void MSLStartElement(void *context,const xmlChar *tag, SetImageType(composite_image,TrueColorMatteType, &exception); (void) CompositeImage(composite_image, - CopyOpacityCompositeOp,msl_info->image[j],0,0, + CopyAlphaCompositeOp,msl_info->image[j],0,0, &exception); break; }