]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/layer.c
(no commit message)
[imagemagick] / MagickCore / layer.c
index d1b2d43f47e319f604ff58935643404917a7106a..4e6ab207fc8b293aa4e9617c704d7fac29cf2fca 100644 (file)
 %                      MagickCore Image Layering Methods                      %
 %                                                                             %
 %                              Software Design                                %
-%                                John Cristy                                  %
+%                                   Cristy                                    %
 %                              Anthony Thyssen                                %
 %                               January 2006                                  %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
@@ -40,6 +40,7 @@
 #include "MagickCore/studio.h"
 #include "MagickCore/artifact.h"
 #include "MagickCore/cache.h"
+#include "MagickCore/channel.h"
 #include "MagickCore/color.h"
 #include "MagickCore/color-private.h"
 #include "MagickCore/composite.h"
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
 %  ClearBounds() Clear the area specified by the bounds in an image to
-%  transparency.  This typically used to handle Background Disposal
-%  for the previous frame in an animation sequence.
+%  transparency.  This typically used to handle Background Disposal for the
+%  previous frame in an animation sequence.
 %
-%  WARNING: no bounds checks are performed, except for the null or
-%  missed image, for images that don't change. in all other cases
-%  bound must fall within the image.
+%  Warning: no bounds checks are performed, except for the null or missed
+%  image, for images that don't change. in all other cases bound must fall
+%  within the image.
 %
 %  The format is:
 %
-%      void ClearBounds(Image *image,RectangleInfo *bounds)
+%      void ClearBounds(Image *image,RectangleInfo *bounds,
+%        ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
 %
 %    o bounds: the area to be clear within the imag image
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
-static void ClearBounds(Image *image,RectangleInfo *bounds)
+static void ClearBounds(Image *image,RectangleInfo *bounds,
+  ExceptionInfo *exception)
 {
-  ExceptionInfo
-    *exception;
-
   ssize_t
     y;
 
   if (bounds->x < 0)
     return;
-  exception=(&image->exception);
-  if (image->matte == MagickFalse)
+  if (image->alpha_trait != BlendPixelTrait)
     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
   for (y=0; y < (ssize_t) bounds->height; y++)
   {
@@ -115,7 +116,7 @@ static void ClearBounds(Image *image,RectangleInfo *bounds)
       *restrict q;
 
     q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
-    if (q == (const Quantum *) NULL)
+    if (q == (Quantum *) NULL)
       break;
     for (x=0; x < (ssize_t) bounds->width; x++)
     {
@@ -143,6 +144,10 @@ static void ClearBounds(Image *image,RectangleInfo *bounds)
 %  to check if a proposed disposal method will work successfully to generate
 %  the second frame image from the first disposed form of the previous frame.
 %
+%  Warning: no bounds checks are performed, except for the null or missed
+%  image, for images that don't change. in all other cases bound must fall
+%  within the image.
+%
 %  The format is:
 %
 %      MagickBooleanType IsBoundsCleared(const Image *image1,
@@ -156,37 +161,26 @@ static void ClearBounds(Image *image,RectangleInfo *bounds)
 %
 %    o exception: return any errors or warnings in this structure.
 %
-%  WARNING: no bounds checks are performed, except for the null or
-%  missed image, for images that don't change. in all other cases
-%  bound must fall within the image.
-%
 */
 static MagickBooleanType IsBoundsCleared(const Image *image1,
   const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception)
 {
-  register ssize_t
-    x;
-
   register const Quantum
     *p,
     *q;
 
+  register ssize_t
+    x;
+
   ssize_t
     y;
 
-#if 0
-  assert(image1->matte==MagickTrue);
-  assert(image2->matte==MagickTrue);
-#endif
-
-  if ( bounds->x< 0 ) return(MagickFalse);
-
+  if (bounds->x < 0)
+    return(MagickFalse);
   for (y=0; y < (ssize_t) bounds->height; y++)
   {
-    p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,
-      exception);
-    q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,
-      exception);
+    p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,exception);
+    q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,exception);
     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
       break;
     for (x=0; x < (ssize_t) bounds->width; x++)
@@ -254,8 +248,6 @@ MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   assert(exception != (ExceptionInfo *) NULL);
   assert(exception->signature == MagickSignature);
-
-  /* initialise first image */
   next=GetFirstImageInList(image);
   bounds=next->page;
   if (bounds.width == 0)
@@ -276,16 +268,16 @@ MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
     exception);
   if (coalesce_image == (Image *) NULL)
     return((Image *) NULL);
+  (void) SetImageBackgroundColor(coalesce_image,exception);
+  coalesce_image->alpha_trait=next->alpha_trait;
   coalesce_image->page=bounds;
   coalesce_image->dispose=NoneDispose;
-  coalesce_image->background_color.alpha=(Quantum) TransparentAlpha;
-  (void) SetImageBackgroundColor(coalesce_image);
   /*
     Coalesce rest of the images.
   */
   dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
-  (void) CompositeImage(coalesce_image,CopyCompositeOp,next,next->page.x,
-    next->page.y);
+  (void) CompositeImage(coalesce_image,next,CopyCompositeOp,MagickTrue,
+    next->page.x,next->page.y,exception);
   next=GetNextImageInList(next);
   for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
   {
@@ -327,7 +319,7 @@ MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
       Clear the overlaid area of the coalesced bounds for background disposal
     */
     if (next->previous->dispose == BackgroundDispose)
-      ClearBounds(dispose_image, &bounds);
+      ClearBounds(dispose_image,&bounds,exception);
     /*
       Next image is the dispose image, overlaid with next frame in sequence.
     */
@@ -335,9 +327,9 @@ MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
     coalesce_image->next->previous=coalesce_image;
     previous=coalesce_image;
     coalesce_image=GetNextImageInList(coalesce_image);
-    coalesce_image->matte=MagickTrue;
-    (void) CompositeImage(coalesce_image,next->matte != MagickFalse ?
-      OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
+    (void) CompositeImage(coalesce_image,next,
+      next->alpha_trait == BlendPixelTrait ? OverCompositeOp : CopyCompositeOp,
+      MagickTrue,next->page.x,next->page.y,exception);
     (void) CloneImageProfiles(coalesce_image,next);
     (void) CloneImageProperties(coalesce_image,next);
     (void) CloneImageArtifacts(coalesce_image,next);
@@ -345,7 +337,7 @@ MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
     /*
       If a pixel goes opaque to transparent, use background dispose.
     */
-    if (IsBoundsCleared(previous,coalesce_image,&bounds,exception))
+    if (IsBoundsCleared(previous,coalesce_image,&bounds,exception) != MagickFalse)
       coalesce_image->dispose=BackgroundDispose;
     else
       coalesce_image->dispose=NoneDispose;
@@ -367,8 +359,8 @@ MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
 %  DisposeImages() returns the coalesced frames of a GIF animation as it would
-%  appear after the GIF dispose method of that frame has been applied.  That
-%  is it returned the appearance of each frame before the next is overlaid.
+%  appear after the GIF dispose method of that frame has been applied.  That is
+%  it returned the appearance of each frame before the next is overlaid.
 %
 %  The format of the DisposeImages method is:
 %
@@ -376,45 +368,46 @@ MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
-%    o image: the image sequence.
+%    o images: the image sequence.
 %
 %    o exception: return any errors or warnings in this structure.
 %
 */
-MagickExport Image *DisposeImages(const Image *image,ExceptionInfo *exception)
+MagickExport Image *DisposeImages(const Image *images,ExceptionInfo *exception)
 {
   Image
     *dispose_image,
     *dispose_images;
 
-  register Image
-    *curr;
-
   RectangleInfo
     bounds;
 
+  register Image
+    *image,
+    *next;
+
   /*
     Run the image through the animation sequence
   */
-  assert(image != (Image *) NULL);
-  assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  assert(images != (Image *) NULL);
+  assert(images->signature == MagickSignature);
+  if (images->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
   assert(exception != (ExceptionInfo *) NULL);
   assert(exception->signature == MagickSignature);
-  curr=GetFirstImageInList(image);
-  dispose_image=CloneImage(curr,curr->page.width,curr->page.height,MagickTrue,
-    exception);
+  image=GetFirstImageInList(images);
+  dispose_image=CloneImage(image,image->page.width,image->page.height,
+    MagickTrue,exception);
   if (dispose_image == (Image *) NULL)
     return((Image *) NULL);
-  dispose_image->page=curr->page;
+  dispose_image->page=image->page;
   dispose_image->page.x=0;
   dispose_image->page.y=0;
   dispose_image->dispose=NoneDispose;
   dispose_image->background_color.alpha=(Quantum) TransparentAlpha;
-  (void) SetImageBackgroundColor(dispose_image);
+  (void) SetImageBackgroundColor(dispose_image,exception);
   dispose_images=NewImageList();
-  for ( ; curr != (Image *) NULL; curr=GetNextImageInList(curr))
+  for (next=image; image != (Image *) NULL; image=GetNextImageInList(image))
   {
     Image
       *current_image;
@@ -429,17 +422,17 @@ MagickExport Image *DisposeImages(const Image *image,ExceptionInfo *exception)
         dispose_image=DestroyImage(dispose_image);
         return((Image *) NULL);
       }
-    (void) CompositeImage(current_image,curr->matte != MagickFalse ?
-      OverCompositeOp : CopyCompositeOp,curr,curr->page.x,curr->page.y);
-
+    (void) CompositeImage(current_image,next,
+      next->alpha_trait == BlendPixelTrait ? OverCompositeOp : CopyCompositeOp,
+      MagickTrue,next->page.x,next->page.y,exception);
     /*
       Handle Background dispose: image is displayed for the delay period.
     */
-    if (curr->dispose == BackgroundDispose)
+    if (next->dispose == BackgroundDispose)
       {
-        bounds=curr->page;
-        bounds.width=curr->columns;
-        bounds.height=curr->rows;
+        bounds=next->page;
+        bounds.width=next->columns;
+        bounds.height=next->rows;
         if (bounds.x < 0)
           {
             bounds.width+=bounds.x;
@@ -454,18 +447,18 @@ MagickExport Image *DisposeImages(const Image *image,ExceptionInfo *exception)
           }
         if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
           bounds.height=current_image->rows-bounds.y;
-        ClearBounds(current_image,&bounds);
+        ClearBounds(current_image,&bounds,exception);
       }
     /*
       Select the appropriate previous/disposed image.
     */
-    if (curr->dispose == PreviousDispose)
+    if (next->dispose == PreviousDispose)
       current_image=DestroyImage(current_image);
     else
       {
         dispose_image=DestroyImage(dispose_image);
         dispose_image=current_image;
-        current_image=(Image *)NULL;
+        current_image=(Image *) NULL;
       }
     /*
       Save the dispose image just calculated for return.
@@ -481,12 +474,12 @@ MagickExport Image *DisposeImages(const Image *image,ExceptionInfo *exception)
           dispose_image=DestroyImage(dispose_image);
           return((Image *) NULL);
         }
-      (void) CloneImageProfiles(dispose,curr);
-      (void) CloneImageProperties(dispose,curr);
-      (void) CloneImageArtifacts(dispose,curr);
+      (void) CloneImageProfiles(dispose,next);
+      (void) CloneImageProperties(dispose,next);
+      (void) CloneImageArtifacts(dispose,next);
       dispose->page.x=0;
       dispose->page.y=0;
-      dispose->dispose=curr->dispose;
+      dispose->dispose=next->dispose;
       AppendImageToList(&dispose_images,dispose);
     }
   }
@@ -513,7 +506,7 @@ MagickExport Image *DisposeImages(const Image *image,ExceptionInfo *exception)
 %
 %  The format of the ComparePixels method is:
 %
-%      MagickBooleanType *ComparePixels(const ImageLayerMethod method,
+%      MagickBooleanType *ComparePixels(const LayerMethod method,
 %        const PixelInfo *p,const PixelInfo *q)
 %
 %  A description of each parameter follows:
@@ -525,10 +518,10 @@ MagickExport Image *DisposeImages(const Image *image,ExceptionInfo *exception)
 %
 */
 
-static MagickBooleanType ComparePixels(const ImageLayerMethod method,
+static MagickBooleanType ComparePixels(const LayerMethod method,
   const PixelInfo *p,const PixelInfo *q)
 {
-  MagickRealType
+  double
     o1,
     o2;
 
@@ -538,22 +531,20 @@ static MagickBooleanType ComparePixels(const ImageLayerMethod method,
   if (method == CompareAnyLayer)
     return((MagickBooleanType)(IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
 
-  o1 = (p->matte != MagickFalse) ? p->alpha : OpaqueAlpha;
-  o2 = (q->matte != MagickFalse) ? q->alpha : OpaqueAlpha;
-
+  o1 = (p->alpha_trait == BlendPixelTrait) ? p->alpha : OpaqueAlpha;
+  o2 = (q->alpha_trait == BlendPixelTrait) ? q->alpha : OpaqueAlpha;
   /*
-    Pixel goes from opaque to transprency
+    Pixel goes from opaque to transprency.
   */
   if (method == CompareClearLayer)
-    return((MagickBooleanType) ( (o1 <= ((MagickRealType) QuantumRange/2.0)) &&
-      (o2 > ((MagickRealType) QuantumRange/2.0)) ) );
-
+    return((MagickBooleanType) ( (o1 <= ((double) QuantumRange/2.0)) &&
+      (o2 > ((double) QuantumRange/2.0)) ) );
   /*
-    overlay would change first pixel by second
+    Overlay would change first pixel by second.
   */
   if (method == CompareOverlayLayer)
     {
-      if (o2 > ((MagickRealType) QuantumRange/2.0))
+      if (o2 > ((double) QuantumRange/2.0))
         return MagickFalse;
       return((MagickBooleanType) (IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
     }
@@ -581,7 +572,7 @@ static MagickBooleanType ComparePixels(const ImageLayerMethod method,
 %
 %  The format of the CompareImagesBounds method is:
 %
-%      RectangleInfo *CompareImagesBounds(const ImageLayerMethod method,
+%      RectangleInfo *CompareImagesBounds(const LayerMethod method,
 %        const Image *image1, const Image *image2, ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
@@ -595,8 +586,8 @@ static MagickBooleanType ComparePixels(const ImageLayerMethod method,
 %
 */
 
-static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2,
-  const ImageLayerMethod method,ExceptionInfo *exception)
+static RectangleInfo CompareImagesBounds(const Image *image1,
+  const Image *image2,const LayerMethod method,ExceptionInfo *exception)
 {
   RectangleInfo
     bounds;
@@ -625,12 +616,12 @@ static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2
     p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
     q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
     if ((p == (const Quantum *) NULL) ||
-        (q == (const Quantum *) NULL))
+        (q == (Quantum *) NULL))
       break;
     for (y=0; y < (ssize_t) image1->rows; y++)
     {
-      SetPixelInfo(image1,p,&pixel1);
-      SetPixelInfo(image2,q,&pixel2);
+      GetPixelInfoPixel(image1,p,&pixel1);
+      GetPixelInfoPixel(image2,q,&pixel2);
       if (ComparePixels(method,&pixel1,&pixel2))
         break;
       p+=GetPixelChannels(image1);
@@ -656,12 +647,12 @@ static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2
     p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
     q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
     if ((p == (const Quantum *) NULL) ||
-        (q == (const Quantum *) NULL))
+        (q == (Quantum *) NULL))
       break;
     for (y=0; y < (ssize_t) image1->rows; y++)
     {
-      SetPixelInfo(image1,p,&pixel1);
-      SetPixelInfo(image2,q,&pixel2);
+      GetPixelInfoPixel(image1,p,&pixel1);
+      GetPixelInfoPixel(image2,q,&pixel2);
       if (ComparePixels(method,&pixel1,&pixel2))
         break;
       p+=GetPixelChannels(image1);
@@ -676,12 +667,12 @@ static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2
     p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
     q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
     if ((p == (const Quantum *) NULL) ||
-        (q == (const Quantum *) NULL))
+        (q == (Quantum *) NULL))
       break;
     for (x=0; x < (ssize_t) image1->columns; x++)
     {
-      SetPixelInfo(image1,p,&pixel1);
-      SetPixelInfo(image2,q,&pixel2);
+      GetPixelInfoPixel(image1,p,&pixel1);
+      GetPixelInfoPixel(image2,q,&pixel2);
       if (ComparePixels(method,&pixel1,&pixel2))
         break;
       p+=GetPixelChannels(image1);
@@ -696,12 +687,12 @@ static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2
     p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
     q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
     if ((p == (const Quantum *) NULL) ||
-        (q == (const Quantum *) NULL))
+        (q == (Quantum *) NULL))
       break;
     for (x=0; x < (ssize_t) image1->columns; x++)
     {
-      SetPixelInfo(image1,p,&pixel1);
-      SetPixelInfo(image2,q,&pixel2);
+      GetPixelInfoPixel(image1,p,&pixel1);
+      GetPixelInfoPixel(image2,q,&pixel2);
       if (ComparePixels(method,&pixel1,&pixel2))
         break;
       p+=GetPixelChannels(image1);
@@ -727,7 +718,7 @@ static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2
 %
 %  CompareImagesLayers() compares each image with the next in a sequence and
 %  returns the minimum bounding region of all the pixel differences (of the
-%  ImageLayerMethod specified) it discovers.
+%  LayerMethod specified) it discovers.
 %
 %  Images do NOT have to be the same size, though it is best that all the
 %  images are 'coalesced' (images are all the same size, on a flattened
@@ -739,7 +730,7 @@ static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2
 %  The format of the CompareImagesLayers method is:
 %
 %      Image *CompareImagesLayers(const Image *images,
-%        const ImageLayerMethod method,ExceptionInfo *exception)
+%        const LayerMethod method,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -753,7 +744,7 @@ static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2
 */
 
 MagickExport Image *CompareImagesLayers(const Image *image,
-  const ImageLayerMethod method, ExceptionInfo *exception)
+  const LayerMethod method, ExceptionInfo *exception)
 {
   Image
     *image_a,
@@ -797,11 +788,12 @@ MagickExport Image *CompareImagesLayers(const Image *image,
       return((Image *) NULL);
     }
   image_a->background_color.alpha=(Quantum) TransparentAlpha;
-  (void) SetImageBackgroundColor(image_a);
+  (void) SetImageBackgroundColor(image_a,exception);
   image_a->page=next->page;
   image_a->page.x=0;
   image_a->page.y=0;
-  (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,next->page.y);
+  (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
+    next->page.y,exception);
   /*
     Compute the bounding box of changes for the later images
   */
@@ -816,10 +808,9 @@ MagickExport Image *CompareImagesLayers(const Image *image,
         bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
         return((Image *) NULL);
       }
-    (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,
-                           next->page.y);
+    (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
+      next->page.y,exception);
     bounds[i]=CompareImagesBounds(image_b,image_a,method,exception);
-
     image_b=DestroyImage(image_b);
     i++;
   }
@@ -883,7 +874,7 @@ MagickExport Image *CompareImagesLayers(const Image *image,
 %  The format of the OptimizeLayerFrames method is:
 %
 %      Image *OptimizeLayerFrames(const Image *image,
-%        const ImageLayerMethod method, ExceptionInfo *exception)
+%        const LayerMethod method, ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -912,7 +903,7 @@ MagickExport Image *CompareImagesLayers(const Image *image,
 #define DEBUG_OPT_FRAME 0
 
 static Image *OptimizeLayerFrames(const Image *image,
-  const ImageLayerMethod method, ExceptionInfo *exception)
+  const LayerMethod method, ExceptionInfo *exception)
 {
   ExceptionInfo
     *sans_exception;
@@ -952,13 +943,12 @@ static Image *OptimizeLayerFrames(const Image *image,
   assert(method == OptimizeLayer ||
          method == OptimizeImageLayer ||
          method == OptimizePlusLayer);
-
   /*
-    Are we allowed to add/remove frames from animation
+    Are we allowed to add/remove frames from animation?
   */
   add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
   /*
-    Ensure  all the images are the same size
+    Ensure  all the images are the same size.
   */
   curr=GetFirstImageInList(image);
   for (; curr != (Image *) NULL; curr=GetNextImageInList(curr))
@@ -1004,7 +994,7 @@ static Image *OptimizeLayerFrames(const Image *image,
   prev_image->dispose=NoneDispose;
 
   prev_image->background_color.alpha=(Quantum) TransparentAlpha;
-  (void) SetImageBackgroundColor(prev_image);
+  (void) SetImageBackgroundColor(prev_image,exception);
   /*
     Figure out the area of overlay of the first frame
     No pixel could be cleared as all pixels are already cleared.
@@ -1109,7 +1099,7 @@ static Image *OptimizeLayerFrames(const Image *image,
                 return((Image *) NULL);
               }
             dup_bounds=CompareImagesBounds(dup_image,curr,CompareClearLayer,exception);
-            ClearBounds(dup_image,&dup_bounds);
+            ClearBounds(dup_image,&dup_bounds,exception);
             try_bounds=CompareImagesBounds(dup_image,curr,CompareAnyLayer,exception);
             if ( cleared ||
                    dup_bounds.width*dup_bounds.height
@@ -1139,7 +1129,7 @@ static Image *OptimizeLayerFrames(const Image *image,
             return((Image *) NULL);
           }
         bgnd_bounds=bounds[i-1]; /* interum bounds of the previous image */
-        ClearBounds(bgnd_image,&bgnd_bounds);
+        ClearBounds(bgnd_image,&bgnd_bounds,exception);
         try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
         try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
 #if DEBUG_OPT_FRAME
@@ -1202,7 +1192,7 @@ static Image *OptimizeLayerFrames(const Image *image,
                     (double) bgnd_bounds.x,(double) bgnd_bounds.y );
 #endif
               }
-            ClearBounds(bgnd_image,&bgnd_bounds);
+            ClearBounds(bgnd_image,&bgnd_bounds,exception);
 #if DEBUG_OPT_FRAME
 /* Something strange is happening with a specific animation
  * CompareAnyLayers (normal method) and CompareClearLayers returns the whole
@@ -1488,7 +1478,7 @@ MagickExport void OptimizeImageTransparency(const Image *image,
   dispose_image->page.y=0;
   dispose_image->dispose=NoneDispose;
   dispose_image->background_color.alpha=(Quantum) TransparentAlpha;
-  (void) SetImageBackgroundColor(dispose_image);
+  (void) SetImageBackgroundColor(dispose_image,exception);
 
   while ( next != (Image *) NULL )
   {
@@ -1504,8 +1494,9 @@ MagickExport void OptimizeImageTransparency(const Image *image,
         dispose_image=DestroyImage(dispose_image);
         return;
       }
-    (void) CompositeImage(current_image,next->matte != MagickFalse ?
-      OverCompositeOp : CopyCompositeOp, next,next->page.x,next->page.y);
+    (void) CompositeImage(current_image,next,next->alpha_trait == BlendPixelTrait ?
+      OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
+      exception);
     /*
       At this point the image would be displayed, for the delay period
     **
@@ -1532,7 +1523,7 @@ MagickExport void OptimizeImageTransparency(const Image *image,
           }
         if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
           bounds.height=current_image->rows-bounds.y;
-        ClearBounds(current_image, &bounds);
+        ClearBounds(current_image, &bounds,exception);
       }
     if (next->dispose != PreviousDispose)
       {
@@ -1546,9 +1537,9 @@ MagickExport void OptimizeImageTransparency(const Image *image,
       Optimize Transparency of the next frame (if present)
     */
     next=GetNextImageInList(next);
-    if ( next != (Image *) NULL ) {
-      (void) CompositeImage(next, ChangeMaskCompositeOp,
-          dispose_image, -(next->page.x), -(next->page.y) );
+    if (next != (Image *) NULL) {
+      (void) CompositeImage(next,dispose_image,ChangeMaskCompositeOp,
+        MagickTrue,-(next->page.x),-(next->page.y),exception);
     }
   }
   dispose_image=DestroyImage(dispose_image);
@@ -1712,34 +1703,38 @@ MagickExport void RemoveZeroDelayLayers(Image **images,
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  CompositeLayers() compose first image sequence (source) over the second
-%  image sequence (destination), using the given compose method and offsets.
+%  CompositeLayers() compose the source image sequence over the destination
+%  image sequence, starting with the current image in both lists.
+%
+%  Each layer from the two image lists are composted together until the end of
+%  one of the image lists is reached.  The offset of each composition is also
+%  adjusted to match the virtual canvas offsets of each layer. As such the
+%  given offset is relative to the virtual canvas, and not the actual image.
+%
+%  Composition uses given x and y offsets, as the 'origin' location of the
+%  source images virtual canvas (not the real image) allowing you to compose a
+%  list of 'layer images' into the destiantioni images.  This makes it well
+%  sutiable for directly composing 'Clears Frame Animations' or 'Coaleased
+%  Animations' onto a static or other 'Coaleased Animation' destination image
+%  list.  GIF disposal handling is not looked at.
 %
-%  The pointers to the image list does not have to be the start of that image
-%  list, but may start somewhere in the middle.  Each layer from the two image
-%  lists are composted together until the end of one of the image lists is
-%  reached.  The offset of each composition is also adjusted to match the
-%  virtual canvas offsets of each layer. As such the given offset is relative
-%  to the virtual canvas, and not the actual image.
+%  Special case:- If one of the image sequences is the last image (just a
+%  single image remaining), that image is repeatally composed with all the
+%  images in the other image list.  Either the source or destination lists may
+%  be the single image, for this situation.
 %
-%  No GIF disposal handling is performed, so GIF animations should be
-%  coalesced before use.  However this not a requirement, and individual
-%  layer images may have any size or offset, for special compositions.
+%  In the case of a single destination image (or last image given), that image
+%  will ve cloned to match the number of images remaining in the source image
+%  list.
 %
-%  Special case:- If one of the image sequences is just a single image that
-%  image is repeatally composed with all the images in the other image list.
-%  Either the source or destination lists may be the single image, for this
-%  situation.
+%  This is equivelent to the "-layer Composite" Shell API operator.
 %
-%  The destination list will be expanded as needed to match number of source
-%  image overlaid (from current position to end of list).
 %
 %  The format of the CompositeLayers method is:
 %
-%      void CompositeLayers(Image *destination,
-%          const CompositeOperator compose, Image *source,
-%          const ssize_t x_offset, const ssize_t y_offset,
-%          ExceptionInfo *exception);
+%      void CompositeLayers(Image *destination, const CompositeOperator
+%      compose, Image *source, const ssize_t x_offset, const ssize_t y_offset,
+%      ExceptionInfo *exception);
 %
 %  A description of each parameter follows:
 %
@@ -1752,13 +1747,15 @@ MagickExport void RemoveZeroDelayLayers(Image **images,
 %    o exception: return any errors or warnings in this structure.
 %
 */
+
 static inline void CompositeCanvas(Image *destination,
-  const CompositeOperator compose, Image *source,ssize_t x_offset,
-  ssize_t y_offset )
+  const CompositeOperator compose,Image *source,ssize_t x_offset,
+  ssize_t y_offset,ExceptionInfo *exception)
 {
   x_offset+=source->page.x-destination->page.x;
   y_offset+=source->page.y-destination->page.y;
-  (void) CompositeImage(destination,compose,source,x_offset,y_offset);
+  (void) CompositeImage(destination,source,compose,MagickTrue,x_offset,
+    y_offset,exception);
 }
 
 MagickExport void CompositeLayers(Image *destination,
@@ -1773,31 +1770,32 @@ MagickExport void CompositeLayers(Image *destination,
   assert(exception->signature == MagickSignature);
   if (source->debug != MagickFalse || destination->debug != MagickFalse)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
-         source->filename, destination->filename);
+      source->filename, destination->filename);
 
   /*
     Overlay single source image over destation image/list
   */
-  if ( source->previous == (Image *) NULL && source->next == (Image *) NULL )
+  if ( source->next == (Image *) NULL )
     while ( destination != (Image *) NULL )
     {
-      CompositeCanvas(destination, compose, source, x_offset, y_offset);
+      CompositeCanvas(destination, compose, source, x_offset, y_offset,
+        exception);
       destination=GetNextImageInList(destination);
     }
 
   /*
-    Overlay source image list over single destination
-    Generating multiple clones of destination image to match source list.
+    Overlay source image list over single destination.
+    Multiple clones of destination image are created to match source list.
     Original Destination image becomes first image of generated list.
     As such the image list pointer does not require any change in caller.
     Some animation attributes however also needs coping in this case.
   */
-  else if ( destination->previous == (Image *) NULL &&
-            destination->next == (Image *) NULL )
+  else if ( destination->next == (Image *) NULL )
   {
     Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
 
-    CompositeCanvas(destination, compose, source, x_offset, y_offset);
+    CompositeCanvas(destination, compose, source, x_offset, y_offset,
+      exception);
     /* copy source image attributes ? */
     if ( source->next != (Image *) NULL )
       {
@@ -1812,7 +1810,8 @@ MagickExport void CompositeLayers(Image *destination,
            CloneImage(dest,0,0,MagickTrue,exception));
       destination=GetLastImageInList(destination);
 
-      CompositeCanvas(destination, compose, source, x_offset, y_offset);
+      CompositeCanvas(destination, compose, source, x_offset, y_offset,
+        exception);
       destination->delay = source->delay;
       destination->iterations = source->iterations;
       source=GetNextImageInList(source);
@@ -1827,7 +1826,8 @@ MagickExport void CompositeLayers(Image *destination,
   else
     while ( source != (Image *) NULL && destination != (Image *) NULL )
     {
-      CompositeCanvas(destination, compose, source, x_offset, y_offset);
+      CompositeCanvas(destination, compose, source, x_offset, y_offset,
+        exception);
       source=GetNextImageInList(source);
       destination=GetNextImageInList(destination);
     }
@@ -1847,7 +1847,7 @@ MagickExport void CompositeLayers(Image *destination,
 %  MergeImageLayers() composes all the image layers from the current given
 %  image onward to produce a single image of the merged layers.
 %
-%  The inital canvas's size depends on the given ImageLayerMethod, and is
+%  The inital canvas's size depends on the given LayerMethod, and is
 %  initialized using the first images background color.  The images
 %  are then compositied onto that image in sequence using the given
 %  composition that has been assigned to each individual image.
@@ -1855,7 +1855,7 @@ MagickExport void CompositeLayers(Image *destination,
 %  The format of the MergeImageLayers is:
 %
 %      Image *MergeImageLayers(const Image *image,
-%        const ImageLayerMethod method, ExceptionInfo *exception)
+%        const LayerMethod method, ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -1886,8 +1886,8 @@ MagickExport void CompositeLayers(Image *destination,
 %    o exception: return any errors or warnings in this structure.
 %
 */
-MagickExport Image *MergeImageLayers(Image *image,
-  const ImageLayerMethod method,ExceptionInfo *exception)
+MagickExport Image *MergeImageLayers(Image *image,const LayerMethod method,
+  ExceptionInfo *exception)
 {
 #define MergeLayersTag  "Merge/Layers"
 
@@ -1929,28 +1929,31 @@ MagickExport Image *MergeImageLayers(Image *image,
     case MergeLayer:
     default:
     {
-      next = GetNextImageInList(image);
-      for ( ; next != (Image *) NULL;  next=GetNextImageInList(next)) {
-        if ( page.x > next->page.x ) {
-             width += page.x-next->page.x;
-             page.x = next->page.x;
-        }
-        if ( page.y > next->page.y ) {
-             height += page.y-next->page.y;
-             page.y = next->page.y;
-        }
-        if ( (ssize_t) width < (next->page.x + (ssize_t)next->columns - page.x) )
-           width = (size_t) next->page.x + (ssize_t)next->columns - page.x;
-        if ( (ssize_t) height < (next->page.y + (ssize_t)next->rows - page.y) )
-           height = (size_t) next->page.y + (ssize_t)next->rows - page.y;
+      next=GetNextImageInList(image);
+      for ( ; next != (Image *) NULL;  next=GetNextImageInList(next))
+      {
+        if (page.x > next->page.x)
+          {
+            width+=page.x-next->page.x;
+            page.x=next->page.x;
+          }
+        if (page.y > next->page.y)
+          {
+            height+=page.y-next->page.y;
+            page.y=next->page.y;
+          }
+        if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x))
+          width=(size_t) next->page.x+(ssize_t)next->columns-page.x;
+        if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y))
+          height=(size_t) next->page.y+(ssize_t) next->rows-page.y;
       }
       break;
     }
     case FlattenLayer:
     {
-      if ( page.width > 0 )
+      if (page.width > 0)
         width=page.width;
-      if ( page.height > 0 )
+      if (page.height > 0)
         height=page.height;
       page.x=0;
       page.y=0;
@@ -1958,19 +1961,21 @@ MagickExport Image *MergeImageLayers(Image *image,
     }
     case MosaicLayer:
     {
-      if ( page.width > 0 )
+      if (page.width > 0)
         width=page.width;
-      if ( page.height > 0 )
+      if (page.height > 0)
         height=page.height;
-      for (next=image; next != (Image *) NULL; next=GetNextImageInList(next)) {
-        if (method == MosaicLayer) {
-          page.x=next->page.x;
-          page.y=next->page.y;
-          if ( (ssize_t) width < (next->page.x + (ssize_t)next->columns) )
-             width = (size_t) next->page.x + next->columns;
-          if ( (ssize_t) height < (next->page.y + (ssize_t)next->rows) )
-             height = (size_t) next->page.y + next->rows;
-        }
+      for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
+      {
+        if (method == MosaicLayer)
+          {
+            page.x=next->page.x;
+            page.y=next->page.y;
+            if ((ssize_t) width < (next->page.x+(ssize_t) next->columns))
+              width=(size_t) next->page.x+next->columns;
+            if ((ssize_t) height < (next->page.y+(ssize_t) next->rows))
+              height=(size_t) next->page.y+next->rows;
+          }
       }
       page.width=width;
       page.height=height;
@@ -1979,55 +1984,59 @@ MagickExport Image *MergeImageLayers(Image *image,
     }
     break;
   }
-  /* set virtual canvas size if not defined */
-  if ( page.width == 0 )
-    page.width = (page.x < 0) ? width : width+page.x;
-  if ( page.height == 0 )
-    page.height = (page.y < 0) ? height : height+page.y;
-
   /*
-    Handle "TrimBoundsLayer" method separately to normal 'layer merge'
+    Set virtual canvas size if not defined.
+  */
+  if (page.width == 0)
+    page.width=page.x < 0 ? width : width+page.x;
+  if (page.height == 0)
+    page.height=page.y < 0 ? height : height+page.y;
+  /*
+    Handle "TrimBoundsLayer" method separately to normal 'layer merge'.
   */
-  if ( method == TrimBoundsLayer ) {
-    number_images=GetImageListLength(image);
-    for (scene=0; scene < (ssize_t) number_images; scene++)
+  if (method == TrimBoundsLayer)
     {
-      image->page.x -= page.x;
-      image->page.y -= page.y;
-      image->page.width = width;
-      image->page.height = height;
-      proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
-        number_images);
-      if (proceed == MagickFalse)
-        break;
-      image=GetNextImageInList(image);
+      number_images=GetImageListLength(image);
+      for (scene=0; scene < (ssize_t) number_images; scene++)
+      {
+        image->page.x-=page.x;
+        image->page.y-=page.y;
+        image->page.width=width;
+        image->page.height=height;
+        proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
+          number_images);
+        if (proceed == MagickFalse)
+          break;
+        image=GetNextImageInList(image);
+        if (image == (Image *) NULL)
+          break;
+      }
+      return((Image *) NULL);
     }
-    return((Image *) NULL);
-  }
-
   /*
     Create canvas size of width and height, and background color.
   */
   canvas=CloneImage(image,width,height,MagickTrue,exception);
   if (canvas == (Image *) NULL)
     return((Image *) NULL);
-  (void) SetImageBackgroundColor(canvas);
+  (void) SetImageBackgroundColor(canvas,exception);
   canvas->page=page;
   canvas->dispose=UndefinedDispose;
-
   /*
     Compose images onto canvas, with progress monitor
   */
   number_images=GetImageListLength(image);
   for (scene=0; scene < (ssize_t) number_images; scene++)
   {
-    (void) CompositeImage(canvas,image->compose,image,image->page.x-
-      canvas->page.x,image->page.y-canvas->page.y);
+    (void) CompositeImage(canvas,image,image->compose,MagickTrue,image->page.x-
+      canvas->page.x,image->page.y-canvas->page.y,exception);
     proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
       number_images);
     if (proceed == MagickFalse)
       break;
     image=GetNextImageInList(image);
+    if (image == (Image *) NULL)
+      break;
   }
   return(canvas);
 }