MagickCore  6.7.5
layer.c
Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                     L       AAA   Y   Y  EEEEE  RRRR                        %
00006 %                     L      A   A   Y Y   E      R   R                       %
00007 %                     L      AAAAA    Y    EEE    RRRR                        %
00008 %                     L      A   A    Y    E      R R                         %
00009 %                     LLLLL  A   A    Y    EEEEE  R  R                        %
00010 %                                                                             %
00011 %                      MagickCore Image Layering Methods                      %
00012 %                                                                             %
00013 %                              Software Design                                %
00014 %                                John Cristy                                  %
00015 %                              Anthony Thyssen                                %
00016 %                               January 2006                                  %
00017 %                                                                             %
00018 %                                                                             %
00019 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
00020 %  dedicated to making software imaging solutions freely available.           %
00021 %                                                                             %
00022 %  You may not use this file except in compliance with the License.  You may  %
00023 %  obtain a copy of the License at                                            %
00024 %                                                                             %
00025 %    http://www.imagemagick.org/script/license.php                            %
00026 %                                                                             %
00027 %  Unless required by applicable law or agreed to in writing, software        %
00028 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00029 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00030 %  See the License for the specific language governing permissions and        %
00031 %  limitations under the License.                                             %
00032 %                                                                             %
00033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00034 %
00035 */
00036 
00037 /*
00038   Include declarations.
00039 */
00040 #include "MagickCore/studio.h"
00041 #include "MagickCore/artifact.h"
00042 #include "MagickCore/cache.h"
00043 #include "MagickCore/color.h"
00044 #include "MagickCore/color-private.h"
00045 #include "MagickCore/composite.h"
00046 #include "MagickCore/effect.h"
00047 #include "MagickCore/exception.h"
00048 #include "MagickCore/exception-private.h"
00049 #include "MagickCore/geometry.h"
00050 #include "MagickCore/image.h"
00051 #include "MagickCore/layer.h"
00052 #include "MagickCore/list.h"
00053 #include "MagickCore/memory_.h"
00054 #include "MagickCore/monitor.h"
00055 #include "MagickCore/monitor-private.h"
00056 #include "MagickCore/option.h"
00057 #include "MagickCore/pixel-accessor.h"
00058 #include "MagickCore/property.h"
00059 #include "MagickCore/profile.h"
00060 #include "MagickCore/resource_.h"
00061 #include "MagickCore/resize.h"
00062 #include "MagickCore/statistic.h"
00063 #include "MagickCore/string_.h"
00064 #include "MagickCore/transform.h"
00065 
00066 /*
00067 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00068 %                                                                             %
00069 %                                                                             %
00070 %                                                                             %
00071 +     C l e a r B o u n d s                                                   %
00072 %                                                                             %
00073 %                                                                             %
00074 %                                                                             %
00075 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00076 %
00077 %  ClearBounds() Clear the area specified by the bounds in an image to
00078 %  transparency.  This typically used to handle Background Disposal
00079 %  for the previous frame in an animation sequence.
00080 %
00081 %  WARNING: no bounds checks are performed, except for the null or
00082 %  missed image, for images that don't change. in all other cases
00083 %  bound must fall within the image.
00084 %
00085 %  The format is:
00086 %
00087 %      void ClearBounds(Image *image,RectangleInfo *bounds
00088 %        ExceptionInfo *exception)
00089 %
00090 %  A description of each parameter follows:
00091 %
00092 %    o image: the image to had the area cleared in
00093 %
00094 %    o bounds: the area to be clear within the imag image
00095 %
00096 %    o exception: return any errors or warnings in this structure.
00097 %
00098 */
00099 static void ClearBounds(Image *image,RectangleInfo *bounds,
00100   ExceptionInfo *exception)
00101 {
00102   ssize_t
00103     y;
00104 
00105   if (bounds->x < 0)
00106     return;
00107   if (image->matte == MagickFalse)
00108     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
00109   for (y=0; y < (ssize_t) bounds->height; y++)
00110   {
00111     register ssize_t
00112       x;
00113 
00114     register Quantum
00115       *restrict q;
00116 
00117     q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
00118     if (q == (Quantum *) NULL)
00119       break;
00120     for (x=0; x < (ssize_t) bounds->width; x++)
00121     {
00122       SetPixelAlpha(image,TransparentAlpha,q);
00123       q+=GetPixelChannels(image);
00124     }
00125     if (SyncAuthenticPixels(image,exception) == MagickFalse)
00126       break;
00127   }
00128 }
00129 
00130 /*
00131 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00132 %                                                                             %
00133 %                                                                             %
00134 %                                                                             %
00135 +     I s B o u n d s C l e a r e d                                           %
00136 %                                                                             %
00137 %                                                                             %
00138 %                                                                             %
00139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00140 %
00141 %  IsBoundsCleared() tests whether any pixel in the bounds given, gets cleared
00142 %  when going from the first image to the second image.  This typically used
00143 %  to check if a proposed disposal method will work successfully to generate
00144 %  the second frame image from the first disposed form of the previous frame.
00145 %
00146 %  The format is:
00147 %
00148 %      MagickBooleanType IsBoundsCleared(const Image *image1,
00149 %        const Image *image2,RectangleInfo bounds,ExceptionInfo *exception)
00150 %
00151 %  A description of each parameter follows:
00152 %
00153 %    o image1, image 2: the images to check for cleared pixels
00154 %
00155 %    o bounds: the area to be clear within the imag image
00156 %
00157 %    o exception: return any errors or warnings in this structure.
00158 %
00159 %  WARNING: no bounds checks are performed, except for the null or
00160 %  missed image, for images that don't change. in all other cases
00161 %  bound must fall within the image.
00162 %
00163 */
00164 static MagickBooleanType IsBoundsCleared(const Image *image1,
00165   const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception)
00166 {
00167   register ssize_t
00168     x;
00169 
00170   register const Quantum
00171     *p,
00172     *q;
00173 
00174   ssize_t
00175     y;
00176 
00177   if (bounds->x < 0)
00178     return(MagickFalse);
00179   for (y=0; y < (ssize_t) bounds->height; y++)
00180   {
00181     p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,
00182       exception);
00183     q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,
00184       exception);
00185     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
00186       break;
00187     for (x=0; x < (ssize_t) bounds->width; x++)
00188     {
00189       if ((GetPixelAlpha(image1,p) <= (Quantum) (QuantumRange/2)) &&
00190           (GetPixelAlpha(image1,q) > (Quantum) (QuantumRange/2)))
00191         break;
00192       p+=GetPixelChannels(image1);
00193       q++;
00194     }
00195     if (x < (ssize_t) bounds->width)
00196       break;
00197   }
00198   return(y < (ssize_t) bounds->height ? MagickTrue : MagickFalse);
00199 }
00200 
00201 /*
00202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00203 %                                                                             %
00204 %                                                                             %
00205 %                                                                             %
00206 %     C o a l e s c e I m a g e s                                             %
00207 %                                                                             %
00208 %                                                                             %
00209 %                                                                             %
00210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00211 %
00212 %  CoalesceImages() composites a set of images while respecting any page
00213 %  offsets and disposal methods.  GIF, MIFF, and MNG animation sequences
00214 %  typically start with an image background and each subsequent image
00215 %  varies in size and offset.  A new image sequence is returned with all
00216 %  images the same size as the first images virtual canvas and composited
00217 %  with the next image in the sequence.
00218 %
00219 %  The format of the CoalesceImages method is:
00220 %
00221 %      Image *CoalesceImages(Image *image,ExceptionInfo *exception)
00222 %
00223 %  A description of each parameter follows:
00224 %
00225 %    o image: the image sequence.
00226 %
00227 %    o exception: return any errors or warnings in this structure.
00228 %
00229 */
00230 MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
00231 {
00232   Image
00233     *coalesce_image,
00234     *dispose_image,
00235     *previous;
00236 
00237   register Image
00238     *next;
00239 
00240   RectangleInfo
00241     bounds;
00242 
00243   /*
00244     Coalesce the image sequence.
00245   */
00246   assert(image != (Image *) NULL);
00247   assert(image->signature == MagickSignature);
00248   if (image->debug != MagickFalse)
00249     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00250   assert(exception != (ExceptionInfo *) NULL);
00251   assert(exception->signature == MagickSignature);
00252   next=GetFirstImageInList(image);
00253   bounds=next->page;
00254   if (bounds.width == 0)
00255     {
00256       bounds.width=next->columns;
00257       if (bounds.x > 0)
00258         bounds.width+=bounds.x;
00259     }
00260   if (bounds.height == 0)
00261     {
00262       bounds.height=next->rows;
00263       if (bounds.y > 0)
00264         bounds.height+=bounds.y;
00265     }
00266   bounds.x=0;
00267   bounds.y=0;
00268   coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue,
00269     exception);
00270   if (coalesce_image == (Image *) NULL)
00271     return((Image *) NULL);
00272   coalesce_image->page=bounds;
00273   coalesce_image->dispose=NoneDispose;
00274   (void) SetImageBackgroundColor(coalesce_image,exception);
00275   /*
00276     Coalesce rest of the images.
00277   */
00278   dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
00279   (void) CompositeImage(coalesce_image,CopyCompositeOp,next,next->page.x,
00280     next->page.y,exception);
00281   next=GetNextImageInList(next);
00282   for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
00283   {
00284     /*
00285       Determine the bounds that was overlaid in the previous image.
00286     */
00287     previous=GetPreviousImageInList(next);
00288     bounds=previous->page;
00289     bounds.width=previous->columns;
00290     bounds.height=previous->rows;
00291     if (bounds.x < 0)
00292       {
00293         bounds.width+=bounds.x;
00294         bounds.x=0;
00295       }
00296     if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) coalesce_image->columns)
00297       bounds.width=coalesce_image->columns-bounds.x;
00298     if (bounds.y < 0)
00299       {
00300         bounds.height+=bounds.y;
00301         bounds.y=0;
00302       }
00303     if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) coalesce_image->rows)
00304       bounds.height=coalesce_image->rows-bounds.y;
00305     /*
00306       Replace the dispose image with the new coalesced image.
00307     */
00308     if (GetPreviousImageInList(next)->dispose != PreviousDispose)
00309       {
00310         dispose_image=DestroyImage(dispose_image);
00311         dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
00312         if (dispose_image == (Image *) NULL)
00313           {
00314             coalesce_image=DestroyImageList(coalesce_image);
00315             return((Image *) NULL);
00316           }
00317       }
00318     /*
00319       Clear the overlaid area of the coalesced bounds for background disposal
00320     */
00321     if (next->previous->dispose == BackgroundDispose)
00322       ClearBounds(dispose_image,&bounds,exception);
00323     /*
00324       Next image is the dispose image, overlaid with next frame in sequence.
00325     */
00326     coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception);
00327     coalesce_image->next->previous=coalesce_image;
00328     previous=coalesce_image;
00329     coalesce_image=GetNextImageInList(coalesce_image);
00330     coalesce_image->matte=MagickTrue;
00331     (void) CompositeImage(coalesce_image,next->matte != MagickFalse ?
00332       OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y,
00333       exception);
00334     (void) CloneImageProfiles(coalesce_image,next);
00335     (void) CloneImageProperties(coalesce_image,next);
00336     (void) CloneImageArtifacts(coalesce_image,next);
00337     coalesce_image->page=previous->page;
00338     /*
00339       If a pixel goes opaque to transparent, use background dispose.
00340     */
00341     if (IsBoundsCleared(previous,coalesce_image,&bounds,exception))
00342       coalesce_image->dispose=BackgroundDispose;
00343     else
00344       coalesce_image->dispose=NoneDispose;
00345     previous->dispose=coalesce_image->dispose;
00346   }
00347   dispose_image=DestroyImage(dispose_image);
00348   return(GetFirstImageInList(coalesce_image));
00349 }
00350 
00351 /*
00352 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00353 %                                                                             %
00354 %                                                                             %
00355 %                                                                             %
00356 %     D i s p o s e I m a g e s                                               %
00357 %                                                                             %
00358 %                                                                             %
00359 %                                                                             %
00360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00361 %
00362 %  DisposeImages() returns the coalesced frames of a GIF animation as it would
00363 %  appear after the GIF dispose method of that frame has been applied.  That
00364 %  is it returned the appearance of each frame before the next is overlaid.
00365 %
00366 %  The format of the DisposeImages method is:
00367 %
00368 %      Image *DisposeImages(Image *image,ExceptionInfo *exception)
00369 %
00370 %  A description of each parameter follows:
00371 %
00372 %    o image: the image sequence.
00373 %
00374 %    o exception: return any errors or warnings in this structure.
00375 %
00376 */
00377 MagickExport Image *DisposeImages(const Image *image,ExceptionInfo *exception)
00378 {
00379   Image
00380     *dispose_image,
00381     *dispose_images;
00382 
00383   register Image
00384     *curr;
00385 
00386   RectangleInfo
00387     bounds;
00388 
00389   /*
00390     Run the image through the animation sequence
00391   */
00392   assert(image != (Image *) NULL);
00393   assert(image->signature == MagickSignature);
00394   if (image->debug != MagickFalse)
00395     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00396   assert(exception != (ExceptionInfo *) NULL);
00397   assert(exception->signature == MagickSignature);
00398   curr=GetFirstImageInList(image);
00399   dispose_image=CloneImage(curr,curr->page.width,curr->page.height,MagickTrue,
00400     exception);
00401   if (dispose_image == (Image *) NULL)
00402     return((Image *) NULL);
00403   dispose_image->page=curr->page;
00404   dispose_image->page.x=0;
00405   dispose_image->page.y=0;
00406   dispose_image->dispose=NoneDispose;
00407   dispose_image->background_color.alpha=(Quantum) TransparentAlpha;
00408   (void) SetImageBackgroundColor(dispose_image,exception);
00409   dispose_images=NewImageList();
00410   for ( ; curr != (Image *) NULL; curr=GetNextImageInList(curr))
00411   {
00412     Image
00413       *current_image;
00414 
00415     /*
00416       Overlay this frame's image over the previous disposal image.
00417     */
00418     current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
00419     if (current_image == (Image *) NULL)
00420       {
00421         dispose_images=DestroyImageList(dispose_images);
00422         dispose_image=DestroyImage(dispose_image);
00423         return((Image *) NULL);
00424       }
00425     (void) CompositeImage(current_image,curr->matte != MagickFalse ?
00426       OverCompositeOp : CopyCompositeOp,curr,curr->page.x,curr->page.y,
00427       exception);
00428     /*
00429       Handle Background dispose: image is displayed for the delay period.
00430     */
00431     if (curr->dispose == BackgroundDispose)
00432       {
00433         bounds=curr->page;
00434         bounds.width=curr->columns;
00435         bounds.height=curr->rows;
00436         if (bounds.x < 0)
00437           {
00438             bounds.width+=bounds.x;
00439             bounds.x=0;
00440           }
00441         if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
00442           bounds.width=current_image->columns-bounds.x;
00443         if (bounds.y < 0)
00444           {
00445             bounds.height+=bounds.y;
00446             bounds.y=0;
00447           }
00448         if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
00449           bounds.height=current_image->rows-bounds.y;
00450         ClearBounds(current_image,&bounds,exception);
00451       }
00452     /*
00453       Select the appropriate previous/disposed image.
00454     */
00455     if (curr->dispose == PreviousDispose)
00456       current_image=DestroyImage(current_image);
00457     else
00458       {
00459         dispose_image=DestroyImage(dispose_image);
00460         dispose_image=current_image;
00461         current_image=(Image *)NULL;
00462       }
00463     /*
00464       Save the dispose image just calculated for return.
00465     */
00466     {
00467       Image
00468         *dispose;
00469 
00470       dispose=CloneImage(dispose_image,0,0,MagickTrue,exception);
00471       if (dispose == (Image *) NULL)
00472         {
00473           dispose_images=DestroyImageList(dispose_images);
00474           dispose_image=DestroyImage(dispose_image);
00475           return((Image *) NULL);
00476         }
00477       (void) CloneImageProfiles(dispose,curr);
00478       (void) CloneImageProperties(dispose,curr);
00479       (void) CloneImageArtifacts(dispose,curr);
00480       dispose->page.x=0;
00481       dispose->page.y=0;
00482       dispose->dispose=curr->dispose;
00483       AppendImageToList(&dispose_images,dispose);
00484     }
00485   }
00486   dispose_image=DestroyImage(dispose_image);
00487   return(GetFirstImageInList(dispose_images));
00488 }
00489 
00490 /*
00491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00492 %                                                                             %
00493 %                                                                             %
00494 %                                                                             %
00495 +     C o m p a r e P i x e l s                                               %
00496 %                                                                             %
00497 %                                                                             %
00498 %                                                                             %
00499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00500 %
00501 %  ComparePixels() Compare the two pixels and return true if the pixels
00502 %  differ according to the given LayerType comparision method.
00503 %
00504 %  This currently only used internally by CompareImagesBounds(). It is
00505 %  doubtful that this sub-routine will be useful outside this module.
00506 %
00507 %  The format of the ComparePixels method is:
00508 %
00509 %      MagickBooleanType *ComparePixels(const ImageLayerMethod method,
00510 %        const PixelInfo *p,const PixelInfo *q)
00511 %
00512 %  A description of each parameter follows:
00513 %
00514 %    o method: What differences to look for. Must be one of
00515 %              CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
00516 %
00517 %    o p, q: the pixels to test for appropriate differences.
00518 %
00519 */
00520 
00521 static MagickBooleanType ComparePixels(const ImageLayerMethod method,
00522   const PixelInfo *p,const PixelInfo *q)
00523 {
00524   MagickRealType
00525     o1,
00526     o2;
00527 
00528   /*
00529     Any change in pixel values
00530   */
00531   if (method == CompareAnyLayer)
00532     return((MagickBooleanType)(IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
00533 
00534   o1 = (p->matte != MagickFalse) ? p->alpha : OpaqueAlpha;
00535   o2 = (q->matte != MagickFalse) ? q->alpha : OpaqueAlpha;
00536 
00537   /*
00538     Pixel goes from opaque to transprency
00539   */
00540   if (method == CompareClearLayer)
00541     return((MagickBooleanType) ( (o1 <= ((MagickRealType) QuantumRange/2.0)) &&
00542       (o2 > ((MagickRealType) QuantumRange/2.0)) ) );
00543 
00544   /*
00545     overlay would change first pixel by second
00546   */
00547   if (method == CompareOverlayLayer)
00548     {
00549       if (o2 > ((MagickRealType) QuantumRange/2.0))
00550         return MagickFalse;
00551       return((MagickBooleanType) (IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
00552     }
00553   return(MagickFalse);
00554 }
00555 
00556 
00557 /*
00558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00559 %                                                                             %
00560 %                                                                             %
00561 %                                                                             %
00562 +     C o m p a r e I m a g e B o u n d s                                     %
00563 %                                                                             %
00564 %                                                                             %
00565 %                                                                             %
00566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00567 %
00568 %  CompareImagesBounds() Given two images return the smallest rectangular area
00569 %  by which the two images differ, accourding to the given 'Compare...'
00570 %  layer method.
00571 %
00572 %  This currently only used internally in this module, but may eventually
00573 %  be used by other modules.
00574 %
00575 %  The format of the CompareImagesBounds method is:
00576 %
00577 %      RectangleInfo *CompareImagesBounds(const ImageLayerMethod method,
00578 %        const Image *image1, const Image *image2, ExceptionInfo *exception)
00579 %
00580 %  A description of each parameter follows:
00581 %
00582 %    o method: What differences to look for. Must be one of CompareAnyLayer,
00583 %      CompareClearLayer, CompareOverlayLayer.
00584 %
00585 %    o image1, image2: the two images to compare.
00586 %
00587 %    o exception: return any errors or warnings in this structure.
00588 %
00589 */
00590 
00591 static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2,
00592   const ImageLayerMethod method,ExceptionInfo *exception)
00593 {
00594   RectangleInfo
00595     bounds;
00596 
00597   PixelInfo
00598     pixel1,
00599     pixel2;
00600 
00601   register const Quantum
00602     *p,
00603     *q;
00604 
00605   register ssize_t
00606     x;
00607 
00608   ssize_t
00609     y;
00610 
00611   /*
00612     Set bounding box of the differences between images.
00613   */
00614   GetPixelInfo(image1,&pixel1);
00615   GetPixelInfo(image2,&pixel2);
00616   for (x=0; x < (ssize_t) image1->columns; x++)
00617   {
00618     p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
00619     q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
00620     if ((p == (const Quantum *) NULL) ||
00621         (q == (Quantum *) NULL))
00622       break;
00623     for (y=0; y < (ssize_t) image1->rows; y++)
00624     {
00625       GetPixelInfoPixel(image1,p,&pixel1);
00626       GetPixelInfoPixel(image2,q,&pixel2);
00627       if (ComparePixels(method,&pixel1,&pixel2))
00628         break;
00629       p+=GetPixelChannels(image1);
00630       q++;
00631     }
00632     if (y < (ssize_t) image1->rows)
00633       break;
00634   }
00635   if (x >= (ssize_t) image1->columns)
00636     {
00637       /*
00638         Images are identical, return a null image.
00639       */
00640       bounds.x=-1;
00641       bounds.y=-1;
00642       bounds.width=1;
00643       bounds.height=1;
00644       return(bounds);
00645     }
00646   bounds.x=x;
00647   for (x=(ssize_t) image1->columns-1; x >= 0; x--)
00648   {
00649     p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
00650     q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
00651     if ((p == (const Quantum *) NULL) ||
00652         (q == (Quantum *) NULL))
00653       break;
00654     for (y=0; y < (ssize_t) image1->rows; y++)
00655     {
00656       GetPixelInfoPixel(image1,p,&pixel1);
00657       GetPixelInfoPixel(image2,q,&pixel2);
00658       if (ComparePixels(method,&pixel1,&pixel2))
00659         break;
00660       p+=GetPixelChannels(image1);
00661       q++;
00662     }
00663     if (y < (ssize_t) image1->rows)
00664       break;
00665   }
00666   bounds.width=(size_t) (x-bounds.x+1);
00667   for (y=0; y < (ssize_t) image1->rows; y++)
00668   {
00669     p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
00670     q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
00671     if ((p == (const Quantum *) NULL) ||
00672         (q == (Quantum *) NULL))
00673       break;
00674     for (x=0; x < (ssize_t) image1->columns; x++)
00675     {
00676       GetPixelInfoPixel(image1,p,&pixel1);
00677       GetPixelInfoPixel(image2,q,&pixel2);
00678       if (ComparePixels(method,&pixel1,&pixel2))
00679         break;
00680       p+=GetPixelChannels(image1);
00681       q++;
00682     }
00683     if (x < (ssize_t) image1->columns)
00684       break;
00685   }
00686   bounds.y=y;
00687   for (y=(ssize_t) image1->rows-1; y >= 0; y--)
00688   {
00689     p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
00690     q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
00691     if ((p == (const Quantum *) NULL) ||
00692         (q == (Quantum *) NULL))
00693       break;
00694     for (x=0; x < (ssize_t) image1->columns; x++)
00695     {
00696       GetPixelInfoPixel(image1,p,&pixel1);
00697       GetPixelInfoPixel(image2,q,&pixel2);
00698       if (ComparePixels(method,&pixel1,&pixel2))
00699         break;
00700       p+=GetPixelChannels(image1);
00701       q++;
00702     }
00703     if (x < (ssize_t) image1->columns)
00704       break;
00705   }
00706   bounds.height=(size_t) (y-bounds.y+1);
00707   return(bounds);
00708 }
00709 
00710 /*
00711 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00712 %                                                                             %
00713 %                                                                             %
00714 %                                                                             %
00715 %     C o m p a r e I m a g e L a y e r s                                     %
00716 %                                                                             %
00717 %                                                                             %
00718 %                                                                             %
00719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00720 %
00721 %  CompareImagesLayers() compares each image with the next in a sequence and
00722 %  returns the minimum bounding region of all the pixel differences (of the
00723 %  ImageLayerMethod specified) it discovers.
00724 %
00725 %  Images do NOT have to be the same size, though it is best that all the
00726 %  images are 'coalesced' (images are all the same size, on a flattened
00727 %  canvas, so as to represent exactly how an specific frame should look).
00728 %
00729 %  No GIF dispose methods are applied, so GIF animations must be coalesced
00730 %  before applying this image operator to find differences to them.
00731 %
00732 %  The format of the CompareImagesLayers method is:
00733 %
00734 %      Image *CompareImagesLayers(const Image *images,
00735 %        const ImageLayerMethod method,ExceptionInfo *exception)
00736 %
00737 %  A description of each parameter follows:
00738 %
00739 %    o image: the image.
00740 %
00741 %    o method: the layers type to compare images with. Must be one of...
00742 %              CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
00743 %
00744 %    o exception: return any errors or warnings in this structure.
00745 %
00746 */
00747 
00748 MagickExport Image *CompareImagesLayers(const Image *image,
00749   const ImageLayerMethod method, ExceptionInfo *exception)
00750 {
00751   Image
00752     *image_a,
00753     *image_b,
00754     *layers;
00755 
00756   RectangleInfo
00757     *bounds;
00758 
00759   register const Image
00760     *next;
00761 
00762   register ssize_t
00763     i;
00764 
00765   assert(image != (const Image *) NULL);
00766   assert(image->signature == MagickSignature);
00767   if (image->debug != MagickFalse)
00768     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00769   assert(exception != (ExceptionInfo *) NULL);
00770   assert(exception->signature == MagickSignature);
00771   assert((method == CompareAnyLayer) ||
00772          (method == CompareClearLayer) ||
00773          (method == CompareOverlayLayer));
00774   /*
00775     Allocate bounds memory.
00776   */
00777   next=GetFirstImageInList(image);
00778   bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
00779     GetImageListLength(next),sizeof(*bounds));
00780   if (bounds == (RectangleInfo *) NULL)
00781     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00782   /*
00783     Set up first comparision images.
00784   */
00785   image_a=CloneImage(next,next->page.width,next->page.height,
00786     MagickTrue,exception);
00787   if (image_a == (Image *) NULL)
00788     {
00789       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00790       return((Image *) NULL);
00791     }
00792   image_a->background_color.alpha=(Quantum) TransparentAlpha;
00793   (void) SetImageBackgroundColor(image_a,exception);
00794   image_a->page=next->page;
00795   image_a->page.x=0;
00796   image_a->page.y=0;
00797   (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,next->page.y,
00798     exception);
00799   /*
00800     Compute the bounding box of changes for the later images
00801   */
00802   i=0;
00803   next=GetNextImageInList(next);
00804   for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
00805   {
00806     image_b=CloneImage(image_a,0,0,MagickTrue,exception);
00807     if (image_b == (Image *) NULL)
00808       {
00809         image_a=DestroyImage(image_a);
00810         bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00811         return((Image *) NULL);
00812       }
00813     (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,
00814       next->page.y,exception);
00815     bounds[i]=CompareImagesBounds(image_b,image_a,method,exception);
00816 
00817     image_b=DestroyImage(image_b);
00818     i++;
00819   }
00820   image_a=DestroyImage(image_a);
00821   /*
00822     Clone first image in sequence.
00823   */
00824   next=GetFirstImageInList(image);
00825   layers=CloneImage(next,0,0,MagickTrue,exception);
00826   if (layers == (Image *) NULL)
00827     {
00828       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00829       return((Image *) NULL);
00830     }
00831   /*
00832     Deconstruct the image sequence.
00833   */
00834   i=0;
00835   next=GetNextImageInList(next);
00836   for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
00837   {
00838     image_a=CloneImage(next,0,0,MagickTrue,exception);
00839     if (image_a == (Image *) NULL)
00840       break;
00841     image_b=CropImage(image_a,&bounds[i],exception);
00842     image_a=DestroyImage(image_a);
00843     if (image_b == (Image *) NULL)
00844       break;
00845     AppendImageToList(&layers,image_b);
00846     i++;
00847   }
00848   bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00849   if (next != (Image *) NULL)
00850     {
00851       layers=DestroyImageList(layers);
00852       return((Image *) NULL);
00853     }
00854   return(GetFirstImageInList(layers));
00855 }
00856 
00857 /*
00858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00859 %                                                                             %
00860 %                                                                             %
00861 %                                                                             %
00862 +     O p t i m i z e L a y e r F r a m e s                                   %
00863 %                                                                             %
00864 %                                                                             %
00865 %                                                                             %
00866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00867 %
00868 %  OptimizeLayerFrames() takes a coalesced GIF animation, and compares each
00869 %  frame against the three different 'disposal' forms of the previous frame.
00870 %  From this it then attempts to select the smallest cropped image and
00871 %  disposal method needed to reproduce the resulting image.
00872 %
00873 %  Note that this not easy, and may require the expansion of the bounds
00874 %  of previous frame, simply clear pixels for the next animation frame to
00875 %  transparency according to the selected dispose method.
00876 %
00877 %  The format of the OptimizeLayerFrames method is:
00878 %
00879 %      Image *OptimizeLayerFrames(const Image *image,
00880 %        const ImageLayerMethod method, ExceptionInfo *exception)
00881 %
00882 %  A description of each parameter follows:
00883 %
00884 %    o image: the image.
00885 %
00886 %    o method: the layers technique to optimize with. Must be one of...
00887 %      OptimizeImageLayer, or  OptimizePlusLayer.  The Plus form allows
00888 %      the addition of extra 'zero delay' frames to clear pixels from
00889 %      the previous frame, and the removal of frames that done change,
00890 %      merging the delay times together.
00891 %
00892 %    o exception: return any errors or warnings in this structure.
00893 %
00894 */
00895 /*
00896   Define a 'fake' dispose method where the frame is duplicated, (for
00897   OptimizePlusLayer) with a extra zero time delay frame which does a
00898   BackgroundDisposal to clear the pixels that need to be cleared.
00899 */
00900 #define DupDispose  ((DisposeType)9)
00901 /*
00902   Another 'fake' dispose method used to removed frames that don't change.
00903 */
00904 #define DelDispose  ((DisposeType)8)
00905 
00906 #define DEBUG_OPT_FRAME 0
00907 
00908 static Image *OptimizeLayerFrames(const Image *image,
00909   const ImageLayerMethod method, ExceptionInfo *exception)
00910 {
00911   ExceptionInfo
00912     *sans_exception;
00913 
00914   Image
00915     *prev_image,
00916     *dup_image,
00917     *bgnd_image,
00918     *optimized_image;
00919 
00920   RectangleInfo
00921     try_bounds,
00922     bgnd_bounds,
00923     dup_bounds,
00924     *bounds;
00925 
00926   MagickBooleanType
00927     add_frames,
00928     try_cleared,
00929     cleared;
00930 
00931   DisposeType
00932     *disposals;
00933 
00934   register const Image
00935     *curr;
00936 
00937   register ssize_t
00938     i;
00939 
00940   assert(image != (const Image *) NULL);
00941   assert(image->signature == MagickSignature);
00942   if (image->debug != MagickFalse)
00943     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00944   assert(exception != (ExceptionInfo *) NULL);
00945   assert(exception->signature == MagickSignature);
00946   assert(method == OptimizeLayer ||
00947          method == OptimizeImageLayer ||
00948          method == OptimizePlusLayer);
00949 
00950   /*
00951     Are we allowed to add/remove frames from animation
00952   */
00953   add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
00954   /*
00955     Ensure  all the images are the same size
00956   */
00957   curr=GetFirstImageInList(image);
00958   for (; curr != (Image *) NULL; curr=GetNextImageInList(curr))
00959   {
00960     if ((curr->columns != image->columns) || (curr->rows != image->rows))
00961       ThrowImageException(OptionError,"ImagesAreNotTheSameSize");
00962     /*
00963       FUTURE: also check that image is also fully coalesced (full page)
00964       Though as long as they are the same size it should not matter.
00965     */
00966   }
00967   /*
00968     Allocate memory (times 2 if we allow the use of frame duplications)
00969   */
00970   curr=GetFirstImageInList(image);
00971   bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
00972     GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)*
00973     sizeof(*bounds));
00974   if (bounds == (RectangleInfo *) NULL)
00975     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00976   disposals=(DisposeType *) AcquireQuantumMemory((size_t)
00977     GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
00978     sizeof(*disposals));
00979   if (disposals == (DisposeType *) NULL)
00980     {
00981       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00982       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00983     }
00984   /*
00985     Initialise Previous Image as fully transparent
00986   */
00987   prev_image=CloneImage(curr,curr->page.width,curr->page.height,
00988     MagickTrue,exception);
00989   if (prev_image == (Image *) NULL)
00990     {
00991       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00992       disposals=(DisposeType *) RelinquishMagickMemory(disposals);
00993       return((Image *) NULL);
00994     }
00995   prev_image->page=curr->page;  /* ERROR: <-- should not be need, but is! */
00996   prev_image->page.x=0;
00997   prev_image->page.y=0;
00998   prev_image->dispose=NoneDispose;
00999 
01000   prev_image->background_color.alpha=(Quantum) TransparentAlpha;
01001   (void) SetImageBackgroundColor(prev_image,exception);
01002   /*
01003     Figure out the area of overlay of the first frame
01004     No pixel could be cleared as all pixels are already cleared.
01005   */
01006 #if DEBUG_OPT_FRAME
01007   i=0;
01008   (void) FormatLocaleFile(stderr, "frame %.20g :-\n", (double) i);
01009 #endif
01010   disposals[0]=NoneDispose;
01011   bounds[0]=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
01012 #if DEBUG_OPT_FRAME
01013   (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g\n\n",
01014     (double) bounds[i].width,(double) bounds[i].height,
01015     (double) bounds[i].x,(double) bounds[i].y );
01016 #endif
01017   /*
01018     Compute the bounding box of changes for each pair of images.
01019   */
01020   i=1;
01021   bgnd_image=(Image *)NULL;
01022   dup_image=(Image *)NULL;
01023   dup_bounds.width=0;
01024   dup_bounds.height=0;
01025   dup_bounds.x=0;
01026   dup_bounds.y=0;
01027   curr=GetNextImageInList(curr);
01028   for ( ; curr != (const Image *) NULL; curr=GetNextImageInList(curr))
01029   {
01030 #if DEBUG_OPT_FRAME
01031     (void) FormatLocaleFile(stderr, "frame %.20g :-\n", (double) i);
01032 #endif
01033     /*
01034       Assume none disposal is the best
01035     */
01036     bounds[i]=CompareImagesBounds(curr->previous,curr,CompareAnyLayer,exception);
01037     cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception);
01038     disposals[i-1]=NoneDispose;
01039 #if DEBUG_OPT_FRAME
01040     (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g%s%s\n",
01041          (double) bounds[i].width,(double) bounds[i].height,
01042          (double) bounds[i].x,(double) bounds[i].y,
01043          bounds[i].x < 0?"  (unchanged)":"",
01044          cleared?"  (pixels cleared)":"");
01045 #endif
01046     if ( bounds[i].x < 0 ) {
01047       /*
01048         Image frame is exactly the same as the previous frame!
01049         If not adding frames leave it to be cropped down to a null image.
01050         Otherwise mark previous image for deleted, transfering its crop bounds
01051         to the current image.
01052       */
01053       if ( add_frames && i>=2 ) {
01054         disposals[i-1]=DelDispose;
01055         disposals[i]=NoneDispose;
01056         bounds[i]=bounds[i-1];
01057         i++;
01058         continue;
01059       }
01060     }
01061     else
01062       {
01063         /*
01064           Compare a none disposal against a previous disposal
01065         */
01066         try_bounds=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
01067         try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception);
01068 #if DEBUG_OPT_FRAME
01069     (void) FormatLocaleFile(stderr, "test_prev: %.20gx%.20g%+.20g%+.20g%s\n",
01070          (double) try_bounds.width,(double) try_bounds.height,
01071          (double) try_bounds.x,(double) try_bounds.y,
01072          try_cleared?"  (pixels were cleared)":"");
01073 #endif
01074         if ( (!try_cleared && cleared ) ||
01075                 try_bounds.width * try_bounds.height
01076                     <  bounds[i].width * bounds[i].height )
01077           {
01078             cleared=try_cleared;
01079             bounds[i]=try_bounds;
01080             disposals[i-1]=PreviousDispose;
01081 #if DEBUG_OPT_FRAME
01082             (void) FormatLocaleFile(stderr, "previous: accepted\n");
01083           } else {
01084             (void) FormatLocaleFile(stderr, "previous: rejected\n");
01085 #endif
01086           }
01087 
01088         /*
01089           If we are allowed lets try a complex frame duplication.
01090           It is useless if the previous image already clears pixels correctly.
01091           This method will always clear all the pixels that need to be cleared.
01092         */
01093         dup_bounds.width=dup_bounds.height=0; /* no dup, no pixel added */
01094         if ( add_frames )
01095           {
01096             dup_image=CloneImage(curr->previous,curr->previous->page.width,
01097                 curr->previous->page.height,MagickTrue,exception);
01098             if (dup_image == (Image *) NULL)
01099               {
01100                 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01101                 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
01102                 prev_image=DestroyImage(prev_image);
01103                 return((Image *) NULL);
01104               }
01105             dup_bounds=CompareImagesBounds(dup_image,curr,CompareClearLayer,exception);
01106             ClearBounds(dup_image,&dup_bounds,exception);
01107             try_bounds=CompareImagesBounds(dup_image,curr,CompareAnyLayer,exception);
01108             if ( cleared ||
01109                    dup_bounds.width*dup_bounds.height
01110                       +try_bounds.width*try_bounds.height
01111                    < bounds[i].width * bounds[i].height )
01112               {
01113                 cleared=MagickFalse;
01114                 bounds[i]=try_bounds;
01115                 disposals[i-1]=DupDispose;
01116                 /* to be finalised later, if found to be optimial */
01117               }
01118             else
01119               dup_bounds.width=dup_bounds.height=0;
01120           }
01121         /*
01122           Now compare against a simple background disposal
01123         */
01124         bgnd_image=CloneImage(curr->previous,curr->previous->page.width,
01125           curr->previous->page.height,MagickTrue,exception);
01126         if (bgnd_image == (Image *) NULL)
01127           {
01128             bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01129             disposals=(DisposeType *) RelinquishMagickMemory(disposals);
01130             prev_image=DestroyImage(prev_image);
01131             if ( dup_image != (Image *) NULL)
01132               dup_image=DestroyImage(dup_image);
01133             return((Image *) NULL);
01134           }
01135         bgnd_bounds=bounds[i-1]; /* interum bounds of the previous image */
01136         ClearBounds(bgnd_image,&bgnd_bounds,exception);
01137         try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
01138         try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
01139 #if DEBUG_OPT_FRAME
01140     (void) FormatLocaleFile(stderr, "background: %s\n",
01141          try_cleared?"(pixels cleared)":"");
01142 #endif
01143         if ( try_cleared )
01144           {
01145             /*
01146               Straight background disposal failed to clear pixels needed!
01147               Lets try expanding the disposal area of the previous frame, to
01148               include the pixels that are cleared.  This guaranteed
01149               to work, though may not be the most optimized solution.
01150             */
01151             try_bounds=CompareImagesBounds(curr->previous,curr,CompareClearLayer,exception);
01152 #if DEBUG_OPT_FRAME
01153             (void) FormatLocaleFile(stderr, "expand_clear: %.20gx%.20g%+.20g%+.20g%s\n",
01154                 (double) try_bounds.width,(double) try_bounds.height,
01155                 (double) try_bounds.x,(double) try_bounds.y,
01156                 try_bounds.x<0?"  (no expand nessary)":"");
01157 #endif
01158             if ( bgnd_bounds.x < 0 )
01159               bgnd_bounds = try_bounds;
01160             else
01161               {
01162 #if DEBUG_OPT_FRAME
01163                 (void) FormatLocaleFile(stderr, "expand_bgnd: %.20gx%.20g%+.20g%+.20g\n",
01164                     (double) bgnd_bounds.width,(double) bgnd_bounds.height,
01165                     (double) bgnd_bounds.x,(double) bgnd_bounds.y );
01166 #endif
01167                 if ( try_bounds.x < bgnd_bounds.x )
01168                   {
01169                      bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x;
01170                      if ( bgnd_bounds.width < try_bounds.width )
01171                        bgnd_bounds.width = try_bounds.width;
01172                      bgnd_bounds.x = try_bounds.x;
01173                   }
01174                 else
01175                   {
01176                      try_bounds.width += try_bounds.x - bgnd_bounds.x;
01177                      if ( bgnd_bounds.width < try_bounds.width )
01178                        bgnd_bounds.width = try_bounds.width;
01179                   }
01180                 if ( try_bounds.y < bgnd_bounds.y )
01181                   {
01182                      bgnd_bounds.height += bgnd_bounds.y - try_bounds.y;
01183                      if ( bgnd_bounds.height < try_bounds.height )
01184                        bgnd_bounds.height = try_bounds.height;
01185                      bgnd_bounds.y = try_bounds.y;
01186                   }
01187                 else
01188                   {
01189                     try_bounds.height += try_bounds.y - bgnd_bounds.y;
01190                      if ( bgnd_bounds.height < try_bounds.height )
01191                        bgnd_bounds.height = try_bounds.height;
01192                   }
01193 #if DEBUG_OPT_FRAME
01194                 (void) FormatLocaleFile(stderr, "        to : %.20gx%.20g%+.20g%+.20g\n",
01195                     (double) bgnd_bounds.width,(double) bgnd_bounds.height,
01196                     (double) bgnd_bounds.x,(double) bgnd_bounds.y );
01197 #endif
01198               }
01199             ClearBounds(bgnd_image,&bgnd_bounds,exception);
01200 #if DEBUG_OPT_FRAME
01201 /* Something strange is happening with a specific animation
01202  * CompareAnyLayers (normal method) and CompareClearLayers returns the whole
01203  * image, which is not posibly correct!  As verified by previous tests.
01204  * Something changed beyond the bgnd_bounds clearing.  But without being able
01205  * to see, or writet he image at this point it is hard to tell what is wrong!
01206  * Only CompareOverlay seemed to return something sensible.
01207  */
01208             try_bounds=CompareImagesBounds(bgnd_image,curr,CompareClearLayer,exception);
01209             (void) FormatLocaleFile(stderr, "expand_ctst: %.20gx%.20g%+.20g%+.20g\n",
01210                 (double) try_bounds.width,(double) try_bounds.height,
01211                 (double) try_bounds.x,(double) try_bounds.y );
01212             try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
01213             try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
01214             (void) FormatLocaleFile(stderr, "expand_any : %.20gx%.20g%+.20g%+.20g%s\n",
01215                 (double) try_bounds.width,(double) try_bounds.height,
01216                 (double) try_bounds.x,(double) try_bounds.y,
01217                 try_cleared?"   (pixels cleared)":"");
01218 #endif
01219             try_bounds=CompareImagesBounds(bgnd_image,curr,CompareOverlayLayer,exception);
01220 #if DEBUG_OPT_FRAME
01221             try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
01222             (void) FormatLocaleFile(stderr, "expand_test: %.20gx%.20g%+.20g%+.20g%s\n",
01223                 (double) try_bounds.width,(double) try_bounds.height,
01224                 (double) try_bounds.x,(double) try_bounds.y,
01225                 try_cleared?"   (pixels cleared)":"");
01226 #endif
01227           }
01228         /*
01229           Test if this background dispose is smaller than any of the
01230           other methods we tryed before this (including duplicated frame)
01231         */
01232         if ( cleared ||
01233               bgnd_bounds.width*bgnd_bounds.height
01234                 +try_bounds.width*try_bounds.height
01235               < bounds[i-1].width*bounds[i-1].height
01236                   +dup_bounds.width*dup_bounds.height
01237                   +bounds[i].width*bounds[i].height )
01238           {
01239             cleared=MagickFalse;
01240             bounds[i-1]=bgnd_bounds;
01241             bounds[i]=try_bounds;
01242             if ( disposals[i-1] == DupDispose )
01243               dup_image=DestroyImage(dup_image);
01244             disposals[i-1]=BackgroundDispose;
01245 #if DEBUG_OPT_FRAME
01246     (void) FormatLocaleFile(stderr, "expand_bgnd: accepted\n");
01247           } else {
01248     (void) FormatLocaleFile(stderr, "expand_bgnd: reject\n");
01249 #endif
01250           }
01251       }
01252     /*
01253        Finalise choice of dispose, set new prev_image,
01254        and junk any extra images as appropriate,
01255     */
01256     if ( disposals[i-1] == DupDispose )
01257       {
01258          if (bgnd_image != (Image *) NULL)
01259            bgnd_image=DestroyImage(bgnd_image);
01260          prev_image=DestroyImage(prev_image);
01261          prev_image=dup_image, dup_image=(Image *) NULL;
01262          bounds[i+1]=bounds[i];
01263          bounds[i]=dup_bounds;
01264          disposals[i-1]=DupDispose;
01265          disposals[i]=BackgroundDispose;
01266          i++;
01267       }
01268     else
01269       {
01270         if ( dup_image != (Image *) NULL)
01271           dup_image=DestroyImage(dup_image);
01272         if ( disposals[i-1] != PreviousDispose )
01273           prev_image=DestroyImage(prev_image);
01274         if ( disposals[i-1] == BackgroundDispose )
01275           prev_image=bgnd_image,  bgnd_image=(Image *)NULL;
01276         if (bgnd_image != (Image *) NULL)
01277           bgnd_image=DestroyImage(bgnd_image);
01278         if ( disposals[i-1] == NoneDispose )
01279           {
01280             prev_image=CloneImage(curr->previous,curr->previous->page.width,
01281               curr->previous->page.height,MagickTrue,exception);
01282             if (prev_image == (Image *) NULL)
01283               {
01284                 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01285                 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
01286                 return((Image *) NULL);
01287               }
01288           }
01289 
01290       }
01291     assert(prev_image != (Image *) NULL);
01292     disposals[i]=disposals[i-1];
01293 #if DEBUG_OPT_FRAME
01294     (void) FormatLocaleFile(stderr, "final   %.20g : %s  %.20gx%.20g%+.20g%+.20g\n",
01295          (double) i-1,
01296          CommandOptionToMnemonic(MagickDisposeOptions, disposals[i-1]),
01297          (double) bounds[i-1].width, (double) bounds[i-1].height,
01298          (double) bounds[i-1].x, (double) bounds[i-1].y );
01299 #endif
01300 #if DEBUG_OPT_FRAME
01301     (void) FormatLocaleFile(stderr, "interum %.20g : %s  %.20gx%.20g%+.20g%+.20g\n",
01302          (double) i,
01303          CommandOptionToMnemonic(MagickDisposeOptions, disposals[i]),
01304          (double) bounds[i].width, (double) bounds[i].height,
01305          (double) bounds[i].x, (double) bounds[i].y );
01306     (void) FormatLocaleFile(stderr, "\n");
01307 #endif
01308     i++;
01309   }
01310   prev_image=DestroyImage(prev_image);
01311   /*
01312     Optimize all images in sequence.
01313   */
01314   sans_exception=AcquireExceptionInfo();
01315   i=0;
01316   curr=GetFirstImageInList(image);
01317   optimized_image=NewImageList();
01318   while ( curr != (const Image *) NULL )
01319   {
01320     prev_image=CloneImage(curr,0,0,MagickTrue,exception);
01321     if (prev_image == (Image *) NULL)
01322       break;
01323     if ( disposals[i] == DelDispose ) {
01324       size_t time = 0;
01325       while ( disposals[i] == DelDispose ) {
01326         time += curr->delay*1000/curr->ticks_per_second;
01327         curr=GetNextImageInList(curr);
01328         i++;
01329       }
01330       time += curr->delay*1000/curr->ticks_per_second;
01331       prev_image->ticks_per_second = 100L;
01332       prev_image->delay = time*prev_image->ticks_per_second/1000;
01333     }
01334     bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
01335     prev_image=DestroyImage(prev_image);
01336     if (bgnd_image == (Image *) NULL)
01337       break;
01338     bgnd_image->dispose=disposals[i];
01339     if ( disposals[i] == DupDispose ) {
01340       bgnd_image->delay=0;
01341       bgnd_image->dispose=NoneDispose;
01342     }
01343     else
01344       curr=GetNextImageInList(curr);
01345     AppendImageToList(&optimized_image,bgnd_image);
01346     i++;
01347   }
01348   sans_exception=DestroyExceptionInfo(sans_exception);
01349   bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01350   disposals=(DisposeType *) RelinquishMagickMemory(disposals);
01351   if (curr != (Image *) NULL)
01352     {
01353       optimized_image=DestroyImageList(optimized_image);
01354       return((Image *) NULL);
01355     }
01356   return(GetFirstImageInList(optimized_image));
01357 }
01358 
01359 /*
01360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01361 %                                                                             %
01362 %                                                                             %
01363 %                                                                             %
01364 %     O p t i m i z e I m a g e L a y e r s                                   %
01365 %                                                                             %
01366 %                                                                             %
01367 %                                                                             %
01368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01369 %
01370 %  OptimizeImageLayers() compares each image the GIF disposed forms of the
01371 %  previous image in the sequence.  From this it attempts to select the
01372 %  smallest cropped image to replace each frame, while preserving the results
01373 %  of the GIF animation.
01374 %
01375 %  The format of the OptimizeImageLayers method is:
01376 %
01377 %      Image *OptimizeImageLayers(const Image *image,
01378 %               ExceptionInfo *exception)
01379 %
01380 %  A description of each parameter follows:
01381 %
01382 %    o image: the image.
01383 %
01384 %    o exception: return any errors or warnings in this structure.
01385 %
01386 */
01387 MagickExport Image *OptimizeImageLayers(const Image *image,
01388   ExceptionInfo *exception)
01389 {
01390   return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
01391 }
01392 
01393 /*
01394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01395 %                                                                             %
01396 %                                                                             %
01397 %                                                                             %
01398 %     O p t i m i z e P l u s I m a g e L a y e r s                           %
01399 %                                                                             %
01400 %                                                                             %
01401 %                                                                             %
01402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01403 %
01404 %  OptimizeImagePlusLayers() is exactly as OptimizeImageLayers(), but may
01405 %  also add or even remove extra frames in the animation, if it improves
01406 %  the total number of pixels in the resulting GIF animation.
01407 %
01408 %  The format of the OptimizePlusImageLayers method is:
01409 %
01410 %      Image *OptimizePlusImageLayers(const Image *image,
01411 %               ExceptionInfo *exception)
01412 %
01413 %  A description of each parameter follows:
01414 %
01415 %    o image: the image.
01416 %
01417 %    o exception: return any errors or warnings in this structure.
01418 %
01419 */
01420 MagickExport Image *OptimizePlusImageLayers(const Image *image,
01421   ExceptionInfo *exception)
01422 {
01423   return OptimizeLayerFrames(image, OptimizePlusLayer, exception);
01424 }
01425 
01426 /*
01427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01428 %                                                                             %
01429 %                                                                             %
01430 %                                                                             %
01431 %     O p t i m i z e I m a g e T r a n s p a r e n c y                       %
01432 %                                                                             %
01433 %                                                                             %
01434 %                                                                             %
01435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01436 %
01437 %  OptimizeImageTransparency() takes a frame optimized GIF animation, and
01438 %  compares the overlayed pixels against the disposal image resulting from all
01439 %  the previous frames in the animation.  Any pixel that does not change the
01440 %  disposal image (and thus does not effect the outcome of an overlay) is made
01441 %  transparent.
01442 %
01443 %  WARNING: This modifies the current images directly, rather than generate
01444 %  a new image sequence.
01445 %
01446 %  The format of the OptimizeImageTransperency method is:
01447 %
01448 %      void OptimizeImageTransperency(Image *image,ExceptionInfo *exception)
01449 %
01450 %  A description of each parameter follows:
01451 %
01452 %    o image: the image sequence
01453 %
01454 %    o exception: return any errors or warnings in this structure.
01455 %
01456 */
01457 MagickExport void OptimizeImageTransparency(const Image *image,
01458      ExceptionInfo *exception)
01459 {
01460   Image
01461     *dispose_image;
01462 
01463   register Image
01464     *next;
01465 
01466   /*
01467     Run the image through the animation sequence
01468   */
01469   assert(image != (Image *) NULL);
01470   assert(image->signature == MagickSignature);
01471   if (image->debug != MagickFalse)
01472     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01473   assert(exception != (ExceptionInfo *) NULL);
01474   assert(exception->signature == MagickSignature);
01475   next=GetFirstImageInList(image);
01476   dispose_image=CloneImage(next,next->page.width,next->page.height,
01477     MagickTrue,exception);
01478   if (dispose_image == (Image *) NULL)
01479     return;
01480   dispose_image->page=next->page;
01481   dispose_image->page.x=0;
01482   dispose_image->page.y=0;
01483   dispose_image->dispose=NoneDispose;
01484   dispose_image->background_color.alpha=(Quantum) TransparentAlpha;
01485   (void) SetImageBackgroundColor(dispose_image,exception);
01486 
01487   while ( next != (Image *) NULL )
01488   {
01489     Image
01490       *current_image;
01491 
01492     /*
01493       Overlay this frame's image over the previous disposal image
01494     */
01495     current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
01496     if (current_image == (Image *) NULL)
01497       {
01498         dispose_image=DestroyImage(dispose_image);
01499         return;
01500       }
01501     (void) CompositeImage(current_image,next->matte != MagickFalse ?
01502       OverCompositeOp : CopyCompositeOp, next,next->page.x,next->page.y,
01503       exception);
01504     /*
01505       At this point the image would be displayed, for the delay period
01506     **
01507       Work out the disposal of the previous image
01508     */
01509     if (next->dispose == BackgroundDispose)
01510       {
01511         RectangleInfo
01512           bounds=next->page;
01513 
01514         bounds.width=next->columns;
01515         bounds.height=next->rows;
01516         if (bounds.x < 0)
01517           {
01518             bounds.width+=bounds.x;
01519             bounds.x=0;
01520           }
01521         if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
01522           bounds.width=current_image->columns-bounds.x;
01523         if (bounds.y < 0)
01524           {
01525             bounds.height+=bounds.y;
01526             bounds.y=0;
01527           }
01528         if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
01529           bounds.height=current_image->rows-bounds.y;
01530         ClearBounds(current_image, &bounds,exception);
01531       }
01532     if (next->dispose != PreviousDispose)
01533       {
01534         dispose_image=DestroyImage(dispose_image);
01535         dispose_image=current_image;
01536       }
01537     else
01538       current_image=DestroyImage(current_image);
01539 
01540     /*
01541       Optimize Transparency of the next frame (if present)
01542     */
01543     next=GetNextImageInList(next);
01544     if ( next != (Image *) NULL ) {
01545       (void) CompositeImage(next, ChangeMaskCompositeOp,
01546         dispose_image, -(next->page.x), -(next->page.y), exception );
01547     }
01548   }
01549   dispose_image=DestroyImage(dispose_image);
01550   return;
01551 }
01552 
01553 /*
01554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01555 %                                                                             %
01556 %                                                                             %
01557 %                                                                             %
01558 %     R e m o v e D u p l i c a t e L a y e r s                               %
01559 %                                                                             %
01560 %                                                                             %
01561 %                                                                             %
01562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01563 %
01564 %  RemoveDuplicateLayers() removes any image that is exactly the same as the
01565 %  next image in the given image list.  Image size and virtual canvas offset
01566 %  must also match, though not the virtual canvas size itself.
01567 %
01568 %  No check is made with regards to image disposal setting, though it is the
01569 %  dispose setting of later image that is kept.  Also any time delays are also
01570 %  added together. As such coalesced image animations should still produce the
01571 %  same result, though with duplicte frames merged into a single frame.
01572 %
01573 %  The format of the RemoveDuplicateLayers method is:
01574 %
01575 %      void RemoveDuplicateLayers(Image **image, ExceptionInfo *exception)
01576 %
01577 %  A description of each parameter follows:
01578 %
01579 %    o images: the image list
01580 %
01581 %    o exception: return any errors or warnings in this structure.
01582 %
01583 */
01584 MagickExport void RemoveDuplicateLayers(Image **images,
01585      ExceptionInfo *exception)
01586 {
01587   register Image
01588     *curr,
01589     *next;
01590 
01591   RectangleInfo
01592     bounds;
01593 
01594   assert((*images) != (const Image *) NULL);
01595   assert((*images)->signature == MagickSignature);
01596   if ((*images)->debug != MagickFalse)
01597     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
01598   assert(exception != (ExceptionInfo *) NULL);
01599   assert(exception->signature == MagickSignature);
01600 
01601   curr=GetFirstImageInList(*images);
01602   for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next)
01603   {
01604     if ( curr->columns != next->columns || curr->rows != next->rows
01605          || curr->page.x != next->page.x || curr->page.y != next->page.y )
01606       continue;
01607     bounds=CompareImagesBounds(curr,next,CompareAnyLayer,exception);
01608     if ( bounds.x < 0 ) {
01609       /*
01610         the two images are the same, merge time delays and delete one.
01611       */
01612       size_t time;
01613       time = curr->delay*1000/curr->ticks_per_second;
01614       time += next->delay*1000/next->ticks_per_second;
01615       next->ticks_per_second = 100L;
01616       next->delay = time*curr->ticks_per_second/1000;
01617       next->iterations = curr->iterations;
01618       *images = curr;
01619       (void) DeleteImageFromList(images);
01620     }
01621   }
01622   *images = GetFirstImageInList(*images);
01623 }
01624 
01625 /*
01626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01627 %                                                                             %
01628 %                                                                             %
01629 %                                                                             %
01630 %     R e m o v e Z e r o D e l a y L a y e r s                               %
01631 %                                                                             %
01632 %                                                                             %
01633 %                                                                             %
01634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01635 %
01636 %  RemoveZeroDelayLayers() removes any image that as a zero delay time. Such
01637 %  images generally represent intermediate or partial updates in GIF
01638 %  animations used for file optimization.  They are not ment to be displayed
01639 %  to users of the animation.  Viewable images in an animation should have a
01640 %  time delay of 3 or more centi-seconds (hundredths of a second).
01641 %
01642 %  However if all the frames have a zero time delay, then either the animation
01643 %  is as yet incomplete, or it is not a GIF animation.  This a non-sensible
01644 %  situation, so no image will be removed and a 'Zero Time Animation' warning
01645 %  (exception) given.
01646 %
01647 %  No warning will be given if no image was removed because all images had an
01648 %  appropriate non-zero time delay set.
01649 %
01650 %  Due to the special requirements of GIF disposal handling, GIF animations
01651 %  should be coalesced first, before calling this function, though that is not
01652 %  a requirement.
01653 %
01654 %  The format of the RemoveZeroDelayLayers method is:
01655 %
01656 %      void RemoveZeroDelayLayers(Image **image, ExceptionInfo *exception)
01657 %
01658 %  A description of each parameter follows:
01659 %
01660 %    o images: the image list
01661 %
01662 %    o exception: return any errors or warnings in this structure.
01663 %
01664 */
01665 MagickExport void RemoveZeroDelayLayers(Image **images,
01666      ExceptionInfo *exception)
01667 {
01668   Image
01669     *i;
01670 
01671   assert((*images) != (const Image *) NULL);
01672   assert((*images)->signature == MagickSignature);
01673   if ((*images)->debug != MagickFalse)
01674     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
01675   assert(exception != (ExceptionInfo *) NULL);
01676   assert(exception->signature == MagickSignature);
01677 
01678   i=GetFirstImageInList(*images);
01679   for ( ; i != (Image *) NULL; i=GetNextImageInList(i))
01680     if ( i->delay != 0L ) break;
01681   if ( i == (Image *) NULL ) {
01682     (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
01683        "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename);
01684     return;
01685   }
01686   i=GetFirstImageInList(*images);
01687   while ( i != (Image *) NULL )
01688   {
01689     if ( i->delay == 0L ) {
01690       (void) DeleteImageFromList(&i);
01691       *images=i;
01692     }
01693     else
01694       i=GetNextImageInList(i);
01695   }
01696   *images=GetFirstImageInList(*images);
01697 }
01698 
01699 /*
01700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01701 %                                                                             %
01702 %                                                                             %
01703 %                                                                             %
01704 %     C o m p o s i t e L a y e r s                                           %
01705 %                                                                             %
01706 %                                                                             %
01707 %                                                                             %
01708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01709 %
01710 %  CompositeLayers() compose first image sequence (source) over the second
01711 %  image sequence (destination), using the given compose method and offsets.
01712 %
01713 %  The pointers to the image list does not have to be the start of that image
01714 %  list, but may start somewhere in the middle.  Each layer from the two image
01715 %  lists are composted together until the end of one of the image lists is
01716 %  reached.  The offset of each composition is also adjusted to match the
01717 %  virtual canvas offsets of each layer. As such the given offset is relative
01718 %  to the virtual canvas, and not the actual image.
01719 %
01720 %  No GIF disposal handling is performed, so GIF animations should be
01721 %  coalesced before use.  However this not a requirement, and individual
01722 %  layer images may have any size or offset, for special compositions.
01723 %
01724 %  Special case:- If one of the image sequences is just a single image that
01725 %  image is repeatally composed with all the images in the other image list.
01726 %  Either the source or destination lists may be the single image, for this
01727 %  situation.
01728 %
01729 %  The destination list will be expanded as needed to match number of source
01730 %  image overlaid (from current position to end of list).
01731 %
01732 %  The format of the CompositeLayers method is:
01733 %
01734 %      void CompositeLayers(Image *destination,
01735 %          const CompositeOperator compose, Image *source,
01736 %          const ssize_t x_offset, const ssize_t y_offset,
01737 %          ExceptionInfo *exception);
01738 %
01739 %  A description of each parameter follows:
01740 %
01741 %    o destination: the destination images and results
01742 %
01743 %    o source: source image(s) for the layer composition
01744 %
01745 %    o compose, x_offset, y_offset:  arguments passed on to CompositeImages()
01746 %
01747 %    o exception: return any errors or warnings in this structure.
01748 %
01749 */
01750 
01751 static inline void CompositeCanvas(Image *destination,
01752   const CompositeOperator compose,Image *source,ssize_t x_offset,
01753   ssize_t y_offset,ExceptionInfo *exception)
01754 {
01755   x_offset+=source->page.x-destination->page.x;
01756   y_offset+=source->page.y-destination->page.y;
01757   (void) CompositeImage(destination,compose,source,x_offset,y_offset,
01758     exception);
01759 }
01760 
01761 MagickExport void CompositeLayers(Image *destination,
01762   const CompositeOperator compose, Image *source,const ssize_t x_offset,
01763   const ssize_t y_offset,ExceptionInfo *exception)
01764 {
01765   assert(destination != (Image *) NULL);
01766   assert(destination->signature == MagickSignature);
01767   assert(source != (Image *) NULL);
01768   assert(source->signature == MagickSignature);
01769   assert(exception != (ExceptionInfo *) NULL);
01770   assert(exception->signature == MagickSignature);
01771   if (source->debug != MagickFalse || destination->debug != MagickFalse)
01772     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
01773       source->filename, destination->filename);
01774 
01775   /*
01776     Overlay single source image over destation image/list
01777   */
01778   if ( source->previous == (Image *) NULL && source->next == (Image *) NULL )
01779     while ( destination != (Image *) NULL )
01780     {
01781       CompositeCanvas(destination, compose, source, x_offset, y_offset,
01782         exception);
01783       destination=GetNextImageInList(destination);
01784     }
01785 
01786   /*
01787     Overlay source image list over single destination
01788     Generating multiple clones of destination image to match source list.
01789     Original Destination image becomes first image of generated list.
01790     As such the image list pointer does not require any change in caller.
01791     Some animation attributes however also needs coping in this case.
01792   */
01793   else if ( destination->previous == (Image *) NULL &&
01794             destination->next == (Image *) NULL )
01795   {
01796     Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
01797 
01798     CompositeCanvas(destination, compose, source, x_offset, y_offset,
01799       exception);
01800     /* copy source image attributes ? */
01801     if ( source->next != (Image *) NULL )
01802       {
01803         destination->delay = source->delay;
01804         destination->iterations = source->iterations;
01805       }
01806     source=GetNextImageInList(source);
01807 
01808     while ( source != (Image *) NULL )
01809     {
01810       AppendImageToList(&destination,
01811            CloneImage(dest,0,0,MagickTrue,exception));
01812       destination=GetLastImageInList(destination);
01813 
01814       CompositeCanvas(destination, compose, source, x_offset, y_offset,
01815         exception);
01816       destination->delay = source->delay;
01817       destination->iterations = source->iterations;
01818       source=GetNextImageInList(source);
01819     }
01820     dest=DestroyImage(dest);
01821   }
01822 
01823   /*
01824     Overlay a source image list over a destination image list
01825     until either list runs out of images. (Does not repeat)
01826   */
01827   else
01828     while ( source != (Image *) NULL && destination != (Image *) NULL )
01829     {
01830       CompositeCanvas(destination, compose, source, x_offset, y_offset,
01831         exception);
01832       source=GetNextImageInList(source);
01833       destination=GetNextImageInList(destination);
01834     }
01835 }
01836 
01837 /*
01838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01839 %                                                                             %
01840 %                                                                             %
01841 %                                                                             %
01842 %     M e r g e I m a g e L a y e r s                                         %
01843 %                                                                             %
01844 %                                                                             %
01845 %                                                                             %
01846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01847 %
01848 %  MergeImageLayers() composes all the image layers from the current given
01849 %  image onward to produce a single image of the merged layers.
01850 %
01851 %  The inital canvas's size depends on the given ImageLayerMethod, and is
01852 %  initialized using the first images background color.  The images
01853 %  are then compositied onto that image in sequence using the given
01854 %  composition that has been assigned to each individual image.
01855 %
01856 %  The format of the MergeImageLayers is:
01857 %
01858 %      Image *MergeImageLayers(const Image *image,
01859 %        const ImageLayerMethod method, ExceptionInfo *exception)
01860 %
01861 %  A description of each parameter follows:
01862 %
01863 %    o image: the image list to be composited together
01864 %
01865 %    o method: the method of selecting the size of the initial canvas.
01866 %
01867 %        MergeLayer: Merge all layers onto a canvas just large enough
01868 %           to hold all the actual images. The virtual canvas of the
01869 %           first image is preserved but otherwise ignored.
01870 %
01871 %        FlattenLayer: Use the virtual canvas size of first image.
01872 %           Images which fall outside this canvas is clipped.
01873 %           This can be used to 'fill out' a given virtual canvas.
01874 %
01875 %        MosaicLayer: Start with the virtual canvas of the first image,
01876 %           enlarging left and right edges to contain all images.
01877 %           Images with negative offsets will be clipped.
01878 %
01879 %        TrimBoundsLayer: Determine the overall bounds of all the image
01880 %           layers just as in "MergeLayer", then adjust the the canvas
01881 %           and offsets to be relative to those bounds, without overlaying
01882 %           the images.
01883 %
01884 %           WARNING: a new image is not returned, the original image
01885 %           sequence page data is modified instead.
01886 %
01887 %    o exception: return any errors or warnings in this structure.
01888 %
01889 */
01890 MagickExport Image *MergeImageLayers(Image *image,const ImageLayerMethod method,
01891   ExceptionInfo *exception)
01892 {
01893 #define MergeLayersTag  "Merge/Layers"
01894 
01895   Image
01896     *canvas;
01897 
01898   MagickBooleanType
01899     proceed;
01900 
01901   RectangleInfo
01902     page;
01903 
01904   register const Image
01905     *next;
01906 
01907   size_t
01908     number_images,
01909     height,
01910     width;
01911 
01912   ssize_t
01913     scene;
01914 
01915   assert(image != (Image *) NULL);
01916   assert(image->signature == MagickSignature);
01917   if (image->debug != MagickFalse)
01918     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01919   assert(exception != (ExceptionInfo *) NULL);
01920   assert(exception->signature == MagickSignature);
01921   /*
01922     Determine canvas image size, and its virtual canvas size and offset
01923   */
01924   page=image->page;
01925   width=image->columns;
01926   height=image->rows;
01927   switch (method)
01928   {
01929     case TrimBoundsLayer:
01930     case MergeLayer:
01931     default:
01932     {
01933       next=GetNextImageInList(image);
01934       for ( ; next != (Image *) NULL;  next=GetNextImageInList(next))
01935       {
01936         if (page.x > next->page.x)
01937           {
01938             width+=page.x-next->page.x;
01939             page.x=next->page.x;
01940           }
01941         if (page.y > next->page.y)
01942           {
01943             height+=page.y-next->page.y;
01944             page.y=next->page.y;
01945           }
01946         if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x))
01947           width=(size_t) next->page.x+(ssize_t)next->columns-page.x;
01948         if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y))
01949           height=(size_t) next->page.y+(ssize_t) next->rows-page.y;
01950       }
01951       break;
01952     }
01953     case FlattenLayer:
01954     {
01955       if (page.width > 0)
01956         width=page.width;
01957       if (page.height > 0)
01958         height=page.height;
01959       page.x=0;
01960       page.y=0;
01961       break;
01962     }
01963     case MosaicLayer:
01964     {
01965       if (page.width > 0)
01966         width=page.width;
01967       if (page.height > 0)
01968         height=page.height;
01969       for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
01970       {
01971         if (method == MosaicLayer)
01972           {
01973             page.x=next->page.x;
01974             page.y=next->page.y;
01975             if ((ssize_t) width < (next->page.x+(ssize_t) next->columns))
01976               width=(size_t) next->page.x+next->columns;
01977             if ((ssize_t) height < (next->page.y+(ssize_t) next->rows))
01978               height=(size_t) next->page.y+next->rows;
01979           }
01980       }
01981       page.width=width;
01982       page.height=height;
01983       page.x=0;
01984       page.y=0;
01985     }
01986     break;
01987   }
01988   /*
01989     Set virtual canvas size if not defined.
01990   */
01991   if (page.width == 0)
01992     page.width=page.x < 0 ? width : width+page.x;
01993   if (page.height == 0)
01994     page.height=page.y < 0 ? height : height+page.y;
01995   /*
01996     Handle "TrimBoundsLayer" method separately to normal 'layer merge'.
01997   */
01998   if (method == TrimBoundsLayer)
01999     {
02000       number_images=GetImageListLength(image);
02001       for (scene=0; scene < (ssize_t) number_images; scene++)
02002       {
02003         image->page.x-=page.x;
02004         image->page.y-=page.y;
02005         image->page.width=width;
02006         image->page.height=height;
02007         proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
02008           number_images);
02009         if (proceed == MagickFalse)
02010           break;
02011         image=GetNextImageInList(image);
02012         if (image == (Image *) NULL)
02013           break;
02014       }
02015       return((Image *) NULL);
02016     }
02017   /*
02018     Create canvas size of width and height, and background color.
02019   */
02020   canvas=CloneImage(image,width,height,MagickTrue,exception);
02021   if (canvas == (Image *) NULL)
02022     return((Image *) NULL);
02023   (void) SetImageBackgroundColor(canvas,exception);
02024   canvas->page=page;
02025   canvas->dispose=UndefinedDispose;
02026   /*
02027     Compose images onto canvas, with progress monitor
02028   */
02029   number_images=GetImageListLength(image);
02030   for (scene=0; scene < (ssize_t) number_images; scene++)
02031   {
02032     (void) CompositeImage(canvas,image->compose,image,image->page.x-
02033       canvas->page.x,image->page.y-canvas->page.y,exception);
02034     proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
02035       number_images);
02036     if (proceed == MagickFalse)
02037       break;
02038     image=GetNextImageInList(image);
02039     if (image == (Image *) NULL)
02040       break;
02041   }
02042   return(canvas);
02043 }
02044