diff --git a/MagickCore/composite.c b/MagickCore/composite.c
new file mode 100644
index 0000000..428d827
--- /dev/null
+++ b/MagickCore/composite.c
@@ -0,0 +1,2922 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
+% C O O MM MM P P O O SS I T E %
+% C O O M M M PPPP O O SSS I T EEE %
+% C O O M M P O O SS I T E %
+% CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
+% %
+% %
+% MagickCore Image Composite Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2011 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 %
+% obtain a copy of the License at %
+% %
+% http://www.imagemagick.org/script/license.php %
+% %
+% Unless required by applicable law or agreed to in writing, software %
+% distributed under the License is distributed on an "AS IS" BASIS, %
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
+% See the License for the specific language governing permissions and %
+% limitations under the License. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "MagickCore/studio.h"
+#include "MagickCore/artifact.h"
+#include "MagickCore/cache.h"
+#include "MagickCore/cache-view.h"
+#include "MagickCore/client.h"
+#include "MagickCore/color.h"
+#include "MagickCore/color-private.h"
+#include "MagickCore/colorspace.h"
+#include "MagickCore/colorspace-private.h"
+#include "MagickCore/composite.h"
+#include "MagickCore/composite-private.h"
+#include "MagickCore/constitute.h"
+#include "MagickCore/draw.h"
+#include "MagickCore/fx.h"
+#include "MagickCore/gem.h"
+#include "MagickCore/geometry.h"
+#include "MagickCore/image.h"
+#include "MagickCore/image-private.h"
+#include "MagickCore/list.h"
+#include "MagickCore/log.h"
+#include "MagickCore/monitor.h"
+#include "MagickCore/monitor-private.h"
+#include "MagickCore/memory_.h"
+#include "MagickCore/option.h"
+#include "MagickCore/pixel-accessor.h"
+#include "MagickCore/property.h"
+#include "MagickCore/quantum.h"
+#include "MagickCore/resample.h"
+#include "MagickCore/resource_.h"
+#include "MagickCore/string_.h"
+#include "MagickCore/thread-private.h"
+#include "MagickCore/utility.h"
+#include "MagickCore/version.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p o s i t e I m a g e C h a n n e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CompositeImageChannel() returns the second image composited onto the first
+% at the specified offset, using the specified composite method.
+%
+% The format of the CompositeImageChannel method is:
+%
+% MagickBooleanType CompositeImage(Image *image,
+% const CompositeOperator compose,Image *composite_image,
+% const ssize_t x_offset,const ssize_t y_offset)
+% MagickBooleanType CompositeImageChannel(Image *image,
+% const ChannelType channel,const CompositeOperator compose,
+% Image *composite_image,const ssize_t x_offset,const ssize_t y_offset)
+%
+% A description of each parameter follows:
+%
+% o image: the destination image, modified by he composition
+%
+% o channel: the channel.
+%
+% 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 x_offset: the column offset of the composited image.
+%
+% o y_offset: the row offset of the composited image.
+%
+% Extra Controls from Image meta-data in 'composite_image' (artifacts)
+%
+% o "compose:args"
+% A string containing extra numerical arguments for specific compose
+% methods, generally expressed as a 'geometry' or a comma separated list
+% of numbers.
+%
+% 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.
+%
+% 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 PixelInfo *p,const PixelInfo *q,
+ const ChannelType channel,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 ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels.
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=MagickMin(p->red,q->red);
+ if ((channel & GreenChannel) != 0)
+ composite->green=MagickMin(p->green,q->green);
+ if ((channel & BlueChannel) != 0)
+ composite->blue=MagickMin(p->blue,q->blue);
+ if ((channel & BlackChannel) != 0 &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=MagickMin(p->black,q->black);
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,
+ const PixelInfo *q,const ChannelType channel,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 ((channel & SyncChannels) == 0)
+ {
+ MagickBooleanType
+ from_p;
+
+ from_p=GetPixelInfoIntensity(p) < GetPixelInfoIntensity(q) ? MagickTrue :
+ MagickFalse;
+ if ((channel & RedChannel) != 0)
+ composite->red=from_p != MagickFalse ? p->red : q->red;
+ if ((channel & GreenChannel) != 0)
+ composite->green=from_p != MagickFalse ? p->green : q->green;
+ if ((channel & BlueChannel) != 0)
+ composite->blue=from_p != MagickFalse ? p->blue : q->blue;
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=from_p != MagickFalse ? p->black : q->black;
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,
+ const PixelInfo *q,const ChannelType channel,PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
+ Da=QuantumScale*q->alpha;
+ if ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels.
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=fabs((double) (p->red-q->red));
+ if ((channel & GreenChannel) != 0)
+ composite->green=fabs((double) (p->green-q->green));
+ if ((channel & BlueChannel) != 0)
+ composite->blue=fabs((double) (p->blue-q->blue));
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=fabs((double) (p->black-q->black));
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
+ const ChannelType channel,PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
+ Da=QuantumScale*q->alpha;
+ if ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels.
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=QuantumRange*Divide(QuantumScale*p->red,1.0,
+ QuantumScale*q->red,1.0);
+ if ((channel & GreenChannel) != 0)
+ composite->green=QuantumRange*Divide(QuantumScale*p->green,1.0,
+ QuantumScale*q->green,1.0);
+ if ((channel & BlueChannel) != 0)
+ composite->blue=QuantumRange*Divide(QuantumScale*p->blue,1.0,
+ QuantumScale*q->blue,1.0);
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=QuantumRange*Divide(QuantumScale*p->black,1.0,
+ QuantumScale*q->black,1.0);
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
+ const ChannelType channel,PixelInfo *composite)
+{
+ MagickRealType
+ gamma,
+ Sa,
+ Da;
+
+ Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
+ Da=QuantumScale*q->alpha;
+ if ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels.
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=QuantumRange*Exclusion(QuantumScale*p->red,1.0,
+ QuantumScale*q->red,1.0);
+ if ((channel & GreenChannel) != 0)
+ composite->green=QuantumRange*Exclusion(QuantumScale*p->green,1.0,
+ QuantumScale*q->green,1.0);
+ if ((channel & BlueChannel) != 0)
+ composite->blue=QuantumRange*Exclusion(QuantumScale*p->blue,1.0,
+ QuantumScale*q->blue,1.0);
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=QuantumRange*Exclusion(QuantumScale*p->black,1.0,
+ QuantumScale*q->black,1.0);
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
+ const ChannelType channel,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 ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=MagickMax(p->red,q->red);
+ if ((channel & GreenChannel) != 0)
+ composite->green=MagickMax(p->green,q->green);
+ if ((channel & BlueChannel) != 0)
+ composite->blue=MagickMax(p->blue,q->blue);
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=MagickMax(p->black,q->black);
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,
+ const PixelInfo *q,const ChannelType channel,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 ((channel & SyncChannels) == 0)
+ {
+ MagickBooleanType
+ from_p;
+
+ from_p=GetPixelInfoIntensity(p) > GetPixelInfoIntensity(q) ? MagickTrue :
+ MagickFalse;
+ if ((channel & RedChannel) != 0)
+ composite->red=from_p != MagickFalse ? p->red : q->red;
+ if ((channel & GreenChannel) != 0)
+ composite->green=from_p != MagickFalse ? p->green : q->green;
+ if ((channel & BlueChannel) != 0)
+ composite->blue=from_p != MagickFalse ? p->blue : q->blue;
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=from_p != MagickFalse ? p->black : q->black;
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
+ const ChannelType channel, const GeometryInfo *args, PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=QuantumScale*p->alpha; /* ??? - AT */
+ Da=QuantumScale*q->alpha;
+ if ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels.
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=QuantumRange*Mathematics(QuantumScale*p->red,1.0,
+ QuantumScale*q->red,1.0,args);
+ if ((channel & GreenChannel) != 0)
+ composite->green=QuantumRange*Mathematics(QuantumScale*p->green,1.0,
+ QuantumScale*q->green,1.0,args);
+ if ((channel & BlueChannel) != 0)
+ composite->blue=QuantumRange*Mathematics(QuantumScale*p->blue,1.0,
+ QuantumScale*q->blue,1.0,args);
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=QuantumRange*Mathematics(QuantumScale*p->black,1.0,
+ QuantumScale*q->black,1.0,args);
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
+ const ChannelType channel,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 ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels.
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=p->red+q->red;
+ if ((channel & GreenChannel) != 0)
+ composite->green=p->green+q->green;
+ if ((channel & BlueChannel) != 0)
+ composite->blue=p->blue+q->blue;
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=p->black+q->black;
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
+ const ChannelType channel,PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
+ Da=QuantumScale*q->alpha;
+ if ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels.
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=p->red-q->red;
+ if ((channel & GreenChannel) != 0)
+ composite->green=p->green-q->green;
+ if ((channel & BlueChannel) != 0)
+ composite->blue=p->blue-q->blue;
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=p->black-q->black;
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
+ const ChannelType channel,PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ if ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels.
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=ModulusAdd(p->red,1.0,q->red,1.0);
+ if ((channel & GreenChannel) != 0)
+ composite->green=ModulusAdd(p->green,1.0,q->green,1.0);
+ if ((channel & BlueChannel) != 0)
+ composite->blue=ModulusAdd(p->blue,1.0,q->blue,1.0);
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=ModulusAdd(p->black,1.0,q->black,1.0);
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,
+ const PixelInfo *q, const ChannelType channel,PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ if ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels,
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=ModulusSubtract(p->red,1.0,q->red,1.0);
+ if ((channel & GreenChannel) != 0)
+ composite->green=ModulusSubtract(p->green,1.0,q->green,1.0);
+ if ((channel & BlueChannel) != 0)
+ composite->blue=ModulusSubtract(p->blue,1.0,q->blue,1.0);
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=ModulusSubtract(p->black,1.0,q->black,1.0);
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
+ const ChannelType channel,PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
+ Da=QuantumScale*q->alpha;
+ if ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels.
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=QuantumScale*p->red*q->red;
+ if ((channel & GreenChannel) != 0)
+ composite->green=QuantumScale*p->green*q->green;
+ if ((channel & BlueChannel) != 0)
+ composite->blue=QuantumScale*p->blue*q->blue;
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=QuantumScale*p->black*q->black;
+ if ((channel & AlphaChannel) != 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 PixelInfo *p,const PixelInfo *q,
+ const ChannelType channel,PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
+ Da=QuantumScale*q->alpha;
+ if ((channel & SyncChannels) == 0)
+ {
+ /*
+ Handle channels as separate grayscale channels.
+ */
+ if ((channel & RedChannel) != 0)
+ composite->red=QuantumRange*Screen(QuantumScale*p->red,
+ QuantumScale*q->red);
+ if ((channel & GreenChannel) != 0)
+ composite->green=QuantumRange*Screen(QuantumScale*p->green,
+ QuantumScale*q->green);
+ if ((channel & BlueChannel) != 0)
+ composite->blue=QuantumRange*Screen(QuantumScale*p->blue,
+ QuantumScale*q->blue);
+ if (((channel & BlackChannel) != 0) &&
+ (q->colorspace == CMYKColorspace))
+ composite->black=QuantumRange*Screen(QuantumScale*p->black,
+ QuantumScale*q->black);
+ if ((channel & AlphaChannel) != 0)
+ composite->alpha=QuantumRange*(1.0-Screen(Sa,Da));
+ return;
+ }
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->alpha=(MagickRealType) QuantumRange*gamma;
+ Sa*=QuantumScale; Da*=QuantumScale; /* optimization */
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Screen(p->red*Sa,q->red*Da);
+ composite->green=gamma*Screen(p->green*Sa,q->green*Da);
+ composite->blue=gamma*Screen(p->blue*Sa,q->blue*Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->black=gamma*Screen(p->black*Sa,q->black*Da);
+}
+
+static MagickRealType SoftLight(const MagickRealType Sca,
+ const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
+{
+ MagickRealType
+ alpha,
+ beta;
+
+ /*
+ New specification: March 2009 SVG specification.
+ */
+ alpha=Dca/Da;
+ if ((2.0*Sca) < Sa)
+ return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
+ {
+ beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
+ alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
+ return(beta);
+ }
+ beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
+ return(beta);
+}
+
+static inline void CompositeSoftLight(const PixelInfo *p,const PixelInfo *q,
+ PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
+ Da=QuantumScale*q->alpha;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->alpha=(MagickRealType) QuantumRange*gamma;
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->black=gamma*SoftLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
+ q->black*Da,Da);
+}
+
+static inline MagickRealType Threshold(const MagickRealType p,
+ const MagickRealType q,const MagickRealType threshold,
+ const MagickRealType amount)
+{
+ MagickRealType
+ delta;
+
+ /*
+ Multiply difference by amount, if differance larger than threshold???
+ What use this is is completely unknown. The Opacity calculation appears to
+ be inverted -- Anthony Thyssen
+
+ Deprecated.
+ */
+ delta=p-q;
+ if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
+ return(q);
+ return(q+delta*amount);
+}
+
+static inline void CompositeThreshold(const PixelInfo *p,const PixelInfo *q,
+ const MagickRealType threshold,const MagickRealType amount,
+ PixelInfo *composite)
+{
+ composite->red=Threshold(p->red,q->red,threshold,amount);
+ composite->green=Threshold(p->green,q->green,threshold,amount);
+ composite->blue=Threshold(p->blue,q->blue,threshold,amount);
+ composite->alpha=Threshold(p->alpha,q->alpha,threshold,amount);
+ if (q->colorspace == CMYKColorspace)
+ composite->black=Threshold(p->black,q->black,threshold,amount);
+}
+
+
+static MagickRealType VividLight(const MagickRealType Sca,
+ const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
+{
+ /*
+ VividLight: A Photoshop 7 composition method. See
+ http://www.simplefilter.de/en/basics/mixmods.html.
+
+ f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
+ */
+ if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
+ return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ if ((2.0*Sca) <= Sa)
+ return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
+}
+
+static inline void CompositeVividLight(const PixelInfo *p,const PixelInfo *q,
+ PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
+ Da=QuantumScale*q->alpha;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->alpha=(MagickRealType) QuantumRange*gamma;
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->black=gamma*VividLight(QuantumScale*p->black*Sa,Sa,QuantumScale*
+ q->black*Da,Da);
+}
+
+static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
+ const MagickRealType Dca,const MagickRealType Da)
+{
+ return(Sca*(1.0-Da)+Dca*(1.0-Sa));
+}
+
+static inline void CompositeXor(const PixelInfo *p,const PixelInfo *q,
+ PixelInfo *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=QuantumScale*p->alpha; /* simplify and speed up equations */
+ Da=QuantumScale*q->alpha;
+ gamma=Sa+Da-2.0*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
+ composite->alpha=(MagickRealType) QuantumRange*gamma;
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
+ composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
+ composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->black=gamma*Xor(p->black*Sa,Sa,q->black*Da,Da);
+}
+
+static void HSBComposite(const double hue,const double saturation,
+ const double brightness,MagickRealType *red,MagickRealType *green,
+ MagickRealType *blue)
+{
+ MagickRealType
+ f,
+ h,
+ p,
+ q,
+ t;
+
+ /*
+ Convert HSB to RGB colorspace.
+ */
+ assert(red != (MagickRealType *) NULL);
+ assert(green != (MagickRealType *) NULL);
+ assert(blue != (MagickRealType *) NULL);
+ if (saturation == 0.0)
+ {
+ *red=(MagickRealType) QuantumRange*brightness;
+ *green=(*red);
+ *blue=(*red);
+ return;
+ }
+ h=6.0*(hue-floor(hue));
+ f=h-floor((double) h);
+ p=brightness*(1.0-saturation);
+ q=brightness*(1.0-saturation*f);
+ t=brightness*(1.0-saturation*(1.0-f));
+ switch ((int) h)
+ {
+ case 0:
+ default:
+ {
+ *red=(MagickRealType) QuantumRange*brightness;
+ *green=(MagickRealType) QuantumRange*t;
+ *blue=(MagickRealType) QuantumRange*p;
+ break;
+ }
+ case 1:
+ {
+ *red=(MagickRealType) QuantumRange*q;
+ *green=(MagickRealType) QuantumRange*brightness;
+ *blue=(MagickRealType) QuantumRange*p;
+ break;
+ }
+ case 2:
+ {
+ *red=(MagickRealType) QuantumRange*p;
+ *green=(MagickRealType) QuantumRange*brightness;
+ *blue=(MagickRealType) QuantumRange*t;
+ break;
+ }
+ case 3:
+ {
+ *red=(MagickRealType) QuantumRange*p;
+ *green=(MagickRealType) QuantumRange*q;
+ *blue=(MagickRealType) QuantumRange*brightness;
+ break;
+ }
+ case 4:
+ {
+ *red=(MagickRealType) QuantumRange*t;
+ *green=(MagickRealType) QuantumRange*p;
+ *blue=(MagickRealType) QuantumRange*brightness;
+ break;
+ }
+ case 5:
+ {
+ *red=(MagickRealType) QuantumRange*brightness;
+ *green=(MagickRealType) QuantumRange*p;
+ *blue=(MagickRealType) 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)
+{
+ MagickBooleanType
+ status;
+
+ status=CompositeImageChannel(image,DefaultChannels,compose,composite_image,
+ x_offset,y_offset);
+ return(status);
+}
+
+MagickExport MagickBooleanType CompositeImageChannel(Image *image,
+ const ChannelType channel,const CompositeOperator compose,
+ const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset)
+{
+#define CompositeImageTag "Composite/Image"
+
+ CacheView
+ *composite_view,
+ *image_view;
+
+ const char
+ *value;
+
+ double
+ sans;
+
+ ExceptionInfo
+ *exception;
+
+ GeometryInfo
+ geometry_info;
+
+ Image
+ *destination_image;
+
+ MagickBooleanType
+ modify_outside_overlay,
+ status;
+
+ MagickOffsetType
+ progress;
+
+ PixelInfo
+ zero;
+
+ MagickRealType
+ amount,
+ destination_dissolve,
+ midpoint,
+ percent_brightness,
+ percent_saturation,
+ source_dissolve,
+ threshold;
+
+ MagickStatusType
+ flags;
+
+ ssize_t
+ y;
+
+ /*
+ Prepare composite image.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(composite_image != (Image *) NULL);
+ assert(composite_image->signature == MagickSignature);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ GetPixelInfo(image,&zero);
+ 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))
+ break;
+ if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
+ break;
+ 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);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+#pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (ssize_t) composite_image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const Quantum
+ *p;
+
+ register Quantum
+ *q;
+
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
+ 1,exception);
+ q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
+ composite_image->columns,1,exception);
+ if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
+ {
+ 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+=GetPixelChannels(composite_image);
+ q+=GetPixelChannels(image);
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == 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,
+ (MagickOffsetType) y,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ composite_view=DestroyCacheView(composite_view);
+ image_view=DestroyCacheView(image_view);
+ return(status);
+ }
+ case CopyOpacityCompositeOp:
+ case ChangeMaskCompositeOp:
+ {
+ /*
+ 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;
+ break;
+ }
+ case BlurCompositeOp:
+ {
+ CacheView
+ *composite_view,
+ *destination_view;
+
+ PixelInfo
+ pixel;
+
+ MagickRealType
+ angle_range,
+ angle_start,
+ height,
+ width;
+
+ ResampleFilter
+ *resample_filter;
+
+ SegmentInfo
+ blur;
+
+ /*
+ 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);
+ if (destination_image == (Image *) NULL)
+ return(MagickFalse);
+ /*
+ Determine the horizontal and vertical maximim blur.
+ */
+ SetGeometryInfo(&geometry_info);
+ flags=NoValue;
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ flags=ParseGeometry(value,&geometry_info);
+ if ((flags & WidthValue) == 0 )
+ {
+ destination_image=DestroyImage(destination_image);
+ return(MagickFalse);
+ }
+ width=geometry_info.rho;
+ height=geometry_info.sigma;
+ blur.x1=geometry_info.rho;
+ 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;
+ if ((flags & XValue) != 0 )
+ {
+ MagickRealType
+ angle;
+
+ angle=DegreesToRadians(geometry_info.xi);
+ blur.x1=width*cos(angle);
+ blur.x2=width*sin(angle);
+ blur.y1=(-height*sin(angle));
+ blur.y2=height*cos(angle);
+ }
+ if ((flags & YValue) != 0 )
+ {
+ angle_start=DegreesToRadians(geometry_info.xi);
+ angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
+ }
+ /*
+ Blur Image by resampling.
+ */
+ 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);
+ for (y=0; y < (ssize_t) composite_image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const Quantum
+ *restrict p;
+
+ register Quantum
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
+ continue;
+ p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
+ 1,exception);
+ q=QueueCacheViewAuthenticPixels(destination_view,0,y,
+ destination_image->columns,1,&image->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+=GetPixelChannels(composite_image);
+ continue;
+ }
+ if (fabs(angle_range) > MagickEpsilon)
+ {
+ MagickRealType
+ angle;
+
+ angle=angle_start+angle_range*QuantumScale*
+ GetPixelBlue(composite_image,p);
+ blur.x1=width*cos(angle);
+ blur.x2=width*sin(angle);
+ 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));
+ (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
+ (double) y_offset+y,&pixel);
+ SetPixelPixelInfo(destination_image,&pixel,q);
+ p+=GetPixelChannels(composite_image);
+ q+=GetPixelChannels(destination_image);
+ }
+ sync=SyncCacheViewAuthenticPixels(destination_view,exception);
+ if (sync == MagickFalse)
+ break;
+ }
+ resample_filter=DestroyResampleFilter(resample_filter);
+ composite_view=DestroyCacheView(composite_view);
+ destination_view=DestroyCacheView(destination_view);
+ composite_image=destination_image;
+ break;
+ }
+ case DisplaceCompositeOp:
+ case DistortCompositeOp:
+ {
+ CacheView
+ *composite_view,
+ *destination_view,
+ *image_view;
+
+ PixelInfo
+ pixel;
+
+ MagickRealType
+ horizontal_scale,
+ vertical_scale;
+
+ PointInfo
+ center,
+ offset;
+
+ /*
+ Displace/Distort based on overlay gradient map:
+ X = red_channel; Y = green_channel;
+ compose:args = x_scale[,y_scale[,center.x,center.y]]
+ */
+ destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ &image->exception);
+ if (destination_image == (Image *) NULL)
+ return(MagickFalse);
+ SetGeometryInfo(&geometry_info);
+ flags=NoValue;
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ flags=ParseGeometry(value,&geometry_info);
+ if ((flags & (WidthValue|HeightValue)) == 0 )
+ {
+ if ((flags & AspectValue) == 0)
+ {
+ horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
+ 2.0;
+ vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
+ }
+ else
+ {
+ horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
+ vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
+ }
+ }
+ else
+ {
+ horizontal_scale=geometry_info.rho;
+ vertical_scale=geometry_info.sigma;
+ if ((flags & PercentValue) != 0)
+ {
+ if ((flags & AspectValue) == 0)
+ {
+ horizontal_scale*=(composite_image->columns-1.0)/200.0;
+ vertical_scale*=(composite_image->rows-1.0)/200.0;
+ }
+ else
+ {
+ horizontal_scale*=(image->columns-1.0)/200.0;
+ vertical_scale*=(image->rows-1.0)/200.0;
+ }
+ }
+ if ((flags & HeightValue) == 0)
+ vertical_scale=horizontal_scale;
+ }
+ /*
+ Determine fixed center point for absolute distortion map
+ Absolute distort ==
+ Displace offset relative to a fixed absolute point
+ Select that point according to +X+Y user inputs.
+ default = center of overlay image
+ arg flag '!' = locations/percentage relative to background image
+ */
+ center.x=(MagickRealType) x_offset;
+ center.y=(MagickRealType) y_offset;
+ if (compose == DistortCompositeOp)
+ {
+ if ((flags & XValue) == 0)
+ if ((flags & AspectValue) == 0)
+ center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
+ 2.0;
+ else
+ center.x=((MagickRealType) image->columns-1)/2.0;
+ else
+ if ((flags & AspectValue) == 0)
+ center.x=(MagickRealType) x_offset+geometry_info.xi;
+ else
+ center.x=geometry_info.xi;
+ if ((flags & YValue) == 0)
+ if ((flags & AspectValue) == 0)
+ center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
+ else
+ center.y=((MagickRealType) image->rows-1)/2.0;
+ else
+ if ((flags & AspectValue) == 0)
+ center.y=(MagickRealType) y_offset+geometry_info.psi;
+ else
+ center.y=geometry_info.psi;
+ }
+ /*
+ 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);
+ for (y=0; y < (ssize_t) composite_image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const Quantum
+ *restrict p;
+
+ register Quantum
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
+ continue;
+ p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
+ 1,exception);
+ q=QueueCacheViewAuthenticPixels(destination_view,0,y,
+ destination_image->columns,1,&image->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+=GetPixelChannels(composite_image);
+ continue;
+ }
+ /*
+ Displace the offset.
+ */
+ offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
+ (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
+ QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
+ x : 0);
+ offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
+ (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
+ QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
+ y : 0);
+ (void) InterpolatePixelInfo(image,image_view,
+ UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
+ &pixel,exception);
+ /*
+ 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+=GetPixelChannels(composite_image);
+ q+=GetPixelChannels(destination_image);
+ }
+ sync=SyncCacheViewAuthenticPixels(destination_view,exception);
+ if (sync == MagickFalse)
+ break;
+ }
+ destination_view=DestroyCacheView(destination_view);
+ composite_view=DestroyCacheView(composite_view);
+ image_view=DestroyCacheView(image_view);
+ composite_image=destination_image;
+ break;
+ }
+ case DissolveCompositeOp:
+ {
+ /*
+ Geometry arguments to dissolve factors.
+ */
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ {
+ flags=ParseGeometry(value,&geometry_info);
+ source_dissolve=geometry_info.rho/100.0;
+ destination_dissolve=1.0;
+ if ((source_dissolve-MagickEpsilon) < 0.0)
+ source_dissolve=0.0;
+ if ((source_dissolve+MagickEpsilon) > 1.0)
+ {
+ destination_dissolve=2.0-source_dissolve;
+ source_dissolve=1.0;
+ }
+ if ((flags & SigmaValue) != 0)
+ destination_dissolve=geometry_info.sigma/100.0;
+ if ((destination_dissolve-MagickEpsilon) < 0.0)
+ destination_dissolve=0.0;
+ modify_outside_overlay=MagickTrue;
+ if ((destination_dissolve+MagickEpsilon) > 1.0 )
+ {
+ destination_dissolve=1.0;
+ modify_outside_overlay=MagickFalse;
+ }
+ }
+ break;
+ }
+ case BlendCompositeOp:
+ {
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ {
+ flags=ParseGeometry(value,&geometry_info);
+ source_dissolve=geometry_info.rho/100.0;
+ 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:
+ {
+ /*
+ Just collect the values from "compose:args", setting.
+ Unused values are set to zero automagically.
+
+ Arguments are normally a comma separated list, so this probably should
+ be changed to some 'general comma list' parser, (with a minimum
+ number of values)
+ */
+ SetGeometryInfo(&geometry_info);
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ (void) ParseGeometry(value,&geometry_info);
+ break;
+ }
+ case ModulateCompositeOp:
+ {
+ /*
+ Determine the brightness and saturation scale.
+ */
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ {
+ flags=ParseGeometry(value,&geometry_info);
+ percent_brightness=geometry_info.rho;
+ if ((flags & SigmaValue) != 0)
+ percent_saturation=geometry_info.sigma;
+ }
+ break;
+ }
+ case ThresholdCompositeOp:
+ {
+ /*
+ Determine the amount and threshold.
+ */
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ {
+ flags=ParseGeometry(value,&geometry_info);
+ amount=geometry_info.rho;
+ threshold=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ threshold=0.05f;
+ }
+ threshold*=QuantumRange;
+ break;
+ }
+ 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);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ const Quantum
+ *pixels;
+
+ double
+ brightness,
+ hue,
+ saturation;
+
+ PixelInfo
+ composite,
+ destination,
+ source;
+
+ register const Quantum
+ *restrict p;
+
+ register Quantum
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ if (modify_outside_overlay == MagickFalse)
+ {
+ if (y < y_offset)
+ continue;
+ if ((y-y_offset) >= (ssize_t) composite_image->rows)
+ continue;
+ }
+ /*
+ If pixels is NULL, y is outside overlay region.
+ */
+ pixels=(Quantum *) NULL;
+ p=(Quantum *) NULL;
+ if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
+ {
+ p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
+ composite_image->columns,1,exception);
+ if (p == (const Quantum *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ pixels=p;
+ if (x_offset < 0)
+ p-=x_offset*GetPixelChannels(composite_image);
+ }
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (const Quantum *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ source=zero;
+ destination=zero;
+ hue=0.0;
+ saturation=0.0;
+ brightness=0.0;
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ if (modify_outside_overlay == MagickFalse)
+ {
+ if (x < x_offset)
+ {
+ q+=GetPixelChannels(image);
+ continue;
+ }
+ if ((x-x_offset) >= (ssize_t) composite_image->columns)
+ break;
+ }
+ 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:
+ {
+ composite.alpha=(MagickRealType) TransparentAlpha;
+ break;
+ }
+ default:
+ {
+ (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,y-
+ y_offset,&composite,exception);
+ break;
+ }
+ }
+ if (image->colorspace == CMYKColorspace)
+ {
+ composite.red=(MagickRealType) QuantumRange-composite.red;
+ composite.green=(MagickRealType) QuantumRange-composite.green;
+ composite.blue=(MagickRealType) QuantumRange-composite.blue;
+ composite.black=(MagickRealType) QuantumRange-composite.black;
+ }
+ SetPixelRed(image,ClampToQuantum(composite.red),q);
+ SetPixelGreen(image,ClampToQuantum(composite.green),q);
+ SetPixelBlue(image,ClampToQuantum(composite.blue),q);
+ if (image->matte != MagickFalse)
+ SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
+ if (image->colorspace == CMYKColorspace)
+ SetPixelBlack(image,ClampToQuantum(composite.black),q);
+ 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.
+ */
+ 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:
+ {
+ CompositePlus(&source,&destination,channel,&composite);
+ break;
+ }
+ case MinusDstCompositeOp:
+ {
+ CompositeMinus(&source,&destination,channel,&composite);
+ break;
+ }
+ case MinusSrcCompositeOp:
+ {
+ CompositeMinus(&destination,&source,channel,&composite);
+ break;
+ }
+ case ModulusAddCompositeOp:
+ {
+ CompositeModulusAdd(&source,&destination,channel,&composite);
+ break;
+ }
+ case ModulusSubtractCompositeOp:
+ {
+ CompositeModulusSubtract(&source,&destination,channel,&composite);
+ break;
+ }
+ case DifferenceCompositeOp:
+ {
+ CompositeDifference(&source,&destination,channel,&composite);
+ break;
+ }
+ case ExclusionCompositeOp:
+ {
+ CompositeExclusion(&source,&destination,channel,&composite);
+ break;
+ }
+ case MultiplyCompositeOp:
+ {
+ CompositeMultiply(&source,&destination,channel,&composite);
+ break;
+ }
+ case ScreenCompositeOp:
+ {
+ CompositeScreen(&source,&destination,channel,&composite);
+ break;
+ }
+ case DivideDstCompositeOp:
+ {
+ CompositeDivide(&source,&destination,channel,&composite);
+ break;
+ }
+ case DivideSrcCompositeOp:
+ {
+ CompositeDivide(&destination,&source,channel,&composite);
+ break;
+ }
+ case DarkenCompositeOp:
+ {
+ CompositeDarken(&source,&destination,channel,&composite);
+ break;
+ }
+ case LightenCompositeOp:
+ {
+ CompositeLighten(&source,&destination,channel,&composite);
+ break;
+ }
+ case DarkenIntensityCompositeOp:
+ {
+ CompositeDarkenIntensity(&source,&destination,channel,&composite);
+ break;
+ }
+ case LightenIntensityCompositeOp:
+ {
+ CompositeLightenIntensity(&source,&destination,channel,&composite);
+ break;
+ }
+ case MathematicsCompositeOp:
+ {
+ CompositeMathematics(&source,&destination,channel,&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:
+ {
+ 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);
+ break;
+ }
+ case HueCompositeOp:
+ {
+ 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;
+ 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;
+ break;
+ }
+ case LuminizeCompositeOp:
+ {
+ 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;
+ break;
+ }
+ case ColorizeCompositeOp:
+ {
+ 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;
+ break;
+ }
+ case CopyRedCompositeOp:
+ case CopyCyanCompositeOp:
+ {
+ composite.red=source.red;
+ break;
+ }
+ case CopyGreenCompositeOp:
+ case CopyMagentaCompositeOp:
+ {
+ composite.green=source.green;
+ break;
+ }
+ case CopyBlueCompositeOp:
+ case CopyYellowCompositeOp:
+ {
+ composite.blue=source.blue;
+ break;
+ }
+ case CopyOpacityCompositeOp:
+ {
+ if (source.matte == MagickFalse)
+ {
+ composite.alpha=(MagickRealType) GetPixelInfoIntensity(&source);
+ break;
+ }
+ composite.alpha=source.alpha;
+ break;
+ }
+ case CopyBlackCompositeOp:
+ {
+ if (source.colorspace != CMYKColorspace)
+ ConvertRGBToCMYK(&source);
+ composite.black=source.black;
+ break;
+ }
+ case BlurCompositeOp:
+ case DisplaceCompositeOp:
+ case DistortCompositeOp:
+ {
+ composite=source;
+ break;
+ }
+ default:
+ break;
+ }
+ if (image->colorspace == CMYKColorspace)
+ {
+ composite.red=(MagickRealType) QuantumRange-composite.red;
+ composite.green=(MagickRealType) QuantumRange-composite.green;
+ composite.blue=(MagickRealType) QuantumRange-composite.blue;
+ composite.black=(MagickRealType) QuantumRange-composite.black;
+ }
+ SetPixelRed(image,ClampToQuantum(composite.red),q);
+ SetPixelGreen(image,ClampToQuantum(composite.green),q);
+ SetPixelBlue(image,ClampToQuantum(composite.blue),q);
+ if (image->colorspace == CMYKColorspace)
+ SetPixelBlack(image,ClampToQuantum(composite.black),q);
+ SetPixelAlpha(image,ClampToQuantum(composite.alpha),q);
+ p+=GetPixelChannels(composite_image);
+ if (p >= (pixels+composite_image->columns*GetPixelChannels(composite_image)))
+ 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_CompositeImageChannel)
+#endif
+ proceed=SetImageProgress(image,CompositeImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ composite_view=DestroyCacheView(composite_view);
+ image_view=DestroyCacheView(image_view);
+ if (destination_image != (Image * ) NULL)
+ destination_image=DestroyImage(destination_image);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T e x t u r e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TextureImage() repeatedly tiles the texture image across and down the image
+% canvas.
+%
+% The format of the TextureImage method is:
+%
+% MagickBooleanType TextureImage(Image *image,const Image *texture)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o texture: This image is the texture to layer on the background.
+%
+*/
+MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
+{
+#define TextureImageTag "Texture/Image"
+
+ CacheView
+ *image_view,
+ *texture_view;
+
+ ExceptionInfo
+ *exception;
+
+ MagickBooleanType
+ status;
+
+ ssize_t
+ y;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ if (texture == (const Image *) NULL)
+ return(MagickFalse);
+ (void) SetImageVirtualPixelMethod(texture,TileVirtualPixelMethod);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ status=MagickTrue;
+ if ((image->compose != CopyCompositeOp) &&
+ ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
+ (texture->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)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture->rows)
+ {
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
+ {
+ MagickBooleanType
+ thread_status;
+
+ thread_status=CompositeImage(image,image->compose,texture,x+
+ texture->tile_offset.x,y+texture->tile_offset.y);
+ if (thread_status == MagickFalse)
+ {
+ status=thread_status;
+ break;
+ }
+ }
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_TextureImage)
+#endif
+ proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
+ y,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
+ image->rows,image->rows);
+ return(status);
+ }
+ /*
+ Tile texture onto the image background (optimized).
+ */
+ status=MagickTrue;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ texture_view=AcquireCacheView(texture);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status) omp_throttle(1)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const Quantum
+ *p,
+ *pixels;
+
+ register ssize_t
+ x;
+
+ register Quantum
+ *q;
+
+ size_t
+ width;
+
+ 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);
+ if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture->columns)
+ {
+ register ssize_t
+ i;
+
+ p=pixels;
+ width=texture->columns;
+ if ((x+(ssize_t) width) > (ssize_t) image->columns)
+ width=image->columns-x;
+ for (i=0; i < (ssize_t) width; i++)
+ {
+ 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+=GetPixelChannels(texture);
+ q+=GetPixelChannels(image);
+ }
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_TextureImage)
+#endif
+ proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ texture_view=DestroyCacheView(texture_view);
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}