]> granicus.if.org Git - imagemagick/commitdiff
Modifications of Mathematical Compositions to use the channel 'sync' flag
authoranthony <anthony@git.imagemagick.org>
Wed, 5 May 2010 05:27:54 +0000 (05:27 +0000)
committeranthony <anthony@git.imagemagick.org>
Wed, 5 May 2010 05:27:54 +0000 (05:27 +0000)
ChangeLog
magick/composite-private.h
magick/composite.c
magick/composite.h
magick/option.c

index d28957247f2bad6ff65c9403219701584f1d4147..aaf409f6e3ca17baa2e56932a14094e70346c55c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2010-05-05  6.6.1-6 Anthony Thyssen <A.Thyssen@griffith...>
+  * Rename of "Add" and "Subtract" compositions to "ModulusAdd" and
+    "ModulusSubtract" for more clarity of there functions. Old names
+    will continue to work as expected.
+  * Fix "Plus", "Minus", "ModulusAdd", and "ModulusSubtract" to follow
+    normal SVG 'Over' blending specifications, (see next for override)
+  * Allow the "-channel sync" flag to allow mathematical compositions to work
+    for SVG specified handling, or if turned off, to perform image mathematics
+    on specific individual channels.  You can now use math compose methods
+    with the alpha channels of images, or with just one channel of an image.
+
 2010-04-28  6.6.1-6 Anthony Thyssen <A.Thyssen@griffith...>
   * Special case handling in Barrel distortion (pixel at image center)
   * Calculation of image for Barrel distortion (pixel vs image coords)
index 9b8b455f1fc6446ceecb65fc7b6ac752238f83c7..29e2ecf1c6c329538d7432e2878d9c526dd03ef9 100644 (file)
@@ -105,14 +105,15 @@ static inline void MagickPixelCompositeOver(const MagickPixelPacket *p,
     composite->index=gamma*MagickOver_(p->index,alpha,q->index,beta);
 }
 
+#if 0
 static inline void MagickPixelCompositePlus(const MagickPixelPacket *p,
   const MagickRealType alpha,const MagickPixelPacket *q,
   const MagickRealType beta,MagickPixelPacket *composite)
 {
   MagickRealType
+    Sa,
     Da,
-    gamma,
-    Sa;
+    gamma;
 
   /*
     Add two pixels with the given opacities.
@@ -128,6 +129,7 @@ static inline void MagickPixelCompositePlus(const MagickPixelPacket *p,
   if (q->colorspace == CMYKColorspace)
     composite->index=gamma*(Sa*p->index+Da*q->index);
 }
+#endif
 
 /*
   Blend pixel colors p and q by the amount given.
@@ -136,9 +138,27 @@ static inline void MagickPixelCompositeBlend(const MagickPixelPacket *p,
   const MagickRealType alpha,const MagickPixelPacket *q,
   const MagickRealType beta,MagickPixelPacket *composite)
 {
+#if 0
   MagickPixelCompositePlus(p,(MagickRealType) (QuantumRange-alpha*
     (QuantumRange-p->opacity)),q,(MagickRealType) (QuantumRange-beta*
     GetAlphaPixelComponent(q)),composite);
+#else
+  MagickRealType
+    Sa,
+    Da,
+    gamma;
+
+  Sa=alpha*(1.0-QuantumScale*p->opacity);
+  Da=beta*(1.0-QuantumScale*q->opacity);
+  gamma=RoundToUnity(Sa+Da);  /* 'Plus' blending -- not 'Over' blending */
+  composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+  gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+  composite->red=gamma*(Sa*p->red+Da*q->red);
+  composite->green=gamma*(Sa*p->green+Da*q->green);
+  composite->blue=gamma*(Sa*p->blue+Da*q->blue);
+  if (q->colorspace == CMYKColorspace)
+    composite->index=gamma*(Sa*p->index+Da*q->index);
+#endif
 }
 
 /*
@@ -149,9 +169,28 @@ static inline void MagickPixelCompositeAreaBlend(const MagickPixelPacket *p,
   const MagickRealType beta,const MagickRealType area,
   MagickPixelPacket *composite)
 {
+#if 0
   MagickPixelCompositePlus(p,(MagickRealType) QuantumRange-(1.0-area)*
     (QuantumRange-alpha),q,(MagickRealType) (QuantumRange-area*(QuantumRange-
     beta)),composite);
+#else
+  MagickRealType
+    Sa,
+    Da,
+    gamma;
+
+  Sa=(1.0-area)*(1.0-QuantumScale*alpha);
+  Da=area*(1.0-QuantumScale*beta);
+  gamma=RoundToUnity(Sa+Da);  /* 'Plus' blending -- not 'Over' blending */
+  composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+  gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+  composite->red=gamma*(Sa*p->red+Da*q->red);
+  composite->green=gamma*(Sa*p->green+Da*q->green);
+  composite->blue=gamma*(Sa*p->blue+Da*q->blue);
+  if (q->colorspace == CMYKColorspace)
+    composite->index=gamma*(Sa*p->index+Da*q->index);
+#endif
+
 }
 
 #if defined(__cplusplus) || defined(c_plusplus)
index 32e420c80e082bb793bdc9ce29043fb03f9fce88..e328f574d9540a77b7a5d57beb2c9a40369fc140 100644 (file)
@@ -173,31 +173,30 @@ static inline double MagickMax(const double x,const double y)
 **   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 Add(const MagickRealType p,const MagickRealType q)
-{
-  MagickRealType
-    pixel;
-
-  pixel=p+q;
-  if (pixel > QuantumRange)
-    pixel-=(QuantumRange+1.0);
-  return(pixel);
-}
-
-static inline void CompositeAdd(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
-{
-  composite->red=Add(p->red,q->red);
-  composite->green=Add(p->green,q->green);
-  composite->blue=Add(p->blue,q->blue);
-  composite->opacity=Add(alpha,beta);
-  if (q->colorspace == CMYKColorspace)
-    composite->index=Add(p->index,q->index);
-}
-
 static inline MagickRealType Atop(const MagickRealType p,
   const MagickRealType Sa,const MagickRealType q,
   const MagickRealType magick_unused(Da))
@@ -206,14 +205,13 @@ static inline MagickRealType Atop(const MagickRealType p,
 }
 
 static inline void CompositeAtop(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  composite->opacity=beta;    /* optimized  1.0-Gamma */
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  composite->opacity=q->opacity;   /* 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);
@@ -222,12 +220,11 @@ static inline void CompositeAtop(const MagickPixelPacket *p,
 }
 
 /*
-  What is this Composition method for, can't find any specification!
+  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 MagickPixelPacket *p,
-  const MagickRealType magick_unused(alpha),const MagickPixelPacket *q,
-  const MagickRealType magick_unused(beta),MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     intensity;
@@ -276,16 +273,15 @@ static MagickRealType ColorBurn(const MagickRealType Sca,
 }
 
 static inline void CompositeColorBurn(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
@@ -339,16 +335,15 @@ static MagickRealType ColorDodge(const MagickRealType Sca,
 }
 
 static inline void CompositeColorDodge(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
@@ -372,53 +367,82 @@ static inline MagickRealType Darken(const MagickRealType p,
 }
 
 static inline void CompositeDarken(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,const ChannelType channel,
+  MagickPixelPacket *composite)
 {
+  /*
+    Darken is equivelent to a 'Minimum' method
+    OR a greyscale version of a binary 'Or'
+    OR the 'Intersection' of pixel sets.
+  */
   MagickRealType
     gamma;
 
-  gamma=1.0-QuantumScale*QuantumScale*alpha*beta;
-  composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
-  gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
-  composite->red=gamma*Darken(p->red,alpha,q->red,beta);
-  composite->green=gamma*Darken(p->green,alpha,q->green,beta);
-  composite->blue=gamma*Darken(p->blue,alpha,q->blue,beta);
-  if (q->colorspace == CMYKColorspace)
-    composite->index=gamma*Darken(p->index,alpha,q->index,beta);
+  if ( (channel & SyncChannels) != 0 ) {
+    composite->opacity=QuantumScale*p->opacity*q->opacity; /* Over Blend */
+    gamma=1.0-QuantumScale*composite->opacity;
+    gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+    composite->red=gamma*Darken(p->red,p->opacity,q->red,q->opacity);
+    composite->green=gamma*Darken(p->green,p->opacity,q->green,q->opacity);
+    composite->blue=gamma*Darken(p->blue,p->opacity,q->blue,q->opacity);
+    if (q->colorspace == CMYKColorspace)
+      composite->index=gamma*Darken(p->index,p->opacity,q->index,q->opacity);
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=MagickMax(p->opacity,q->opacity);
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=MagickMin(p->index,q->index);
+  }
 }
 
 static inline MagickRealType Difference(const MagickRealType p,
   const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
 {
-  /*
-    Optimized by Multipling by QuantumRange (taken from gamma).
-  */
+  /* 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 MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,const ChannelType channel,
+  MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
-  gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
-  composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
-  /*
-    Values not normalized as an optimization.
-  */
-  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->index=gamma*Difference(p->index,Sa,q->index,Da);
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
+  if ( (channel & SyncChannels) != 0 ) {
+    gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+    composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+    gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+    /* Values are not normalized as an optimization.  */
+    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->index=gamma*Difference(p->index,Sa,q->index,Da);
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=fabs(p->opacity - q->opacity);
+    if ( (channel | RedChannel) != 0 )
+      composite->red=fabs(p->red - q->red);
+    if ( (channel | GreenChannel) != 0 )
+      composite->green=fabs(p->green - q->green);
+    if ( (channel | BlueChannel) != 0 )
+      composite->blue=fabs(p->blue - q->blue);
+    if ( (channel | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=fabs(p->index - q->index);
+  }
 }
 
 static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
@@ -430,9 +454,9 @@ static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
       f(Sc,Dc) = Sc/Dc
 
     But with appropriate handling for special case of Dc == 0 specifically
-    f(Black,Black)  = Black and f(non-Black,Black) = White.  It is however
-    also important to correctly do 'over' alpha blending which is why it
-    becomes so complex looking.
+    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 looking.
   */
   if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
     return(Sca*(1.0-Da)+Dca*(1.0-Sa));
@@ -442,28 +466,46 @@ static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
 }
 
 static inline void CompositeDivide(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,const ChannelType channel,
+  MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
-  gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
-  composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*Divide(QuantumScale*p->index*Sa,Sa,QuantumScale*
-      q->index*Da,Da);
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
+  if ( (channel & SyncChannels) != 0 ) {
+    gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+    composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*Divide(QuantumScale*p->index*Sa,Sa,QuantumScale*
+        q->index*Da,Da);
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=QuantumRange*(1.0-Divide(Sa,1.0,Da,1.0));
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=QuantumRange*
+          Divide(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0);
+  }
 }
 
 static MagickRealType Exclusion(const MagickRealType Sca,
@@ -473,28 +515,46 @@ static MagickRealType Exclusion(const MagickRealType Sca,
 }
 
 static inline void CompositeExclusion(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,const ChannelType channel,
+  MagickPixelPacket *composite)
 {
   MagickRealType
     gamma,
     Sa,
     Da;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
-  gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
-  composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*Exclusion(QuantumScale*p->index*Sa,Sa,QuantumScale*
-      q->index*Da,Da);
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
+  if ( (channel & SyncChannels) != 0 ) {
+    gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+    composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*Exclusion(QuantumScale*p->index*Sa,Sa,QuantumScale*
+        q->index*Da,Da);
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=QuantumRange*(1.0-Exclusion(Sa,1.0,Da,1.0));
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=QuantumRange*
+          Exclusion(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0);
+  }
 }
 
 static MagickRealType HardLight(const MagickRealType Sca,
@@ -506,16 +566,15 @@ static MagickRealType HardLight(const MagickRealType Sca,
 }
 
 static inline void CompositeHardLight(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
@@ -573,27 +632,30 @@ static void CompositeHSB(const MagickRealType red,const MagickRealType green,
 }
 
 static inline MagickRealType In(const MagickRealType p,
-  const MagickRealType alpha,const MagickRealType magick_unused(q),
-  const MagickRealType beta)
+  const MagickRealType Sa,const MagickRealType magick_unused(q),
+  const MagickRealType Da)
 {
-  return((1.0-QuantumScale*alpha)*p*(1.0-QuantumScale*beta));
+  return(Sa*p*Da);
 }
 
 static inline void CompositeIn(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
-    gamma;
+    gamma,
+    Sa,
+    Da;
 
-  gamma=(1.0-QuantumScale*alpha)*(1.0-QuantumScale*beta);
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
+  gamma=Sa*Da;
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
-  composite->red=gamma*In(p->red,alpha,q->red,beta);
-  composite->green=gamma*In(p->green,alpha,q->green,beta);
-  composite->blue=gamma*In(p->blue,alpha,q->blue,beta);
+  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->index=gamma*In(p->index,alpha,q->index,beta);
+    composite->index=gamma*In(p->index,Sa,q->index,Da);
 }
 
 static inline MagickRealType Lighten(const MagickRealType p,
@@ -605,38 +667,66 @@ static inline MagickRealType Lighten(const MagickRealType p,
 }
 
 static inline void CompositeLighten(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,const ChannelType channel,
+  MagickPixelPacket *composite)
 {
+  /*
+    Lighten is also equivelevt to a 'Maximum' method
+    OR a greyscale version of a binary 'And'
+    OR the 'Union' of pixel sets.
+  */
   MagickRealType
     gamma;
 
-  composite->opacity=QuantumScale*alpha*beta;    /* optimized 1-gamma */
-  gamma=1.0-QuantumScale*composite->opacity;
-  gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
-  composite->red=gamma*Lighten(p->red,alpha,q->red,beta);
-  composite->green=gamma*Lighten(p->green,alpha,q->green,beta);
-  composite->blue=gamma*Lighten(p->blue,alpha,q->blue,beta);
-  if (q->colorspace == CMYKColorspace)
-    composite->index=gamma*Lighten(p->index,alpha,q->index,beta);
+  if ( (channel & SyncChannels) != 0 ) {
+    composite->opacity=QuantumScale*p->opacity*q->opacity; /* Over Blend */
+    gamma=1.0-QuantumScale*composite->opacity;
+    gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+    composite->red=gamma*Lighten(p->red,p->opacity,q->red,q->opacity);
+    composite->green=gamma*Lighten(p->green,p->opacity,q->green,q->opacity);
+    composite->blue=gamma*Lighten(p->blue,p->opacity,q->blue,q->opacity);
+    if (q->colorspace == CMYKColorspace)
+      composite->index=gamma*Lighten(p->index,p->opacity,q->index,q->opacity);
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=MagickMin(p->opacity,q->opacity);
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=MagickMax(p->index,q->index);
+  }
 }
 
+#if 0
+static inline MagickRealType LinearDodge(const MagickRealType Sca,
+  const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
+{
+  /*
+    LinearDodge: simplifies to a trivial formula
+    f(Sc,Dc) = Sc + Dc
+    Dca' = Sca + Dca
+  */
+  return(Sca+Dca);
+}
+#endif
+
 static inline void CompositeLinearDodge(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
-  /*
-    Operation performed directly - not need for sub-routine.
-  */
   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);
@@ -650,25 +740,24 @@ static inline MagickRealType LinearBurn(const MagickRealType Sca,
   const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
 {
   /*
-    LinearLight: as defined by Abode Photoshop, according to
+    LinearBurn: as defined by Abode Photoshop, according to
     http://www.simplefilter.de/en/basics/mixmods.html is:
 
-    f(Sc,Dc) = Dc + Sc - 1
+    f(Sc,Dc) = Sc + Dc - 1
   */
   return(Sca+Dca-Sa*Da);
 }
 
 static inline void CompositeLinearBurn(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
@@ -688,7 +777,7 @@ static inline MagickRealType LinearLight(const MagickRealType Sca,
 {
 #if 0
   /*
-    Previous formula, only valid for fully-opaque images.
+    Previous formula, was only valid for fully-opaque images.
   */
   return(Dca+2*Sca-1.0);
 #else
@@ -703,16 +792,15 @@ static inline MagickRealType LinearLight(const MagickRealType Sca,
 }
 
 static inline void CompositeLinearLight(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
@@ -755,55 +843,225 @@ static inline MagickRealType Mathematics(const MagickRealType Sca,
 }
 
 static inline void CompositeMathematics(const MagickPixelPacket *p,
-  const MagickPixelPacket *q,const GeometryInfo *args,
-  MagickPixelPacket *composite)
+  const MagickPixelPacket *q,const ChannelType channel, const GeometryInfo
+  *args, MagickPixelPacket *composite)
 {
   MagickRealType
+    Sa,
     Da,
-    gamma,
-    Sa;
+    gamma;
 
-  Sa=1.0-QuantumScale*GetOpacityPixelComponent(p);
+  Sa=1.0-QuantumScale*GetOpacityPixelComponent(p); /* ??? - AT */
   Da=1.0-QuantumScale*q->opacity;
-  gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
-  composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*Mathematics(QuantumScale*p->index*Sa,Sa,QuantumScale*
-      q->index*Da,Da,args);
+  if ( (channel & SyncChannels) != 0 ) {
+    gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+    composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*Mathematics(QuantumScale*p->index*Sa,Sa,QuantumScale*
+        q->index*Da,Da,args);
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=QuantumRange*(1.0-Mathematics(Sa,1.0,Da,1.0,args));
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=QuantumRange*
+          Mathematics(QuantumScale*p->index,1.0,QuantumScale*q->index,1.0,args);
+  }
+
+}
+
+static inline void CompositePlus(const MagickPixelPacket *p,
+  const MagickPixelPacket *q,const ChannelType channel,
+  MagickPixelPacket *composite)
+{
+  if ( (channel & SyncChannels) != 0 ) {
+#if 0
+    MagickRealType
+      Sa,
+      Da,
+      gamma;
+
+    Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+    Da=1.0-QuantumScale*q->opacity;
+    /* gamma=RoundToUnity(Sa+Da);  ** is this correct?  - I do not think so! */
+    gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+    composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*(p->index*Sa+q->index*Da);
+#else
+    /* Actually this is just LinearDodge! */
+    CompositeLinearDodge(p,q,composite);
+#endif
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=p->opacity+q->opacity-QuantumRange;
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=p->index+q->index;
+  }
 }
 
 static inline MagickRealType Minus(const MagickRealType Sca,
-  const MagickRealType Dca)
+  const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
 {
-  return(Sca-Dca);
+  return(Sca + Dca - 2*Dca*Sa);
 }
 
 static inline void CompositeMinus(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,const ChannelType channel,
+  MagickPixelPacket *composite)
 {
   MagickRealType
+    Sa,
     Da,
-    gamma,
-    Sa;
+    gamma;
 
-  Sa=1.0-QuantumScale*alpha;
-  Da=1.0-QuantumScale*beta;
-  gamma=RoundToUnity(Sa-Da);  /* is this correct?  - I do not think so! */
-  composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
-  gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
-  composite->red=gamma*Minus(p->red*Sa,q->red*Da);
-  composite->green=gamma*Minus(p->green*Sa,q->green*Da);
-  composite->blue=gamma*Minus(p->blue*Sa,q->blue*Da);
-  if (q->colorspace == CMYKColorspace)
-    composite->index=gamma*Minus(p->index*Sa,q->index*Da);
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
+  if ( (channel & SyncChannels) != 0 ) {
+    gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+    composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*Minus(p->index*Sa,Sa,q->index*Da,Da);
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=QuantumRange*(1.0-(Sa-Da));
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=p->index-q->index;
+  }
+}
+
+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-Da) + q*Da*(1-Sa));
+}
+
+static inline void CompositeModulusAdd(const MagickPixelPacket *p,
+  const MagickPixelPacket *q, const ChannelType channel,
+  MagickPixelPacket *composite)
+{
+  if ( (channel & SyncChannels) != 0 ) {
+    MagickRealType
+      Sa,
+      Da,
+      gamma;
+
+    Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+    Da=1.0-QuantumScale*q->opacity;
+    gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+    composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=ModulusAdd(p->index,Sa,q->index,Da);
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=QuantumRange-ModulusAdd(QuantumRange-p->opacity,
+           1.0,QuantumRange-q->opacity,1.0);
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=ModulusAdd(p->index,1.0,q->index,1.0);
+  }
+}
+
+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-Da) + q*Da*(1-Sa));
+}
+
+static inline void CompositeModulusSubtract(const MagickPixelPacket *p,
+  const MagickPixelPacket *q, const ChannelType channel,
+  MagickPixelPacket *composite)
+{
+  if ( (channel & SyncChannels) != 0 ) {
+    MagickRealType
+      Sa,
+      Da,
+      gamma;
+
+    Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+    Da=1.0-QuantumScale*q->opacity;
+    gamma = RoundToUnity(Sa+Da-Sa*Da);
+    composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=ModulusSubtract(p->index,Sa,q->index,Da);
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=QuantumRange-ModulusSubtract(QuantumRange-p->opacity,
+           1.0,QuantumRange-q->opacity,1.0);
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=ModulusSubtract(p->index,1.0,q->index,1.0);
+  }
 }
 
 static  inline MagickRealType Multiply(const MagickRealType Sca,
@@ -813,68 +1071,77 @@ static  inline MagickRealType Multiply(const MagickRealType Sca,
 }
 
 static inline void CompositeMultiply(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,const ChannelType channel,
+  MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;
-  Da=1.0-QuantumScale*beta;
-  gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
-  composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*Multiply(QuantumScale*p->index*Sa,Sa,QuantumScale*
-      q->index*Da,Da);
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
+  if ( (channel & SyncChannels) != 0 ) {
+    gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+    composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*Multiply(QuantumScale*p->index*Sa,Sa,QuantumScale*
+        q->index*Da,Da);
+  }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=QuantumRange*(1.0-Sa*Da);
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=QuantumScale*p->index*q->index;
+  }
 }
 
 static inline MagickRealType Out(const MagickRealType p,
-  const MagickRealType alpha,const MagickRealType magick_unused(q),
-  const MagickRealType beta)
+  const MagickRealType Sa,const MagickRealType magick_unused(q),
+  const MagickRealType Da)
 {
-  return((1.0-QuantumScale*alpha)*p*QuantumScale*beta);
+  return(Sa*p*(1.0-Da));
 }
 
 static inline void CompositeOut(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
+    Sa,
+    Da,
     gamma;
 
-  gamma=(1.0-QuantumScale*alpha)*QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
+  gamma=Sa*(1.0-Da);
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
-  composite->red=gamma*Out(p->red,alpha,q->red,beta);
-  composite->green=gamma*Out(p->green,alpha,q->green,beta);
-  composite->blue=gamma*Out(p->blue,alpha,q->blue,beta);
+  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->index=gamma*Out(p->index,alpha,q->index,beta);
-}
-
-static inline void CompositeOver(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
-{
-  MagickPixelCompositeOver(p,alpha,q,beta,composite);
+    composite->index=gamma*Out(p->index,Sa,q->index,Da);
 }
 
 static MagickRealType PegtopLight(const MagickRealType Sca,
   const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
 {
   /*
-    PegTOP Soft-Light alternative: A continuous version of the Softlight
-    function, producing very similar results however it does not take into
-    account alpha channel.
+    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
 
@@ -886,16 +1153,15 @@ static MagickRealType PegtopLight(const MagickRealType Sca,
 }
 
 static inline void CompositePegtopLight(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
@@ -927,16 +1193,15 @@ static MagickRealType PinLight(const MagickRealType Sca,
 }
 
 static inline void CompositePinLight(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
@@ -951,42 +1216,53 @@ static inline void CompositePinLight(const MagickPixelPacket *p,
       q->index*Da,Da);
 }
 
-static inline void CompositePlus(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
-{
-  MagickPixelCompositePlus(p,alpha,q,beta,composite);
-}
-
 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 MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,const ChannelType channel,
+  MagickPixelPacket *composite)
 {
   MagickRealType
+    Sa,
     Da,
-    gamma,
-    Sa;
+    gamma;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
-  gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
-  composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
-  gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
-  composite->red=gamma*Screen(QuantumScale*p->red*Sa,QuantumScale*
-    q->red*Da);
-  composite->green=gamma*Screen(QuantumScale*p->green*Sa,QuantumScale*
-    q->green*Da);
-  composite->blue=gamma*Screen(QuantumScale*p->blue*Sa,QuantumScale*
-    q->blue*Da);
-  if (q->colorspace == CMYKColorspace)
-    composite->index=gamma*Screen(QuantumScale*p->index*Sa,QuantumScale*
-      q->index*Da);
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
+  if ( (channel & SyncChannels) != 0 ) {
+    gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+    composite->opacity=(MagickRealType) QuantumRange*(1.0-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->index=gamma*Screen(p->index*Sa,q->index*Da);
+    }
+  else { /* handle channels as separate grayscale channels */
+    if ( (channel | AlphaChannel) != 0 )
+      composite->opacity=QuantumRange*(1.0-Screen(Sa,Da));
+    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 | IndexChannel) != 0 && q->colorspace == CMYKColorspace)
+      composite->index=QuantumRange*Screen(QuantumScale*p->index,
+           QuantumScale*q->index);
+  }
 }
 
 static MagickRealType SoftLight(const MagickRealType Sca,
@@ -994,7 +1270,7 @@ static MagickRealType SoftLight(const MagickRealType Sca,
 {
 #if 0
   /*
-    Oct 2004 SVG specification -- spec discovered to be incorrect
+    Oct 2004 SVG specification -- was found to be incorrect
     See  http://lists.w3.org/Archives/Public/www-svg/2009Feb/0014.html.
   */
   if (2.0*Sca < Sa)
@@ -1027,16 +1303,15 @@ static MagickRealType SoftLight(const MagickRealType Sca,
 }
 
 static inline void CompositeSoftLight(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
@@ -1051,33 +1326,14 @@ static inline void CompositeSoftLight(const MagickPixelPacket *p,
       q->index*Da,Da);
 }
 
-static inline MagickRealType Subtract(const MagickRealType p,
-  const MagickRealType magick_unused(alpha),const MagickRealType q,
-  const MagickRealType magick_unused(beta))
-{
-  MagickRealType
-    pixel;
-
-  pixel=p-q;
-  if (pixel < 0.0)
-    pixel+=(QuantumRange+1.0);
-  return(pixel);
-}
-
-static inline void CompositeSubtract(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
-{
-  composite->red=Subtract(p->red,alpha,q->red,beta);
-  composite->green=Subtract(p->green,alpha,q->green,beta);
-  composite->blue=Subtract(p->blue,alpha,q->blue,beta);
-  if (q->colorspace == CMYKColorspace)
-    composite->index=Subtract(p->index,alpha,q->index,beta);
-}
-
+/*
+  Depreciated
+  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
+*/
 static inline MagickRealType Threshold(const MagickRealType p,
-  const MagickRealType magick_unused(alpha),const MagickRealType q,
-  const MagickRealType magick_unused(beta),const MagickRealType threshold,
+  const MagickRealType q,const MagickRealType threshold,
   const MagickRealType amount)
 {
   MagickRealType
@@ -1090,19 +1346,19 @@ static inline MagickRealType Threshold(const MagickRealType p,
 }
 
 static inline void CompositeThreshold(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,const MagickRealType threshold,
+  const MagickPixelPacket *q,const MagickRealType threshold,
   const MagickRealType amount,MagickPixelPacket *composite)
 {
-  composite->red=Threshold(p->red,alpha,q->red,beta,threshold,amount);
-  composite->green=Threshold(p->green,alpha,q->green,beta,threshold,amount);
-  composite->blue=Threshold(p->blue,alpha,q->blue,beta,threshold,amount);
-  composite->opacity=(MagickRealType) QuantumRange-
-    Threshold(p->opacity,alpha,q->opacity,beta,threshold,amount);
+  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->opacity=QuantumRange-Threshold(p->opacity,q->opacity,
+       threshold,amount);
   if (q->colorspace == CMYKColorspace)
-    composite->index=Threshold(p->index,alpha,q->index,beta,threshold,amount);
+    composite->index=Threshold(p->index,q->index,threshold,amount);
 }
 
+
 static MagickRealType VividLight(const MagickRealType Sca,
   const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
 {
@@ -1120,16 +1376,15 @@ static MagickRealType VividLight(const MagickRealType Sca,
 }
 
 static inline void CompositeVividLight(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
   gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
@@ -1151,21 +1406,17 @@ static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
 }
 
 static inline void CompositeXor(const MagickPixelPacket *p,
-  const MagickRealType alpha,const MagickPixelPacket *q,
-  const MagickRealType beta,MagickPixelPacket *composite)
+  const MagickPixelPacket *q,MagickPixelPacket *composite)
 {
   MagickRealType
     Da,
     gamma,
     Sa;
 
-  Sa=1.0-QuantumScale*alpha;  /* simplify and speed up equations */
-  Da=1.0-QuantumScale*beta;
+  Sa=1.0-QuantumScale*p->opacity;  /* simplify and speed up equations */
+  Da=1.0-QuantumScale*q->opacity;
   gamma=Sa+Da-2*Sa*Da;        /* Xor blend mode X=0,Y=1,Z=1 */
   composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
-  /*
-    Optimized by multipling QuantumRange taken from 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);
@@ -1264,7 +1515,7 @@ MagickExport MagickBooleanType CompositeImage(Image *image,
 }
 
 MagickExport MagickBooleanType CompositeImageChannel(Image *image,
-  const ChannelType magick_unused(channel),const CompositeOperator compose,
+  const ChannelType channel,const CompositeOperator compose,
   const Image *composite_image,const long x_offset,const long y_offset)
 {
 #define CompositeImageTag  "Composite/Image"
@@ -1735,7 +1986,7 @@ MagickExport MagickBooleanType CompositeImageChannel(Image *image,
           (void) ResamplePixelColor(resample_filter,(double) offset.x,
             (double) offset.y,&pixel);
           /*
-            Mask with 'invalid pixel mask' in alpha channel.
+            Mask with the 'invalid pixel mask' in alpha channel.
           */
           pixel.opacity=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
             pixel.opacity)*(1.0-QuantumScale*GetOpacityPixelComponent(p)));
@@ -1835,6 +2086,7 @@ MagickExport MagickBooleanType CompositeImageChannel(Image *image,
     {
       /*
         Determine the amount and threshold.
+        This Composition method is depreciated
       */
       value=GetImageArtifact(composite_image,"compose:args");
       if (value != (char *) NULL)
@@ -2038,12 +2290,7 @@ MagickExport MagickBooleanType CompositeImageChannel(Image *image,
         }
       switch (compose)
       {
-        case AddCompositeOp:
-        {
-          CompositeAdd(&source,source.opacity,&destination,destination.opacity,
-            &composite);
-          break;
-        }
+        /* Duff-Porter Compositions */
         case ClearCompositeOp:
         {
           CompositeClear(&destination,&composite);
@@ -2056,209 +2303,200 @@ MagickExport MagickBooleanType CompositeImageChannel(Image *image,
           composite=source;
           break;
         }
-        case ChangeMaskCompositeOp:
-        {
-          if ((composite.opacity > ((MagickRealType) QuantumRange/2.0)) ||
-              (IsMagickColorSimilar(&source,&destination) != MagickFalse))
-            composite.opacity=(MagickRealType) TransparentOpacity;
-          else
-            composite.opacity=(MagickRealType) OpaqueOpacity;
-          break;
-        }
-        case DivideCompositeOp:
-        {
-          CompositeDivide(&source,source.opacity,&destination,
-            destination.opacity,&composite);
-          break;
-        }
+        case NoCompositeOp:
         case DstCompositeOp:
           break;
         case OverCompositeOp:
         case SrcOverCompositeOp:
         {
-          CompositeOver(&source,source.opacity,&destination,destination.opacity,
-            &composite);
+          MagickPixelCompositeOver(&source,source.opacity,
+               &destination,destination.opacity,&composite);
           break;
         }
         case DstOverCompositeOp:
         {
-          CompositeOver(&destination,destination.opacity,&source,source.opacity,
-            &composite);
+          MagickPixelCompositeOver(&destination,destination.opacity,
+               &source,source.opacity,&composite);
           break;
         }
         case SrcInCompositeOp:
         case InCompositeOp:
         {
-          CompositeIn(&source,source.opacity,&destination,destination.opacity,
-            &composite);
+          CompositeIn(&source,&destination,&composite);
           break;
         }
         case DstInCompositeOp:
         {
-          CompositeIn(&destination,destination.opacity,&source,source.opacity,
-            &composite);
+          CompositeIn(&destination,&source,&composite);
           break;
         }
         case OutCompositeOp:
         case SrcOutCompositeOp:
         {
-          CompositeOut(&source,source.opacity,&destination,destination.opacity,
-            &composite);
+          CompositeOut(&source,&destination,&composite);
           break;
         }
         case DstOutCompositeOp:
         {
-          CompositeOut(&destination,destination.opacity,&source,source.opacity,
-            &composite);
+          CompositeOut(&destination,&source,&composite);
           break;
         }
         case AtopCompositeOp:
         case SrcAtopCompositeOp:
         {
-          CompositeAtop(&source,source.opacity,&destination,destination.opacity,
-            &composite);
+          CompositeAtop(&source,&destination,&composite);
           break;
         }
         case DstAtopCompositeOp:
         {
-          CompositeAtop(&destination,destination.opacity,&source,source.opacity,
-            &composite);
+          CompositeAtop(&destination,&source,&composite);
           break;
         }
         case XorCompositeOp:
         {
-          CompositeXor(&source,source.opacity,&destination,destination.opacity,
-            &composite);
+          CompositeXor(&source,&destination,&composite);
           break;
         }
+        /* Mathematical Compositions */
         case PlusCompositeOp:
         {
-          CompositePlus(&source,source.opacity,&destination,destination.opacity,
-            &composite);
+          CompositePlus(&source,&destination,channel,&composite);
+          break;
+        }
+        case MinusCompositeOp:
+        {
+          CompositeMinus(&source,&destination,
+            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,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeMultiply(&source,&destination,channel,&composite);
           break;
         }
         case ScreenCompositeOp:
         {
-          CompositeScreen(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeScreen(&source,&destination,channel,&composite);
+          break;
+        }
+        case DivideCompositeOp:
+        {
+          CompositeDivide(&source,&destination,channel,&composite);
           break;
         }
         case DarkenCompositeOp:
         {
-          CompositeDarken(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeDarken(&source,&destination,channel,&composite);
           break;
         }
         case LightenCompositeOp:
         {
-          CompositeLighten(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeLighten(&source,&destination,channel,&composite);
           break;
         }
+        case MathematicsCompositeOp:
+        {
+          CompositeMathematics(&source,&destination,channel,&geometry_info,
+               &composite);
+          break;
+        }
+        /* Lighting Compositions */
         case ColorDodgeCompositeOp:
         {
-          CompositeColorDodge(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeColorDodge(&source,&destination,&composite);
           break;
         }
         case ColorBurnCompositeOp:
         {
-          CompositeColorBurn(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeColorBurn(&source,&destination,&composite);
           break;
         }
         case LinearDodgeCompositeOp:
         {
-          CompositeLinearDodge(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeLinearDodge(&source,&destination,&composite);
           break;
         }
         case LinearBurnCompositeOp:
         {
-          CompositeLinearBurn(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeLinearBurn(&source,&destination,&composite);
           break;
         }
         case HardLightCompositeOp:
         {
-          CompositeHardLight(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeHardLight(&source,&destination,&composite);
           break;
         }
         case OverlayCompositeOp:
         {
-          /*
-            Reversed HardLight.
-          */
-          CompositeHardLight(&destination,destination.opacity,&source,
-            source.opacity,&composite);
+          /* Overlay = Reversed HardLight. */
+          CompositeHardLight(&destination,&source,&composite);
           break;
         }
         case SoftLightCompositeOp:
         {
-          CompositeSoftLight(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeSoftLight(&source,&destination,&composite);
           break;
         }
         case LinearLightCompositeOp:
         {
-          CompositeLinearLight(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeLinearLight(&source,&destination,&composite);
           break;
         }
         case PegtopLightCompositeOp:
         {
-          CompositePegtopLight(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositePegtopLight(&source,&destination,&composite);
           break;
         }
         case VividLightCompositeOp:
         {
-          CompositeVividLight(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeVividLight(&source,&destination,&composite);
           break;
         }
         case PinLightCompositeOp:
         {
-          CompositePinLight(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositePinLight(&source,&destination,&composite);
           break;
         }
-        case DifferenceCompositeOp:
-        {
-          CompositeDifference(&source,source.opacity,&destination,
-            destination.opacity,&composite);
-          break;
-        }
-        case ExclusionCompositeOp:
-        {
-          CompositeExclusion(&source,source.opacity,&destination,
-            destination.opacity,&composite);
-          break;
-        }
-        case MinusCompositeOp:
+        /* Other Composition */
+        case ChangeMaskCompositeOp:
         {
-          CompositeMinus(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          if ((composite.opacity > ((MagickRealType) QuantumRange/2.0)) ||
+              (IsMagickColorSimilar(&source,&destination) != MagickFalse))
+            composite.opacity=(MagickRealType) TransparentOpacity;
+          else
+            composite.opacity=(MagickRealType) OpaqueOpacity;
           break;
         }
         case BumpmapCompositeOp:
         {
           if (source.opacity == TransparentOpacity)
             break;
-          CompositeBumpmap(&source,source.opacity,&destination,
-            destination.opacity,&composite);
+          CompositeBumpmap(&source,&destination,&composite);
           break;
         }
         case DissolveCompositeOp:
         {
-          CompositeOver(&source,(MagickRealType) (QuantumRange-source_dissolve*
+          MagickPixelCompositeOver(&source,(MagickRealType) (QuantumRange-source_dissolve*
             (QuantumRange-source.opacity)),&destination,(MagickRealType)
             (QuantumRange-destination_dissolve*(QuantumRange-
             destination.opacity)),&composite);
@@ -2270,22 +2508,9 @@ MagickExport MagickBooleanType CompositeImageChannel(Image *image,
             destination_dissolve,&composite);
           break;
         }
-        case MathematicsCompositeOp:
-        {
-          CompositeMathematics(&source,&destination,&geometry_info,&composite);
-          break;
-        }
-        case BlurCompositeOp:
-        case DisplaceCompositeOp:
-        case DistortCompositeOp:
-        {
-          composite=source;
-          break;
-        }
         case ThresholdCompositeOp:
         {
-          CompositeThreshold(&source,source.opacity,&destination,
-            destination.opacity,threshold,amount,&composite);
+          CompositeThreshold(&source,&destination,threshold,amount,&composite);
           break;
         }
         case ModulateCompositeOp:
@@ -2343,12 +2568,6 @@ MagickExport MagickBooleanType CompositeImageChannel(Image *image,
             composite.opacity=source.opacity;
           break;
         }
-        case SubtractCompositeOp:
-        {
-          CompositeSubtract(&source,source.opacity,&destination,
-            destination.opacity,&composite);
-          break;
-        }
         case LuminizeCompositeOp:
         {
           if (source.opacity == TransparentOpacity)
@@ -2423,6 +2642,14 @@ MagickExport MagickBooleanType CompositeImageChannel(Image *image,
           composite.index=source.index;
           break;
         }
+        /* compose methods that are already handled */
+        case BlurCompositeOp:
+        case DisplaceCompositeOp:
+        case DistortCompositeOp:
+        {
+          composite=source;
+          break;
+        }
         default:
           break;
       }
index b66763efbbcea6f1df6d7e91f9fbf093c0d638ab..2f92e5c5f5b7cbf46c11163237f60b6dbfa8badf 100644 (file)
@@ -26,7 +26,7 @@ typedef enum
 {
   UndefinedCompositeOp,
   NoCompositeOp,
-  AddCompositeOp,
+  ModulusAddCompositeOp,
   AtopCompositeOp,
   BlendCompositeOp,
   BumpmapCompositeOp,
@@ -76,7 +76,7 @@ typedef enum
   SrcInCompositeOp,
   SrcOutCompositeOp,
   SrcOverCompositeOp,
-  SubtractCompositeOp,
+  ModulusSubtractCompositeOp,
   ThresholdCompositeOp,
   XorCompositeOp,
   DivideCompositeOp,
@@ -90,6 +90,10 @@ typedef enum
   MathematicsCompositeOp
 } CompositeOperator;
 
+/* Depreciated Method Names for backward compatibility */
+#define AddCompositeOp       ModulusAddCompositeOp
+#define SubtractCompositeOp  ModulusSubtractCompositeOp
+
 extern MagickExport MagickBooleanType
   CompositeImage(Image *,const CompositeOperator,const Image *,const long,
     const long),
index 4f85dadd860cb84f6e83932baafb29169316ad03..a4c171b75b5c67ee38415a71974fe2dab6ede674 100644 (file)
@@ -691,7 +691,6 @@ static const OptionInfo
   ComposeOptions[] =
   {
     { "Undefined", (long) UndefinedCompositeOp, MagickTrue },
-    { "Add", (long) AddCompositeOp, MagickFalse },
     { "Atop", (long) AtopCompositeOp, MagickFalse },
     { "Blend", (long) BlendCompositeOp, MagickFalse },
     { "Blur", (long) BlurCompositeOp, MagickFalse },
@@ -734,6 +733,8 @@ static const OptionInfo
     { "Mathematics", (long) MathematicsCompositeOp, MagickFalse },
     { "Minus", (long) MinusCompositeOp, MagickFalse },
     { "Modulate", (long) ModulateCompositeOp, MagickFalse },
+    { "ModulusAdd", (long) ModulusAddCompositeOp, MagickFalse },
+    { "ModulusSubtract", (long) ModulusSubtractCompositeOp, MagickFalse },
     { "Multiply", (long) MultiplyCompositeOp, MagickFalse },
     { "None", (long) NoCompositeOp, MagickFalse },
     { "Out", (long) OutCompositeOp, MagickFalse },
@@ -752,10 +753,11 @@ static const OptionInfo
     { "SrcOut", (long) SrcOutCompositeOp, MagickFalse },
     { "SrcOver", (long) SrcOverCompositeOp, MagickFalse },
     { "Src", (long) SrcCompositeOp, MagickFalse },
-    { "Subtract", (long) SubtractCompositeOp, MagickFalse },
-    { "Threshold", (long) ThresholdCompositeOp, MagickTrue }, /* depreciate */
     { "VividLight", (long) VividLightCompositeOp, MagickFalse },
     { "Xor", (long) XorCompositeOp, MagickFalse },
+    { "Add", (long) AddCompositeOp, MagickTrue }, /* depreciate */
+    { "Subtract", (long) SubtractCompositeOp, MagickTrue }, /* depreciate */
+    { "Threshold", (long) ThresholdCompositeOp, MagickTrue }, /* depreciate */
     { (char *) NULL, (long) UndefinedCompositeOp, MagickFalse }
   },
   CompressOptions[] =