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

Generated on 19 Nov 2009 for MagickCore by  doxygen 1.6.1