% July 1992 %
% %
% %
-% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
+% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
#include "MagickCore/studio.h"
#include "MagickCore/artifact.h"
#include "MagickCore/cache.h"
+#include "MagickCore/cache-private.h"
#include "MagickCore/cache-view.h"
#include "MagickCore/client.h"
#include "MagickCore/color.h"
#include "MagickCore/resource_.h"
#include "MagickCore/string_.h"
#include "MagickCore/thread-private.h"
+#include "MagickCore/token.h"
#include "MagickCore/utility.h"
+#include "MagickCore/utility-private.h"
#include "MagickCore/version.h"
\f
/*
% The format of the CompositeImage method is:
%
% MagickBooleanType CompositeImage(Image *image,
-% const CompositeOperator compose,Image *composite_image,
-% const ssize_t x_offset,const ssize_t y_offset)
+% const Image *composite_image,const CompositeOperator compose,
+% const MagickBooleanType clip_to_self,const ssize_t x_offset,
+% const ssize_t y_offset,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: the destination image, modified by he composition
%
+% o composite_image: the composite (source) image.
+%
% o compose: This operator affects how the composite is applied to
% the image. The operators and how they are utilized are listed here
% http://www.w3.org/TR/SVG12/#compositing.
%
-% o composite_image: the composite (source) image.
+% o clip_to_self: set to MagickTrue to limit composition to area composed.
%
% o x_offset: the column offset of the composited image.
%
% Compose methods needing such arguments include "BlendCompositeOp" and
% "DisplaceCompositeOp".
%
-% o "compose:outside-overlay"
-% Modify how the composition is to effect areas not directly covered
-% by the 'composite_image' at the offset given. Normally this is
-% dependant on the 'compose' method, especially Duff-Porter methods.
+% o exception: return any errors or warnings in this structure.
%
-% If set to "false" then disable all normal handling of pixels not
-% covered by the composite_image. Typically used for repeated tiling
-% of the composite_image by the calling API.
-%
-% Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
-%
-*/
-
-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' 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.
-*/
-
-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);
-}
-
-/*
- What is this Composition method for? Can't find any specification!
- WARNING this is not doing correct 'over' blend handling (Anthony Thyssen).
-*/
-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;
- if (q->colorspace == CMYKColorspace)
- composite->black=0.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->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels.
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=MagickMin(p->red,q->red);
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=MagickMin(p->green,q->green);
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=MagickMin(p->blue,q->blue);
- if ((GetPixelBlackTraits(image) & ActivePixelTrait) != 0 &&
- (q->colorspace == CMYKColorspace))
- composite->black=MagickMin(p->black,q->black);
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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->sync == MagickFalse)
- {
- MagickBooleanType
- from_p;
-
- from_p=GetPixelInfoIntensity(p) < GetPixelInfoIntensity(q) ? MagickTrue :
- MagickFalse;
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=from_p != MagickFalse ? p->red : q->red;
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=from_p != MagickFalse ? p->green : q->green;
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=from_p != MagickFalse ? p->blue : q->blue;
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=from_p != MagickFalse ? p->black : q->black;
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels.
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=fabs((double) (p->red-q->red));
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=fabs((double) (p->green-q->green));
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=fabs((double) (p->blue-q->blue));
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=fabs((double) (p->black-q->black));
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels.
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=QuantumRange*Divide(QuantumScale*p->red,1.0,
- QuantumScale*q->red,1.0);
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=QuantumRange*Divide(QuantumScale*p->green,1.0,
- QuantumScale*q->green,1.0);
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=QuantumRange*Divide(QuantumScale*p->blue,1.0,
- QuantumScale*q->blue,1.0);
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=QuantumRange*Divide(QuantumScale*p->black,1.0,
- QuantumScale*q->black,1.0);
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels.
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=QuantumRange*Exclusion(QuantumScale*p->red,1.0,
- QuantumScale*q->red,1.0);
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=QuantumRange*Exclusion(QuantumScale*p->green,1.0,
- QuantumScale*q->green,1.0);
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=QuantumRange*Exclusion(QuantumScale*p->blue,1.0,
- QuantumScale*q->blue,1.0);
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=QuantumRange*Exclusion(QuantumScale*p->black,1.0,
- QuantumScale*q->black,1.0);
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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)
-{
- 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);
-}
-
-static void CompositeHSB(const MagickRealType red,const MagickRealType green,
- const MagickRealType blue,double *hue,double *saturation,double *brightness)
-{
- MagickRealType
- delta,
- max,
- min;
-
- /*
- Convert RGB to HSB colorspace.
- */
- assert(hue != (double *) NULL);
- assert(saturation != (double *) NULL);
- assert(brightness != (double *) NULL);
- max=(red > green ? red : green);
- if (blue > max)
- max=blue;
- min=(red < green ? red : green);
- if (blue < min)
- min=blue;
- *hue=0.0;
- *saturation=0.0;
- *brightness=(double) (QuantumScale*max);
- if (max == 0.0)
- return;
- *saturation=(double) (1.0-min/max);
- delta=max-min;
- if (delta == 0.0)
- return;
- if (red == max)
- *hue=(double) ((green-blue)/delta);
- else
- if (green == max)
- *hue=(double) (2.0+(blue-red)/delta);
- else
- if (blue == max)
- *hue=(double) (4.0+(red-green)/delta);
- *hue/=6.0;
- if (*hue < 0.0)
- *hue+=1.0;
-}
-
-static inline MagickRealType In(const MagickRealType p,const MagickRealType Sa,
- const MagickRealType magick_unused(q),const MagickRealType Da)
-{
- return(Sa*p*Da);
-}
-
-static inline void CompositeIn(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;
- gamma=Sa*Da;
- composite->alpha=(MagickRealType) QuantumRange*gamma;
- gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
- 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);
-}
-
-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)
-{
- MagickRealType
- gamma;
-
- /*
- Lighten is also equvalent to a 'Maximum' method OR a greyscale version of a
- binary 'And' OR the 'Union' of pixel sets.
- */
- if (image->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=MagickMax(p->red,q->red);
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=MagickMax(p->green,q->green);
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=MagickMax(p->blue,q->blue);
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=MagickMax(p->black,q->black);
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
- composite->alpha=MagickMin(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*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->sync == MagickFalse)
- {
- MagickBooleanType
- from_p;
-
- from_p=GetPixelInfoIntensity(p) > GetPixelInfoIntensity(q) ? MagickTrue :
- MagickFalse;
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=from_p != MagickFalse ? p->red : q->red;
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=from_p != MagickFalse ? p->green : q->green;
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=from_p != MagickFalse ? p->blue : q->blue;
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=from_p != MagickFalse ? p->black : q->black;
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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 void CompositeLinearDodge(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=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);
-}
-
-
-static inline MagickRealType LinearBurn(const MagickRealType Sca,
- const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
-{
- /*
- 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);
-}
-
-static inline void CompositeLinearBurn(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*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);
-}
-
-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:
-
- f(Sc,Dc) = Dc + 2*Sc - 1
- */
- return((Sca-Sa)*Da+Sca+Dca);
-}
-
-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)
-
- 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);
-}
-
-static inline void CompositeMathematics(const Image *image,const PixelInfo *p,
- const PixelInfo *q,const GeometryInfo *args,PixelInfo *composite)
-{
- MagickRealType
- Da,
- gamma,
- Sa;
-
- Sa=QuantumScale*p->alpha; /* ??? - AT */
- Da=QuantumScale*q->alpha;
- if (image->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels.
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=QuantumRange*Mathematics(QuantumScale*p->red,1.0,
- QuantumScale*q->red,1.0,args);
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=QuantumRange*Mathematics(QuantumScale*p->green,1.0,
- QuantumScale*q->green,1.0,args);
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=QuantumRange*Mathematics(QuantumScale*p->blue,1.0,
- QuantumScale*q->blue,1.0,args);
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=QuantumRange*Mathematics(QuantumScale*p->black,1.0,
- QuantumScale*q->black,1.0,args);
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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);
-}
-
-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.
- */
- if (image->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels.
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=p->red+q->red;
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=p->green+q->green;
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=p->blue+q->blue;
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=p->black+q->black;
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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))
-{
- /*
- Minus Source from Destination
-
- f(Sc,Dc) = Sc - Dc
- */
- 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->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels.
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=p->red-q->red;
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=p->green-q->green;
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=p->blue-q->blue;
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=p->black-q->black;
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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);
-}
-
-static inline MagickRealType ModulusAdd(const MagickRealType p,
- const MagickRealType Sa,const MagickRealType q, const MagickRealType Da)
-{
- MagickRealType
- pixel;
-
- pixel=p+q;
- if (pixel > QuantumRange)
- pixel-=(QuantumRange+1.0);
- return(pixel*Sa*Da+p*Sa*(1.0-Da)+q*Da*(1.0-Sa));
-}
-
-static inline void CompositeModulusAdd(const Image *image,const PixelInfo *p,
- const PixelInfo *q,PixelInfo *composite)
-{
- MagickRealType
- Da,
- gamma,
- Sa;
-
- if (image->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels.
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=ModulusAdd(p->black,1.0,q->black,1.0);
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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;
-
- 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));
-}
-
-static inline void CompositeModulusSubtract(const Image *image,
- const PixelInfo *p,const PixelInfo *q,PixelInfo *composite)
-{
- MagickRealType
- Da,
- gamma,
- Sa;
-
- if (image->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels,
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=ModulusSubtract(p->black,1.0,q->black,1.0);
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
- composite->alpha=ModulusSubtract(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);
- 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);
-}
-
-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));
-}
-
-static inline void CompositeMultiply(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->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels.
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=QuantumScale*p->red*q->red;
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=QuantumScale*p->green*q->green;
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=QuantumScale*p->blue*q->blue;
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=QuantumScale*p->black*q->black;
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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);
-}
-
-static inline MagickRealType Out(const MagickRealType p,const MagickRealType Sa,
- const MagickRealType magick_unused(q),const MagickRealType Da)
-{
- return(Sa*p*(1.0-Da));
-}
-
-static inline void CompositeOut(const PixelInfo *p,const PixelInfo *q,
- PixelInfo *composite)
-{
- 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);
-}
-
-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.
-
- f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
-
- 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));
-}
-
-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);
-}
-
-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
-
- 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);
-}
-
-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);
-}
-
-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)
- */
- return(Sca+Dca-Sca*Dca);
-}
+*/
-static inline void CompositeScreen(const Image *image,const PixelInfo *p,
- const PixelInfo *q,PixelInfo *composite)
-{
- MagickRealType
- Da,
- gamma,
- Sa;
+/*
+ Composition based on the SVG specification:
- Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
- Da=QuantumScale*q->alpha;
- if (image->sync == MagickFalse)
- {
- /*
- Handle channels as separate grayscale channels.
- */
- if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
- composite->red=QuantumRange*Screen(QuantumScale*p->red,
- QuantumScale*q->red);
- if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
- composite->green=QuantumRange*Screen(QuantumScale*p->green,
- QuantumScale*q->green);
- if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
- composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
- QuantumScale*q->blue);
- if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
- (q->colorspace == CMYKColorspace))
- composite->black=QuantumRange*Screen(QuantumScale*p->black,
- QuantumScale*q->black);
- if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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);
-}
+ 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
-static MagickRealType SoftLight(const MagickRealType Sca,
- const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
-{
- MagickRealType
- alpha,
- beta;
+ 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)
- /*
- 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);
-}
+ 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.
-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);
-}
+ Da' in in the follow formula as 'gamma' The resulting alpla value.
-static inline MagickRealType Threshold(const MagickRealType p,
- const MagickRealType q,const MagickRealType threshold,
- const MagickRealType amount)
-{
- MagickRealType
- delta;
+ 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
- /*
- 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
+ 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'.
- Deprecated.
- */
- delta=p-q;
- if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
- return(q);
- return(q+delta*amount);
-}
+ Mathematical operator changes to be applied from IM v6.7...
-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);
-}
+ 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'.
-static MagickRealType VividLight(const MagickRealType Sca,
- const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
+ 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 void CompositeHSB(const double red,const double green,
+ const double blue,double *hue,double *saturation,double *brightness)
{
- /*
- VividLight: A Photoshop 7 composition method. See
- http://www.simplefilter.de/en/basics/mixmods.html.
+ double
+ delta,
+ max,
+ min;
- f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
+ /*
+ Convert RGB to HSB colorspace.
*/
- 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);
+ assert(hue != (double *) NULL);
+ assert(saturation != (double *) NULL);
+ assert(brightness != (double *) NULL);
+ max=(red > green ? red : green);
+ if (blue > max)
+ max=blue;
+ min=(red < green ? red : green);
+ if (blue < min)
+ min=blue;
+ *hue=0.0;
+ *saturation=0.0;
+ *brightness=(double) (QuantumScale*max);
+ if (fabs((double) max) < MagickEpsilon)
+ return;
+ *saturation=(double) (1.0-min/max);
+ delta=(MagickRealType) max-min;
+ if (fabs(delta) < MagickEpsilon)
+ return;
+ if (fabs((double) red-max) < MagickEpsilon)
+ *hue=(double) ((green-blue)/delta);
+ else
+ if (fabs((double) green-max) < MagickEpsilon)
+ *hue=(double) (2.0+(blue-red)/delta);
+ else
+ if (fabs((double) blue-max) < MagickEpsilon)
+ *hue=(double) (4.0+(red-green)/delta);
+ *hue/=6.0;
+ if (*hue < 0.0)
+ *hue+=1.0;
}
static void HSBComposite(const double hue,const double saturation,
- const double brightness,MagickRealType *red,MagickRealType *green,
- MagickRealType *blue)
+ const double brightness,double *red,double *green,double *blue)
{
- MagickRealType
+ double
f,
h,
p,
/*
Convert HSB to RGB colorspace.
*/
- assert(red != (MagickRealType *) NULL);
- assert(green != (MagickRealType *) NULL);
- assert(blue != (MagickRealType *) NULL);
+ assert(red != (double *) NULL);
+ assert(green != (double *) NULL);
+ assert(blue != (double *) NULL);
if (saturation == 0.0)
{
- *red=(MagickRealType) QuantumRange*brightness;
+ *red=(double) QuantumRange*brightness;
*green=(*red);
*blue=(*red);
return;
case 0:
default:
{
- *red=(MagickRealType) QuantumRange*brightness;
- *green=(MagickRealType) QuantumRange*t;
- *blue=(MagickRealType) QuantumRange*p;
+ *red=(double) QuantumRange*brightness;
+ *green=(double) QuantumRange*t;
+ *blue=(double) QuantumRange*p;
break;
}
case 1:
{
- *red=(MagickRealType) QuantumRange*q;
- *green=(MagickRealType) QuantumRange*brightness;
- *blue=(MagickRealType) QuantumRange*p;
+ *red=(double) QuantumRange*q;
+ *green=(double) QuantumRange*brightness;
+ *blue=(double) QuantumRange*p;
break;
}
case 2:
{
- *red=(MagickRealType) QuantumRange*p;
- *green=(MagickRealType) QuantumRange*brightness;
- *blue=(MagickRealType) QuantumRange*t;
+ *red=(double) QuantumRange*p;
+ *green=(double) QuantumRange*brightness;
+ *blue=(double) QuantumRange*t;
break;
}
case 3:
{
- *red=(MagickRealType) QuantumRange*p;
- *green=(MagickRealType) QuantumRange*q;
- *blue=(MagickRealType) QuantumRange*brightness;
+ *red=(double) QuantumRange*p;
+ *green=(double) QuantumRange*q;
+ *blue=(double) QuantumRange*brightness;
break;
}
case 4:
{
- *red=(MagickRealType) QuantumRange*t;
- *green=(MagickRealType) QuantumRange*p;
- *blue=(MagickRealType) QuantumRange*brightness;
+ *red=(double) QuantumRange*t;
+ *green=(double) QuantumRange*p;
+ *blue=(double) QuantumRange*brightness;
break;
}
case 5:
{
- *red=(MagickRealType) QuantumRange*brightness;
- *green=(MagickRealType) QuantumRange*p;
- *blue=(MagickRealType) QuantumRange*q;
+ *red=(double) QuantumRange*brightness;
+ *green=(double) QuantumRange*p;
+ *blue=(double) QuantumRange*q;
break;
}
}
}
-MagickExport MagickBooleanType CompositeImage(Image *image,
- const CompositeOperator compose,const Image *composite_image,
- const ssize_t x_offset,const ssize_t y_offset)
+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);
+}
+
+static MagickBooleanType CompositeOverImage(Image *image,
+ const Image *composite_image,const MagickBooleanType clip_to_self,
+ const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
{
#define CompositeImageTag "Composite/Image"
*composite_view,
*image_view;
- const char
- *value;
+ MagickBooleanType
+ status;
- double
- sans;
+ MagickOffsetType
+ progress;
+
+ ssize_t
+ y;
+
+ /*
+ Composite image.
+ */
+ status=MagickTrue;
+ progress=0;
+ composite_view=AcquireVirtualCacheView(composite_image,exception);
+ image_view=AcquireAuthenticCacheView(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static,4) shared(progress,status)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ const Quantum
+ *pixels;
+
+ register const Quantum
+ *restrict p;
- ExceptionInfo
- *exception;
+ register Quantum
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ size_t
+ channels;
+
+ if (status == MagickFalse)
+ continue;
+ if (clip_to_self != 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++)
+ {
+ MagickRealType
+ alpha,
+ Da,
+ Dc,
+ gamma,
+ Sa,
+ Sc;
+
+ register ssize_t
+ i;
+
+ if (clip_to_self != 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];
+
+ /*
+ Virtual composite:
+ Sc: source color.
+ Dc: destination color.
+ */
+ if (GetPixelMask(image,q) != 0)
+ {
+ q+=GetPixelChannels(image);
+ continue;
+ }
+ (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;
+ }
+ /*
+ Authentic composite:
+ Sa: normalized source alpha.
+ Da: normalized destination alpha.
+ */
+ if (GetPixelMask(composite_image,p) != 0)
+ {
+ p+=GetPixelChannels(composite_image);
+ channels=GetPixelChannels(composite_image);
+ if (p >= (pixels+channels*composite_image->columns))
+ p=pixels;
+ q+=GetPixelChannels(image);
+ continue;
+ }
+ 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);
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#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);
+}
+
+MagickExport MagickBooleanType CompositeImage(Image *image,
+ const Image *composite_image,const CompositeOperator compose,
+ const MagickBooleanType clip_to_self,const ssize_t x_offset,
+ const ssize_t y_offset,ExceptionInfo *exception)
+{
+#define CompositeImageTag "Composite/Image"
+
+ CacheView
+ *composite_view,
+ *image_view;
GeometryInfo
geometry_info;
*destination_image;
MagickBooleanType
- modify_outside_overlay,
status;
MagickOffsetType
progress;
- PixelInfo
- zero;
-
MagickRealType
amount,
destination_dissolve,
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) == MagickFalse)
+ if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
return(MagickFalse);
- GetPixelInfo(image,&zero);
+ if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
+ (IsGrayColorspace(composite_image->colorspace) == MagickFalse))
+ (void) TransformImageColorspace(image,sRGBColorspace,exception);
+ if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
+ {
+ status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
+ y_offset,exception);
+ return(status);
+ }
destination_image=(Image *) NULL;
amount=0.5;
destination_dissolve=1.0;
- modify_outside_overlay=MagickFalse;
percent_brightness=100.0;
percent_saturation=100.0;
source_dissolve=1.0;
threshold=0.05f;
switch (compose)
{
- case ClearCompositeOp:
- case SrcCompositeOp:
- case InCompositeOp:
- case SrcInCompositeOp:
- case OutCompositeOp:
- case SrcOutCompositeOp:
- case DstInCompositeOp:
- case DstAtopCompositeOp:
- {
- /*
- Modify destination outside the overlaid region.
- */
- modify_outside_overlay=MagickTrue;
- break;
- }
case CopyCompositeOp:
{
if ((x_offset < 0) || (y_offset < 0))
if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
break;
status=MagickTrue;
- exception=(&image->exception);
- image_view=AcquireCacheView(image);
- composite_view=AcquireCacheView(composite_image);
+ composite_view=AcquireVirtualCacheView(composite_image,exception);
+ image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
-#pragma omp parallel for schedule(dynamic,4) shared(status)
+ #pragma omp parallel for schedule(static,4) shared(status)
#endif
for (y=0; y < (ssize_t) composite_image->rows; y++)
{
status=MagickFalse;
continue;
}
- for (x=0; x < (ssize_t) composite_image->columns; x++)
- {
- SetPixelRed(image,GetPixelRed(composite_image,p),q);
- SetPixelGreen(image,GetPixelGreen(composite_image,p),q);
- SetPixelBlue(image,GetPixelBlue(composite_image,p),q);
- SetPixelAlpha(image,GetPixelAlpha(composite_image,p),q);
- if (image->colorspace == CMYKColorspace)
- SetPixelBlack(image,GetPixelBlack(composite_image,p),q);
- p+=GetPixelComponents(composite_image);
- q+=GetPixelComponents(image);
+ for (x=0; x < (ssize_t) composite_image->columns; x++)
+ {
+ register ssize_t
+ i;
+
+ if (GetPixelMask(image,p) != 0)
+ {
+ p+=GetPixelChannels(composite_image);
+ q+=GetPixelChannels(image);
+ continue;
+ }
+ for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
+ {
+ PixelChannel
+ channel;
+
+ PixelTrait
+ composite_traits,
+ traits;
+
+ channel=GetPixelChannelMapChannel(composite_image,i);
+ composite_traits=GetPixelChannelMapTraits(composite_image,channel);
+ traits=GetPixelChannelMapTraits(image,channel);
+ if ((traits == UndefinedPixelTrait) ||
+ (composite_traits == UndefinedPixelTrait))
+ continue;
+ SetPixelChannel(image,channel,p[i],q);
+ }
+ p+=GetPixelChannels(composite_image);
+ q+=GetPixelChannels(image);
}
sync=SyncCacheViewAuthenticPixels(image_view,exception);
if (sync == MagickFalse)
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
-#pragma omp critical (MagickCore_CompositeImage)
+ #pragma omp critical (MagickCore_CompositeImage)
#endif
proceed=SetImageProgress(image,CompositeImageTag,
(MagickOffsetType) y,image->rows);
image_view=DestroyCacheView(image_view);
return(status);
}
- case CopyOpacityCompositeOp:
+ case CopyAlphaCompositeOp:
case ChangeMaskCompositeOp:
+ case IntensityCompositeOp:
{
/*
Modify destination outside the overlaid region and require an alpha
channel to exist, to add transparency.
*/
if (image->matte == MagickFalse)
- (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
- modify_outside_overlay=MagickTrue;
+ {
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
+ image->matte=MagickFalse;
+ }
break;
}
case BlurCompositeOp:
*composite_view,
*destination_view;
+ const char
+ *value;
+
PixelInfo
pixel;
blur;
/*
+ Blur Image by resampling.
+
Blur Image dictated by an overlay gradient map: X = red_channel;
Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
*/
destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
- &image->exception);
+ exception);
if (destination_image == (Image *) NULL)
return(MagickFalse);
/*
- Determine the horizontal and vertical maximim blur.
+ Gather the maximum blur sigma values from user.
*/
SetGeometryInfo(&geometry_info);
flags=NoValue;
value=GetImageArtifact(composite_image,"compose:args");
if (value != (char *) NULL)
flags=ParseGeometry(value,&geometry_info);
- if ((flags & WidthValue) == 0 )
- {
+ if ((flags & WidthValue) == 0 ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"InvalidSetting","'%s' '%s'",
+ "compose:args",value);
destination_image=DestroyImage(destination_image);
return(MagickFalse);
}
- width=geometry_info.rho;
- height=geometry_info.sigma;
- blur.x1=geometry_info.rho;
+ /*
+ Users input sigma now needs to be converted to the EWA ellipse size.
+ The filter defaults to a sigma of 0.5 so to make this match the
+ users input the ellipse size needs to be doubled.
+ */
+ width=height=geometry_info.rho*2.0;
+ if ((flags & HeightValue) != 0 )
+ height=geometry_info.sigma*2.0;
+
+ /* default the unrotated ellipse width and height axis vectors */
+ blur.x1=width;
blur.x2=0.0;
blur.y1=0.0;
- blur.y2=geometry_info.sigma;
- angle_start=0.0;
- angle_range=0.0;
- if ((flags & HeightValue) == 0)
- blur.y2=blur.x1;
+ blur.y2=height;
+ /* rotate vectors if a rotation angle is given */
if ((flags & XValue) != 0 )
{
MagickRealType
blur.y1=(-height*sin(angle));
blur.y2=height*cos(angle);
}
+ /* Otherwise lets set a angle range and calculate in the loop */
+ angle_start=0.0;
+ angle_range=0.0;
if ((flags & YValue) != 0 )
{
angle_start=DegreesToRadians(geometry_info.xi);
angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
}
/*
- Blur Image by resampling.
+ Set up a gaussian cylindrical filter for EWA Bluring.
+
+ As the minimum ellipse radius of support*1.0 the EWA algorithm
+ can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
+ This means that even 'No Blur' will be still a little blurry!
+
+ The solution (as well as the problem of preventing any user
+ expert filter settings, is to set our own user settings, then
+ restore them afterwards.
*/
- pixel=zero;
- exception=(&image->exception);
- resample_filter=AcquireResampleFilter(image,&image->exception);
- SetResampleFilter(resample_filter,CubicFilter,2.0);
- destination_view=AcquireCacheView(destination_image);
- composite_view=AcquireCacheView(composite_image);
+ resample_filter=AcquireResampleFilter(image,exception);
+ SetResampleFilter(resample_filter,GaussianFilter);
+
+ /* do the variable blurring of each pixel in image */
+ GetPixelInfo(image,&pixel);
+ composite_view=AcquireVirtualCacheView(composite_image,exception);
+ destination_view=AcquireAuthenticCacheView(destination_image,exception);
for (y=0; y < (ssize_t) composite_image->rows; y++)
{
MagickBooleanType
p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1,exception);
q=QueueCacheViewAuthenticPixels(destination_view,0,y,
- destination_image->columns,1,&image->exception);
+ destination_image->columns,1,exception);
if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
break;
for (x=0; x < (ssize_t) composite_image->columns; x++)
{
if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
{
- p+=GetPixelComponents(composite_image);
+ p+=GetPixelChannels(composite_image);
continue;
}
if (fabs(angle_range) > MagickEpsilon)
blur.y1=(-height*sin(angle));
blur.y2=height*cos(angle);
}
- ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
- GetPixelRed(composite_image,p),blur.y1*QuantumScale*
- GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
- GetPixelRed(composite_image,p),blur.y2*QuantumScale*
- GetPixelGreen(composite_image,p));
+#if 0
+ if ( x == 10 && y == 60 ) {
+ fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
+ blur.x1, blur.x2, blur.y1, blur.y2);
+ fprintf(stderr, "scaled by=%lf,%lf\n",
+ QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
+#endif
+ ScaleResampleFilter(resample_filter,
+ blur.x1*QuantumScale*GetPixelRed(composite_image,p),
+ blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
+ blur.x2*QuantumScale*GetPixelRed(composite_image,p),
+ blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
(void) ResamplePixelColor(resample_filter,(double) x_offset+x,
- (double) y_offset+y,&pixel);
- SetPixelPixelInfo(destination_image,&pixel,q);
- p+=GetPixelComponents(composite_image);
- q+=GetPixelComponents(destination_image);
+ (double) y_offset+y,&pixel,exception);
+ SetPixelInfoPixel(destination_image,&pixel,q);
+ p+=GetPixelChannels(composite_image);
+ q+=GetPixelChannels(destination_image);
}
sync=SyncCacheViewAuthenticPixels(destination_view,exception);
if (sync == MagickFalse)
*destination_view,
*image_view;
+ const char
+ *value;
+
PixelInfo
pixel;
compose:args = x_scale[,y_scale[,center.x,center.y]]
*/
destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
- &image->exception);
+ exception);
if (destination_image == (Image *) NULL)
return(MagickFalse);
SetGeometryInfo(&geometry_info);
Shift the pixel offset point as defined by the provided,
displacement/distortion map. -- Like a lens...
*/
- pixel=zero;
- exception=(&image->exception);
- image_view=AcquireCacheView(image);
- destination_view=AcquireCacheView(destination_image);
- composite_view=AcquireCacheView(composite_image);
+ GetPixelInfo(image,&pixel);
+ image_view=AcquireVirtualCacheView(image,exception);
+ composite_view=AcquireVirtualCacheView(composite_image,exception);
+ destination_view=AcquireAuthenticCacheView(destination_image,exception);
for (y=0; y < (ssize_t) composite_image->rows; y++)
{
MagickBooleanType
p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1,exception);
q=QueueCacheViewAuthenticPixels(destination_view,0,y,
- destination_image->columns,1,&image->exception);
+ destination_image->columns,1,exception);
if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
break;
for (x=0; x < (ssize_t) composite_image->columns; x++)
{
if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
{
- p+=GetPixelComponents(composite_image);
+ p+=GetPixelChannels(composite_image);
continue;
}
/*
Mask with the 'invalid pixel mask' in alpha channel.
*/
pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
- pixel.alpha)*(1.0-QuantumScale*
- GetPixelAlpha(composite_image,p)));
- SetPixelPixelInfo(destination_image,&pixel,q);
- p+=GetPixelComponents(composite_image);
- q+=GetPixelComponents(destination_image);
+ pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
+ SetPixelInfoPixel(destination_image,&pixel,q);
+ p+=GetPixelChannels(composite_image);
+ q+=GetPixelChannels(destination_image);
}
sync=SyncCacheViewAuthenticPixels(destination_view,exception);
if (sync == MagickFalse)
}
case DissolveCompositeOp:
{
+ const char
+ *value;
+
/*
Geometry arguments to dissolve factors.
*/
destination_dissolve=geometry_info.sigma/100.0;
if ((destination_dissolve-MagickEpsilon) < 0.0)
destination_dissolve=0.0;
- modify_outside_overlay=MagickTrue;
+ /* posible speed up? -- from IMv6 update
+ clip_to_self=MagickFalse;
if ((destination_dissolve+MagickEpsilon) > 1.0 )
{
destination_dissolve=1.0;
- modify_outside_overlay=MagickFalse;
+ clip_to_self=MagickTrue;
}
+ */
}
break;
}
case BlendCompositeOp:
{
+ const char
+ *value;
+
value=GetImageArtifact(composite_image,"compose:args");
if (value != (char *) NULL)
{
destination_dissolve=1.0-source_dissolve;
if ((flags & SigmaValue) != 0)
destination_dissolve=geometry_info.sigma/100.0;
- modify_outside_overlay=MagickTrue;
- if ((destination_dissolve+MagickEpsilon) > 1.0)
- modify_outside_overlay=MagickFalse;
}
break;
}
case MathematicsCompositeOp:
{
+ const char
+ *value;
+
/*
Just collect the values from "compose:args", setting.
Unused values are set to zero automagically.
}
case ModulateCompositeOp:
{
+ const char
+ *value;
+
/*
Determine the brightness and saturation scale.
*/
}
case ThresholdCompositeOp:
{
+ const char
+ *value;
+
/*
Determine the amount and threshold.
*/
default:
break;
}
- value=GetImageArtifact(composite_image,"compose:outside-overlay");
- if (value != (const char *) NULL)
- modify_outside_overlay=IsMagickTrue(value);
/*
Composite image.
*/
status=MagickTrue;
progress=0;
midpoint=((MagickRealType) QuantumRange+1.0)/2;
- GetPixelInfo(composite_image,&zero);
- exception=(&image->exception);
- image_view=AcquireCacheView(image);
- composite_view=AcquireCacheView(composite_image);
+ composite_view=AcquireVirtualCacheView(composite_image,exception);
+ image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+ #pragma omp parallel for schedule(static,4) shared(progress,status)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
*pixels;
double
+ blue,
brightness,
+ green,
hue,
+ red,
saturation;
PixelInfo
- composite,
- destination,
- source;
+ destination_pixel,
+ source_pixel;
register const Quantum
*restrict p;
if (status == MagickFalse)
continue;
- if (modify_outside_overlay == MagickFalse)
+ if (clip_to_self != MagickFalse)
{
if (y < y_offset)
continue;
}
pixels=p;
if (x_offset < 0)
- p-=x_offset*GetPixelComponents(composite_image);
+ p-=x_offset*GetPixelChannels(composite_image);
}
q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
- if (q == (const Quantum *) NULL)
+ if (q == (Quantum *) NULL)
{
status=MagickFalse;
continue;
}
- source=zero;
- destination=zero;
hue=0.0;
saturation=0.0;
brightness=0.0;
+ GetPixelInfo(image,&destination_pixel);
+ GetPixelInfo(composite_image,&source_pixel);
for (x=0; x < (ssize_t) image->columns; x++)
{
- if (modify_outside_overlay == MagickFalse)
+ MagickRealType
+ alpha,
+ Da,
+ Dc,
+ Dca,
+ gamma,
+ Sa,
+ Sc,
+ Sca;
+
+ register ssize_t
+ i;
+
+ size_t
+ channels;
+
+ if (clip_to_self != MagickFalse)
{
if (x < x_offset)
{
- q+=GetPixelComponents(image);
+ q+=GetPixelChannels(image);
continue;
}
if ((x-x_offset) >= (ssize_t) composite_image->columns)
break;
}
- 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);
- /*
- Handle destination modifications outside overlaid region.
- */
- composite=destination;
if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
((x-x_offset) >= (ssize_t) composite_image->columns))
{
- switch (compose)
- {
- case DissolveCompositeOp:
- case BlendCompositeOp:
- {
- composite.alpha=destination_dissolve*(composite.alpha);
- break;
- }
- case ClearCompositeOp:
- case SrcCompositeOp:
- {
- CompositeClear(&destination,&composite);
- break;
- }
- case InCompositeOp:
- case SrcInCompositeOp:
- case OutCompositeOp:
- case SrcOutCompositeOp:
- case DstInCompositeOp:
- case DstAtopCompositeOp:
- case CopyOpacityCompositeOp:
- case ChangeMaskCompositeOp:
+ Quantum
+ source[MaxPixelChannels];
+
+ /*
+ Virtual composite:
+ Sc: source color.
+ Dc: destination color.
+ */
+ (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
+ source,exception);
+ if (GetPixelMask(image,q) != 0)
{
- composite.alpha=(MagickRealType) TransparentAlpha;
- break;
+ q+=GetPixelChannels(image);
+ continue;
}
- default:
+ 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)
{
- (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,y-
- y_offset,&composite,exception);
- break;
+ case AlphaCompositeOp:
+ case ChangeMaskCompositeOp:
+ case CopyAlphaCompositeOp:
+ case DstAtopCompositeOp:
+ case DstInCompositeOp:
+ case InCompositeOp:
+ case IntensityCompositeOp:
+ 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 BlendCompositeOp:
+ 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);
}
- 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->matte != MagickFalse)
- SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
- if (image->colorspace == CMYKColorspace)
- SetPixelBlack(image,ClampToQuantum(composite.black),q);
- q+=GetPixelComponents(image);
+ q+=GetPixelChannels(image);
continue;
}
/*
- Handle normal overlay of source onto destination.
- */
- 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)
- {
- source.red=(MagickRealType) QuantumRange-source.red;
- source.green=(MagickRealType) QuantumRange-source.green;
- source.blue=(MagickRealType) QuantumRange-source.blue;
- source.black=(MagickRealType) QuantumRange-source.black;
- }
- if (composite_image->matte != MagickFalse)
- source.alpha=(MagickRealType) GetPixelAlpha(composite_image,p);
- /*
- Porter-Duff compositions.
+ Authentic composite:
+ Sa: normalized source alpha.
+ Da: normalized destination alpha.
*/
+ Sa=QuantumScale*GetPixelAlpha(composite_image,p);
+ Da=QuantumScale*GetPixelAlpha(image,q);
switch (compose)
{
- case ClearCompositeOp:
- {
- CompositeClear(&destination,&composite);
- break;
- }
- case SrcCompositeOp:
- case CopyCompositeOp:
- case ReplaceCompositeOp:
- {
- composite=source;
- break;
- }
- case NoCompositeOp:
- case DstCompositeOp:
- break;
- case OverCompositeOp:
- case SrcOverCompositeOp:
- {
- CompositePixelInfoOver(&source,source.alpha,&destination,
- destination.alpha,&composite);
- break;
- }
- case DstOverCompositeOp:
- {
- CompositePixelInfoOver(&destination,destination.alpha,&source,
- source.alpha,&composite);
- break;
- }
- case SrcInCompositeOp:
- case InCompositeOp:
- {
- CompositeIn(&source,&destination,&composite);
- break;
- }
- case DstInCompositeOp:
- {
- CompositeIn(&destination,&source,&composite);
- break;
- }
- case OutCompositeOp:
- case SrcOutCompositeOp:
- {
- CompositeOut(&source,&destination,&composite);
- break;
- }
- case DstOutCompositeOp:
- {
- CompositeOut(&destination,&source,&composite);
- break;
- }
- case AtopCompositeOp:
- case SrcAtopCompositeOp:
- {
- CompositeAtop(&source,&destination,&composite);
- break;
- }
- case DstAtopCompositeOp:
- {
- CompositeAtop(&destination,&source,&composite);
- break;
- }
- case XorCompositeOp:
- {
- CompositeXor(&source,&destination,&composite);
- break;
- }
- case PlusCompositeOp:
+ case BumpmapCompositeOp:
{
- CompositePlus(image,&source,&destination,&composite);
+ 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 MathematicsCompositeOp:
case MinusDstCompositeOp:
- {
- CompositeMinus(image,&source,&destination,&composite);
- break;
- }
case MinusSrcCompositeOp:
- {
- CompositeMinus(image,&destination,&source,&composite);
- break;
- }
case ModulusAddCompositeOp:
- {
- CompositeModulusAdd(image,&source,&destination,&composite);
- break;
- }
case ModulusSubtractCompositeOp:
- {
- CompositeModulusSubtract(image,&source,&destination,&composite);
- break;
- }
- case DifferenceCompositeOp:
- {
- CompositeDifference(image,&source,&destination,&composite);
- break;
- }
- case ExclusionCompositeOp:
- {
- CompositeExclusion(image,&source,&destination,&composite);
- break;
- }
case MultiplyCompositeOp:
- {
- CompositeMultiply(image,&source,&destination,&composite);
- break;
- }
- case ScreenCompositeOp:
- {
- CompositeScreen(image,&source,&destination,&composite);
- break;
- }
- case DivideDstCompositeOp:
- {
- CompositeDivide(image,&source,&destination,&composite);
- break;
- }
- case DivideSrcCompositeOp:
- {
- CompositeDivide(image,&destination,&source,&composite);
- break;
- }
- case DarkenCompositeOp:
- {
- CompositeDarken(image,&source,&destination,&composite);
- break;
- }
- case LightenCompositeOp:
- {
- CompositeLighten(image,&source,&destination,&composite);
- break;
- }
- case DarkenIntensityCompositeOp:
- {
- CompositeDarkenIntensity(image,&source,&destination,&composite);
- 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);
- break;
- }
- case ColorBurnCompositeOp:
- {
- CompositeColorBurn(&source,&destination,&composite);
- break;
- }
- case LinearDodgeCompositeOp:
- {
- CompositeLinearDodge(&source,&destination,&composite);
- break;
- }
- case LinearBurnCompositeOp:
- {
- CompositeLinearBurn(&source,&destination,&composite);
- break;
- }
- case HardLightCompositeOp:
- {
- CompositeHardLight(&source,&destination,&composite);
- break;
- }
case OverlayCompositeOp:
- {
- CompositeHardLight(&destination,&source,&composite);
- break;
- }
- case SoftLightCompositeOp:
- {
- CompositeSoftLight(&source,&destination,&composite);
- break;
- }
- case LinearLightCompositeOp:
- {
- CompositeLinearLight(&source,&destination,&composite);
- break;
- }
case PegtopLightCompositeOp:
- {
- CompositePegtopLight(&source,&destination,&composite);
- break;
- }
- case VividLightCompositeOp:
- {
- CompositeVividLight(&source,&destination,&composite);
- break;
- }
case PinLightCompositeOp:
+ case ScreenCompositeOp:
+ case SoftLightCompositeOp:
+ case VividLightCompositeOp:
{
- CompositePinLight(&source,&destination,&composite);
- break;
- }
- case ChangeMaskCompositeOp:
- {
- if ((composite.alpha > ((MagickRealType) QuantumRange/2.0)) ||
- (IsFuzzyEquivalencePixelInfo(&source,&destination) != MagickFalse))
- composite.alpha=(MagickRealType) TransparentAlpha;
- else
- composite.alpha=(MagickRealType) OpaqueAlpha;
- break;
- }
- case BumpmapCompositeOp:
- {
- if (source.alpha == TransparentAlpha)
- break;
- CompositeBumpmap(&source,&destination,&composite);
- break;
- }
- case DissolveCompositeOp:
- {
- CompositePixelInfoOver(&source,source_dissolve*source.alpha,
- &destination,(MagickRealType) (destination_dissolve*
- destination.alpha),&composite);
- break;
- }
- case BlendCompositeOp:
- {
- CompositePixelInfoBlend(&source,source_dissolve,&destination,
- destination_dissolve,&composite);
- break;
- }
- case ThresholdCompositeOp:
- {
- CompositeThreshold(&source,&destination,threshold,amount,&composite);
- break;
- }
- case ModulateCompositeOp:
- {
- ssize_t
- offset;
-
- 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,&composite.red,
- &composite.green,&composite.blue);
+ alpha=RoundToUnity(Sa+Da-Sa*Da);
break;
}
- case HueCompositeOp:
+ case DarkenCompositeOp:
+ case DstAtopCompositeOp:
+ case DstInCompositeOp:
+ case InCompositeOp:
+ case LightenCompositeOp:
+ case SrcInCompositeOp:
{
- if (source.alpha == TransparentAlpha)
- break;
- if (destination.alpha == TransparentAlpha)
- {
- composite=source;
- 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;
+ alpha=Sa*Da;
break;
}
- case SaturateCompositeOp:
- {
- if (source.alpha == TransparentAlpha)
- break;
- if (destination.alpha == TransparentAlpha)
- {
- composite=source;
- 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;
+ case DissolveCompositeOp:
+ {
+ alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
+ Sa+destination_dissolve*Da;
break;
}
- case LuminizeCompositeOp:
+ case DstOverCompositeOp:
{
- if (source.alpha == TransparentAlpha)
- break;
- if (destination.alpha == TransparentAlpha)
- {
- composite=source;
- 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;
+ alpha=Da*(-Sa)+Da+Sa;
break;
}
- case ColorizeCompositeOp:
+ case DstOutCompositeOp:
{
- if (source.alpha == TransparentAlpha)
- break;
- if (destination.alpha == TransparentAlpha)
- {
- composite=source;
- 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;
+ alpha=Da*(1.0-Sa);
break;
}
- case CopyRedCompositeOp:
- case CopyCyanCompositeOp:
+ case OutCompositeOp:
+ case SrcOutCompositeOp:
{
- composite.red=source.red;
+ alpha=Sa*(1.0-Da);
break;
}
- case CopyGreenCompositeOp:
- case CopyMagentaCompositeOp:
+ case OverCompositeOp:
+ case SrcOverCompositeOp:
{
- composite.green=source.green;
+ alpha=Sa*(-Da)+Sa+Da;
break;
}
- case CopyBlueCompositeOp:
- case CopyYellowCompositeOp:
+ case BlendCompositeOp:
+ case PlusCompositeOp:
{
- composite.blue=source.blue;
+ alpha=RoundToUnity(Sa+Da);
break;
}
- case CopyOpacityCompositeOp:
+ case XorCompositeOp:
{
- if (source.matte == MagickFalse)
- {
- composite.alpha=(MagickRealType) GetPixelInfoIntensity(&source);
- break;
- }
- composite.alpha=source.alpha;
+ alpha=Sa+Da-2.0*Sa*Da;
break;
}
- case CopyBlackCompositeOp:
+ default:
{
- if (source.colorspace != CMYKColorspace)
- ConvertRGBToCMYK(&source);
- composite.black=source.black;
+ alpha=1.0;
break;
}
- case BlurCompositeOp:
- case DisplaceCompositeOp:
- case DistortCompositeOp:
+ }
+ if (GetPixelMask(image,p) != 0)
+ {
+ p+=GetPixelChannels(composite_image);
+ q+=GetPixelChannels(image);
+ continue;
+ }
+ switch (compose)
+ {
+ case ColorizeCompositeOp:
+ case HueCompositeOp:
+ case LuminizeCompositeOp:
+ case ModulateCompositeOp:
+ case SaturateCompositeOp:
{
- composite=source;
+ GetPixelInfoPixel(composite_image,p,&source_pixel);
+ GetPixelInfoPixel(image,q,&destination_pixel);
break;
}
default:
break;
}
- if (image->colorspace == CMYKColorspace)
+ for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+ {
+ double
+ sans;
+
+ 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)
+ continue;
+ if ((compose != IntensityCompositeOp) &&
+ (composite_traits == UndefinedPixelTrait))
+ continue;
+ /*
+ Sc: source color.
+ Dc: destination color.
+ */
+ Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
+ Dc=(MagickRealType) q[i];
+ if ((traits & CopyPixelTrait) != 0)
+ {
+ if (channel != AlphaPixelChannel)
+ {
+ /*
+ Copy channel.
+ */
+ q[i]=ClampToQuantum(Sc);
+ continue;
+ }
+ /*
+ Set alpha channel.
+ */
+ switch (compose)
+ {
+ case AlphaCompositeOp:
+ {
+ pixel=QuantumRange*Sa;
+ break;
+ }
+ case AtopCompositeOp:
+ case CopyBlackCompositeOp:
+ case CopyBlueCompositeOp:
+ case CopyCyanCompositeOp:
+ case CopyGreenCompositeOp:
+ case CopyMagentaCompositeOp:
+ case CopyRedCompositeOp:
+ case CopyYellowCompositeOp:
+ case SrcAtopCompositeOp:
+ case DstCompositeOp:
+ case NoCompositeOp:
+ {
+ pixel=QuantumRange*Da;
+ break;
+ }
+ case ChangeMaskCompositeOp:
+ {
+ MagickBooleanType
+ equivalent;
+
+ if (Da > ((MagickRealType) QuantumRange/2.0))
+ {
+ pixel=(MagickRealType) TransparentAlpha;
+ break;
+ }
+ equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
+ if (equivalent != MagickFalse)
+ {
+ pixel=(MagickRealType) TransparentAlpha;
+ break;
+ }
+ pixel=(MagickRealType) OpaqueAlpha;
+ break;
+ }
+ case ClearCompositeOp:
+ {
+ pixel=(MagickRealType) TransparentAlpha;
+ break;
+ }
+ case ColorizeCompositeOp:
+ case HueCompositeOp:
+ case LuminizeCompositeOp:
+ case SaturateCompositeOp:
+ {
+ if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=QuantumRange*Da;
+ break;
+ }
+ if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=QuantumRange*Sa;
+ break;
+ }
+ if (Sa < Da)
+ {
+ pixel=QuantumRange*Da;
+ break;
+ }
+ pixel=QuantumRange*Sa;
+ break;
+ }
+ case CopyCompositeOp:
+ case CopyAlphaCompositeOp:
+ case DisplaceCompositeOp:
+ case DistortCompositeOp:
+ case DstAtopCompositeOp:
+ case ReplaceCompositeOp:
+ case SrcCompositeOp:
+ {
+ pixel=QuantumRange*Sa;
+ break;
+ }
+ case DarkenIntensityCompositeOp:
+ {
+ pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
+ (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
+ break;
+ }
+ case IntensityCompositeOp:
+ {
+ pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
+ break;
+ }
+ case LightenIntensityCompositeOp:
+ {
+ pixel=Sa*GetPixelIntensity(composite_image,p) >
+ Da*GetPixelIntensity(image,q) ? Sa : Da;
+ break;
+ }
+ case ModulateCompositeOp:
+ {
+ if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=QuantumRange*Da;
+ break;
+ }
+ pixel=QuantumRange*Da;
+ break;
+ }
+ default:
+ {
+ pixel=QuantumRange*alpha;
+ break;
+ }
+ }
+ q[i]=ClampToQuantum(pixel);
+ continue;
+ }
+ /*
+ Porter-Duff compositions:
+ Sca: source normalized color multiplied by alpha.
+ Dca: normalized destination color multiplied by alpha.
+ */
+ Sca=QuantumScale*Sa*Sc;
+ Dca=QuantumScale*Da*Dc;
+ switch (compose)
+ {
+ case DarkenCompositeOp:
+ case LightenCompositeOp:
+ case ModulusSubtractCompositeOp:
+ {
+ gamma=1.0-alpha;
+ break;
+ }
+ default:
+ break;
+ }
+ gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
+ pixel=Dc;
+ switch (compose)
{
- composite.red=(MagickRealType) QuantumRange-composite.red;
- composite.green=(MagickRealType) QuantumRange-composite.green;
- composite.blue=(MagickRealType) QuantumRange-composite.blue;
- composite.black=(MagickRealType) QuantumRange-composite.black;
+ case AlphaCompositeOp:
+ {
+ pixel=QuantumRange*Sa;
+ break;
+ }
+ case AtopCompositeOp:
+ case SrcAtopCompositeOp:
+ {
+ pixel=Sc*Sa+Dc*(1.0-Sa);
+ break;
+ }
+ 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:
+ {
+ pixel=Sc;
+ break;
+ }
+ case BumpmapCompositeOp:
+ {
+ if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=Dc;
+ break;
+ }
+ pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
+ break;
+ }
+ case ChangeMaskCompositeOp:
+ {
+ pixel=Dc;
+ break;
+ }
+ case ClearCompositeOp:
+ {
+ pixel=0.0;
+ break;
+ }
+ case ColorBurnCompositeOp:
+ {
+ /*
+ Refer to the March 2009 SVG specification.
+ */
+ if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
+ {
+ pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
+ break;
+ }
+ if (Sca < MagickEpsilon)
+ {
+ pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
+ break;
+ }
+ pixel=QuantumRange*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=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
+ break;
+ }
+ if (fabs(Sca-Sa) < MagickEpsilon)
+ {
+ pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ break;
+ }
+ pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
+ (1.0-Sa));
+ break;
+ }
+ case ColorizeCompositeOp:
+ {
+ if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=Dc;
+ break;
+ }
+ if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=Sc;
+ break;
+ }
+ CompositeHSB(destination_pixel.red,destination_pixel.green,
+ destination_pixel.blue,&sans,&sans,&brightness);
+ CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
+ &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;
+ default: pixel=Dc; break;
+ }
+ break;
+ }
+ case CopyAlphaCompositeOp:
+ case IntensityCompositeOp:
+ {
+ if (channel == AlphaPixelChannel)
+ pixel=(MagickRealType) GetPixelAlpha(composite_image,p);
+ break;
+ }
+ case CopyBlackCompositeOp:
+ {
+ if (channel == BlackPixelChannel)
+ pixel=(MagickRealType) GetPixelBlack(composite_image,p);
+ break;
+ }
+ case CopyBlueCompositeOp:
+ case CopyYellowCompositeOp:
+ {
+ if (channel == BluePixelChannel)
+ pixel=(MagickRealType) GetPixelBlue(composite_image,p);
+ break;
+ }
+ case CopyGreenCompositeOp:
+ case CopyMagentaCompositeOp:
+ {
+ if (channel == GreenPixelChannel)
+ pixel=(MagickRealType) GetPixelGreen(composite_image,p);
+ break;
+ }
+ case CopyRedCompositeOp:
+ case CopyCyanCompositeOp:
+ {
+ if (channel == RedPixelChannel)
+ pixel=(MagickRealType) GetPixelRed(composite_image,p);
+ break;
+ }
+ case DarkenCompositeOp:
+ {
+ /*
+ 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 DarkenIntensityCompositeOp:
+ {
+ pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
+ (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
+ break;
+ }
+ case DifferenceCompositeOp:
+ {
+ pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
+ break;
+ }
+ case DissolveCompositeOp:
+ {
+ pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
+ destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
+ break;
+ }
+ case DivideDstCompositeOp:
+ {
+ if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
+ {
+ pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
+ break;
+ }
+ if (fabs(Dca) < MagickEpsilon)
+ {
+ pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ break;
+ }
+ pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ break;
+ }
+ case DivideSrcCompositeOp:
+ {
+ if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
+ {
+ pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
+ break;
+ }
+ if (fabs(Sca) < MagickEpsilon)
+ {
+ pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
+ break;
+ }
+ pixel=QuantumRange*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=QuantumRange*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=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
+ (1.0-Sa));
+ break;
+ }
+ pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
+ Dca*(1.0-Sa));
+ break;
+ }
+ case HueCompositeOp:
+ {
+ if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=Dc;
+ break;
+ }
+ if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=Sc;
+ break;
+ }
+ CompositeHSB(destination_pixel.red,destination_pixel.green,
+ destination_pixel.blue,&hue,&saturation,&brightness);
+ CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
+ &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;
+ default: pixel=Dc; break;
+ }
+ break;
+ }
+ case InCompositeOp:
+ case SrcInCompositeOp:
+ {
+ pixel=gamma*(Da*Sc*Da);
+ break;
+ }
+ case LinearBurnCompositeOp:
+ {
+ /*
+ LinearBurn: as defined by Abode Photoshop, according to
+ http://www.simplefilter.de/en/basics/mixmods.html is:
+
+ f(Sc,Dc) = Sc + Dc - 1
+ */
+ pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
+ break;
+ }
+ case LinearDodgeCompositeOp:
+ {
+ pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
+ break;
+ }
+ case LinearLightCompositeOp:
+ {
+ /*
+ 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=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
+ break;
+ }
+ case LightenCompositeOp:
+ {
+ if (Sc > Dc)
+ {
+ pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
+ break;
+ }
+ pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
+ break;
+ }
+ case LightenIntensityCompositeOp:
+ {
+ /*
+ 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 LuminizeCompositeOp:
+ {
+ if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=Dc;
+ break;
+ }
+ if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=Sc;
+ break;
+ }
+ CompositeHSB(destination_pixel.red,destination_pixel.green,
+ destination_pixel.blue,&hue,&saturation,&brightness);
+ CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
+ &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;
+ default: pixel=Dc; break;
+ }
+ break;
+ }
+ case MathematicsCompositeOp:
+ {
+ /*
+ '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 MinusDstCompositeOp:
+ {
+ pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
+ break;
+ }
+ case MinusSrcCompositeOp:
+ {
+ /*
+ Minus source from destination.
+
+ f(Sc,Dc) = Sc - Dc
+ */
+ pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
+ break;
+ }
+ case ModulateCompositeOp:
+ {
+ ssize_t
+ offset;
+
+ if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=Dc;
+ break;
+ }
+ offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
+ if (offset == 0)
+ {
+ pixel=Dc;
+ break;
+ }
+ CompositeHSB(destination_pixel.red,destination_pixel.green,
+ destination_pixel.blue,&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 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=QuantumRange*gamma*(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=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
+ break;
+ }
+ case OverlayCompositeOp:
+ {
+ if ((2.0*Dca) < Da)
+ {
+ pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
+ (1.0-Da));
+ break;
+ }
+ pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
+ Sca*(1.0-Da));
+ break;
+ }
+ case PegtopLightCompositeOp:
+ {
+ /*
+ PegTop: A Soft-Light alternative: A continuous version of the
+ Softlight function, producing very similar results.
+
+ f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
+
+ http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
+ */
+ if (fabs(Da) < MagickEpsilon)
+ {
+ pixel=QuantumRange*gamma*(Sca);
+ break;
+ }
+ pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
+ Da)+Dca*(1.0-Sa));
+ break;
+ }
+ case PinLightCompositeOp:
+ {
+ /*
+ 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=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
+ break;
+ }
+ if ((Dca*Sa) > (2.0*Sca*Da))
+ {
+ pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
+ break;
+ }
+ pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
+ break;
+ }
+ case PlusCompositeOp:
+ {
+ pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
+ break;
+ }
+ case SaturateCompositeOp:
+ {
+ if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=Dc;
+ break;
+ }
+ if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
+ {
+ pixel=Sc;
+ break;
+ }
+ CompositeHSB(destination_pixel.red,destination_pixel.green,
+ destination_pixel.blue,&hue,&saturation,&brightness);
+ CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
+ &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;
+ default: pixel=Dc; break;
+ }
+ break;
+ }
+ case ScreenCompositeOp:
+ {
+ /*
+ Screen: a negated multiply:
+
+ f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
+ */
+ pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
+ break;
+ }
+ case SoftLightCompositeOp:
+ {
+ /*
+ Refer to the March 2009 SVG specification.
+ */
+ if ((2.0*Sca) < Sa)
+ {
+ pixel=QuantumRange*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=QuantumRange*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=QuantumRange*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 ThresholdCompositeOp:
+ {
+ MagickRealType
+ delta;
+
+ delta=Sc-Dc;
+ if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
+ {
+ pixel=gamma*Dc;
+ break;
+ }
+ pixel=gamma*(Dc+delta*amount);
+ break;
+ }
+ case VividLightCompositeOp:
+ {
+ /*
+ 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=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ break;
+ }
+ if ((2.0*Sca) <= Sa)
+ {
+ pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
+ (1.0-Da)+Dca*(1.0-Sa));
+ break;
+ }
+ pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
+ Dca*(1.0-Sa));
+ break;
+ }
+ case XorCompositeOp:
+ {
+ pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
+ break;
+ }
+ default:
+ {
+ pixel=Sc;
+ break;
+ }
}
- 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);
- p+=GetPixelComponents(composite_image);
- if (p >= (pixels+composite_image->columns*GetPixelComponents(composite_image)))
+ q[i]=ClampToQuantum(pixel);
+ }
+ p+=GetPixelChannels(composite_image);
+ channels=GetPixelChannels(composite_image);
+ if (p >= (pixels+channels*composite_image->columns))
p=pixels;
- q+=GetPixelComponents(image);
+ q+=GetPixelChannels(image);
}
if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
status=MagickFalse;
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_CompositeImage)
+ #pragma omp critical (MagickCore_CompositeImage)
#endif
proceed=SetImageProgress(image,CompositeImageTag,progress++,
image->rows);
%
% The format of the TextureImage method is:
%
-% MagickBooleanType TextureImage(Image *image,const Image *texture)
+% MagickBooleanType TextureImage(Image *image,const Image *texture,
+% ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: the image.
%
-% o texture: This image is the texture to layer on the background.
+% o texture_image: This image is the texture to layer on the background.
%
*/
-MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
+MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
+ ExceptionInfo *exception)
{
#define TextureImageTag "Texture/Image"
*image_view,
*texture_view;
- ExceptionInfo
- *exception;
+ Image
+ *texture_image;
MagickBooleanType
status;
assert(image->signature == MagickSignature);
if (texture == (const Image *) NULL)
return(MagickFalse);
- (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
- if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
return(MagickFalse);
+ texture_image=CloneImage(texture,0,0,MagickTrue,exception);
+ if (texture_image == (const Image *) NULL)
+ return(MagickFalse);
+ (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
+ exception);
status=MagickTrue;
if ((image->compose != CopyCompositeOp) &&
((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
- (texture->matte != MagickFalse)))
+ (texture_image->matte != MagickFalse)))
{
/*
Tile texture onto the image background.
*/
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
+ #pragma omp parallel for schedule(static) shared(status)
#endif
- for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
+ for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
{
register ssize_t
x;
if (status == MagickFalse)
continue;
- for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
+ for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
{
MagickBooleanType
thread_status;
- thread_status=CompositeImage(image,image->compose,texture,x+
- texture->tile_offset.x,y+texture->tile_offset.y);
+ thread_status=CompositeImage(image,texture_image,image->compose,
+ MagickFalse,x+texture_image->tile_offset.x,y+
+ texture_image->tile_offset.y,exception);
if (thread_status == MagickFalse)
{
status=thread_status;
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_TextureImage)
+ #pragma omp critical (MagickCore_TextureImage)
#endif
proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
y,image->rows);
}
(void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
image->rows,image->rows);
+ texture_image=DestroyImage(texture_image);
return(status);
}
/*
Tile texture onto the image background (optimized).
*/
status=MagickTrue;
- exception=(&image->exception);
- image_view=AcquireCacheView(image);
- texture_view=AcquireCacheView(texture);
+ texture_view=AcquireVirtualCacheView(texture_image,exception);
+ image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
+ #pragma omp parallel for schedule(static) shared(status)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
if (status == MagickFalse)
continue;
- pixels=GetCacheViewVirtualPixels(texture_view,texture->tile_offset.x,(y+
- texture->tile_offset.y) % texture->rows,texture->columns,1,exception);
- q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
- exception);
+ pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
+ (y+texture_image->tile_offset.y) % texture_image->rows,
+ texture_image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
{
status=MagickFalse;
continue;
}
- for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
+ for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
{
register ssize_t
- i;
+ j;
p=pixels;
- width=texture->columns;
+ width=texture_image->columns;
if ((x+(ssize_t) width) > (ssize_t) image->columns)
width=image->columns-x;
- for (i=0; i < (ssize_t) width; i++)
+ for (j=0; j < (ssize_t) width; j++)
{
- SetPixelRed(image,GetPixelRed(texture,p),q);
- SetPixelGreen(image,GetPixelGreen(texture,p),q);
- SetPixelBlue(image,GetPixelBlue(texture,p),q);
- SetPixelAlpha(image,GetPixelAlpha(texture,p),q);
- if ((image->colorspace == CMYKColorspace) &&
- (texture->colorspace == CMYKColorspace))
- SetPixelBlack(image,GetPixelBlack(texture,p),q);
- p+=GetPixelComponents(texture);
- q+=GetPixelComponents(image);
+ register ssize_t
+ i;
+
+ if (GetPixelMask(image,p) != 0)
+ {
+ p+=GetPixelChannels(texture_image);
+ q+=GetPixelChannels(image);
+ continue;
+ }
+ for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
+ {
+ PixelChannel
+ channel;
+
+ PixelTrait
+ texture_traits,
+ traits;
+
+ channel=GetPixelChannelMapChannel(texture_image,i);
+ texture_traits=GetPixelChannelMapTraits(texture_image,channel);
+ traits=GetPixelChannelMapTraits(image,channel);
+ if ((traits == UndefinedPixelTrait) ||
+ (texture_traits == UndefinedPixelTrait))
+ continue;
+ SetPixelChannel(image,channel,p[i],q);
+ }
+ p+=GetPixelChannels(texture_image);
+ q+=GetPixelChannels(image);
}
}
sync=SyncCacheViewAuthenticPixels(image_view,exception);
}
texture_view=DestroyCacheView(texture_view);
image_view=DestroyCacheView(image_view);
+ texture_image=DestroyImage(texture_image);
return(status);
}