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-2008 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   register long
00104     x;
00105 
00106   register PixelPacket
00107     *q;
00108 
00109   if ( bounds->x < 0 ) return;
00110   if (image->matte == MagickFalse)
00111     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
00112   exception=(&image->exception);
00113   for (y=0; y < (long) bounds->height; y++)
00114   {
00115     q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
00116     if (q == (PixelPacket *) NULL)
00117       break;
00118     for (x=0; x < (long) bounds->width; x++)
00119     {
00120       q->opacity=(Quantum) TransparentOpacity;
00121       q++;
00122     }
00123     if (SyncAuthenticPixels(image,exception) == MagickFalse)
00124       break;
00125   }
00126 }
00127 
00128 /*
00129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00130 %                                                                             %
00131 %                                                                             %
00132 %                                                                             %
00133 +     I s B o u n d s C l e a r e d                                           %
00134 %                                                                             %
00135 %                                                                             %
00136 %                                                                             %
00137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00138 %
00139 %  IsBoundsCleared() tests whether any pixel in the bounds given, gets cleared
00140 %  when going from the first image to the second image.  This typically used
00141 %  to check if a proposed disposal method will work successfully to generate
00142 %  the second frame image from the first disposed form of the previous frame.
00143 %
00144 %  The format is:
00145 %
00146 %      MagickBooleanType IsBoundsCleared(const Image *image1,
00147 %        const Image *image2,RectangleInfo bounds,ExceptionInfo *exception)
00148 %
00149 %  A description of each parameter follows:
00150 %
00151 %    o image1, image 2: the images to check for cleared pixels
00152 %
00153 %    o bounds: the area to be clear within the imag image
00154 %
00155 %    o exception: return any errors or warnings in this structure.
00156 %
00157 %  WARNING: no bounds checks are performed, except for the null or
00158 %  missed image, for images that don't change. in all other cases
00159 %  bound must fall within the image.
00160 %
00161 */
00162 static MagickBooleanType IsBoundsCleared(const Image *image1,
00163   const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception)
00164 {
00165   long
00166     y;
00167 
00168   register long
00169     x;
00170 
00171   register const PixelPacket
00172     *p,
00173     *q;
00174 
00175   if ( bounds->x< 0 ) return(MagickFalse);
00176 
00177   for (y=0; y < (long) bounds->height; y++)
00178   {
00179     p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,
00180       exception);
00181     q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,
00182       exception);
00183     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00184       break;
00185     for (x=0; x < (long) bounds->width; x++)
00186     {
00187       if ((p->opacity <= (long) (QuantumRange/2)) &&
00188           (q->opacity > (long) (QuantumRange/2)))
00189         break;
00190       p++;
00191       q++;
00192     }
00193     if (x < (long) bounds->width)
00194       break;
00195   }
00196   return(y < (long) bounds->height ? MagickTrue : MagickFalse);
00197 }
00198 
00199 /*
00200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00201 %                                                                             %
00202 %                                                                             %
00203 %                                                                             %
00204 %     C o a l e s c e I m a g e s                                             %
00205 %                                                                             %
00206 %                                                                             %
00207 %                                                                             %
00208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00209 %
00210 %  CoalesceImages() composites a set of images while respecting any page
00211 %  offsets and disposal methods.  GIF, MIFF, and MNG animation sequences
00212 %  typically start with an image background and each subsequent image
00213 %  varies in size and offset.  A new image sequence is returned with all
00214 %  images the same size as the first images virtual canvas and composited
00215 %  with the next image in the sequence.
00216 %
00217 %  The format of the CoalesceImages method is:
00218 %
00219 %      Image *CoalesceImages(Image *image,ExceptionInfo *exception)
00220 %
00221 %  A description of each parameter follows:
00222 %
00223 %    o image: the image sequence.
00224 %
00225 %    o exception: return any errors or warnings in this structure.
00226 %
00227 */
00228 MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
00229 {
00230   Image
00231     *coalesce_image,
00232     *dispose_image,
00233     *previous;
00234 
00235   register Image
00236     *next;
00237 
00238   RectangleInfo
00239     bounds;
00240 
00241   /*
00242     Coalesce the image sequence.
00243   */
00244   assert(image != (Image *) NULL);
00245   assert(image->signature == MagickSignature);
00246   if (image->debug != MagickFalse)
00247     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00248   assert(exception != (ExceptionInfo *) NULL);
00249   assert(exception->signature == MagickSignature);
00250 
00251   /* initialise first image */
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   coalesce_image->background_color.opacity=(Quantum) TransparentOpacity;
00275   (void) SetImageBackgroundColor(coalesce_image);
00276   /*
00277     Coalesce rest of the images.
00278   */
00279   dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
00280   (void) CompositeImage(coalesce_image,CopyCompositeOp,next,next->page.x,
00281     next->page.y);
00282   next=GetNextImageInList(next);
00283   for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
00284   {
00285     /*
00286       Determine the bounds that was overlaid in the previous image.
00287     */
00288     previous=GetPreviousImageInList(next);
00289     bounds=previous->page;
00290     bounds.width=previous->columns;
00291     bounds.height=previous->rows;
00292     if (bounds.x < 0)
00293       {
00294         bounds.width+=bounds.x;
00295         bounds.x=0;
00296       }
00297     if ((long) (bounds.x+bounds.width) > (long) coalesce_image->columns)
00298       bounds.width=coalesce_image->columns-bounds.x;
00299     if (bounds.y < 0)
00300       {
00301         bounds.height+=bounds.y;
00302         bounds.y=0;
00303       }
00304     if ((long) (bounds.y+bounds.height) > (long) coalesce_image->rows)
00305       bounds.height=coalesce_image->rows-bounds.y;
00306     /*
00307       Replace the dispose image with the new coalesced image.
00308     */
00309     if (GetPreviousImageInList(next)->dispose != PreviousDispose)
00310       {
00311         dispose_image=DestroyImage(dispose_image);
00312         dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
00313         if (dispose_image == (Image *) NULL)
00314           {
00315             coalesce_image=DestroyImageList(coalesce_image);
00316             return((Image *) NULL);
00317           }
00318       }
00319     /*
00320       Clear the overlaid area of the coalesced bounds for background disposal
00321     */
00322     if (next->previous->dispose == BackgroundDispose)
00323       ClearBounds(dispose_image, &bounds);
00324     /*
00325       Next image is the dispose image, overlaid with next frame in sequence.
00326     */
00327     coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception);
00328     coalesce_image->next->previous=coalesce_image;
00329     previous=coalesce_image;
00330     coalesce_image=GetNextImageInList(coalesce_image);
00331     coalesce_image->matte=MagickTrue;
00332     (void) CompositeImage(coalesce_image,next->matte != MagickFalse ?
00333       OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
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     *next;
00385 
00386   /*
00387     Run the image through the animation sequence
00388   */
00389   assert(image != (Image *) NULL);
00390   assert(image->signature == MagickSignature);
00391   if (image->debug != MagickFalse)
00392     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00393   assert(exception != (ExceptionInfo *) NULL);
00394   assert(exception->signature == MagickSignature);
00395   next=GetFirstImageInList(image);
00396   dispose_image=CloneImage(next,next->page.width,next->page.height,MagickTrue,
00397     exception);
00398   if (dispose_image == (Image *) NULL)
00399     return((Image *) NULL);
00400   dispose_image->page=next->page;
00401   dispose_image->page.x=0;
00402   dispose_image->page.y=0;
00403   dispose_image->dispose=NoneDispose;
00404   dispose_image->background_color.opacity=(Quantum) TransparentOpacity;
00405   (void) SetImageBackgroundColor(dispose_image);
00406   dispose_images=NewImageList();
00407   for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
00408   {
00409     Image
00410       *current_image;
00411 
00412     /*
00413       Overlay this frame's image over the previous disposal image.
00414     */
00415     current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
00416     if (current_image == (Image *) NULL)
00417       {
00418         dispose_images=DestroyImageList(dispose_images);
00419         dispose_image=DestroyImage(dispose_image);
00420         return((Image *) NULL);
00421       }
00422     (void) CompositeImage(current_image,next->matte != MagickFalse ?
00423       OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
00424     /*
00425       Handle Background dispose: image is displayed for the delay period.
00426     */
00427     if (next->dispose == BackgroundDispose)
00428       {
00429         RectangleInfo
00430           bounds;
00431 
00432         bounds=next->page;
00433         bounds.width=next->columns;
00434         bounds.height=next->rows;
00435         if (bounds.x < 0)
00436           {
00437             bounds.width+=bounds.x;
00438             bounds.x=0;
00439           }
00440         if ((long) (bounds.x+bounds.width) > (long) current_image->columns)
00441           bounds.width=current_image->columns-bounds.x;
00442         if (bounds.y < 0)
00443           {
00444             bounds.height+=bounds.y;
00445             bounds.y=0;
00446           }
00447         if ((long) (bounds.y+bounds.height) > (long) current_image->rows)
00448           bounds.height=current_image->rows-bounds.y;
00449         ClearBounds(current_image,&bounds);
00450       }
00451     /*
00452       Select the appropriate previous/disposed image.
00453     */
00454     if (next->dispose == PreviousDispose)
00455       current_image=DestroyImage(current_image);
00456     else
00457       {
00458         dispose_image=DestroyImage(dispose_image);
00459         dispose_image=current_image;
00460       }
00461     {
00462       Image
00463         *dispose;
00464 
00465       /*
00466         Save the dispose image just calculated for return.
00467       */
00468       dispose=CloneImage(dispose_image,0,0,MagickTrue,exception);
00469       if (dispose == (Image *) NULL)
00470         {
00471           dispose_images=DestroyImageList(dispose_images);
00472           dispose_image=DestroyImage(dispose_image);
00473           return((Image *) NULL);
00474         }
00475       (void) CloneImageProfiles(dispose,next);
00476       (void) CloneImageProperties(dispose,next);
00477       (void) CloneImageArtifacts(dispose,next);
00478       dispose->page.x=0;
00479       dispose->page.y=0;
00480       dispose->dispose=next->dispose;
00481       AppendImageToList(&dispose_images,dispose);
00482     }
00483   }
00484   dispose_image=DestroyImage(dispose_image);
00485   return(GetFirstImageInList(dispose_images));
00486 }
00487 
00488 /*
00489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00490 %                                                                             %
00491 %                                                                             %
00492 %                                                                             %
00493 +     C o m p a r e P i x e l s                                               %
00494 %                                                                             %
00495 %                                                                             %
00496 %                                                                             %
00497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00498 %
00499 %  ComparePixels() Compare the two pixels and return true if the pixels
00500 %  differ according to the given LayerType comparision method.
00501 %
00502 %  This currently only used internally by CompareImageBounds(). It is
00503 %  doubtful that this sub-routine will be useful outside this module.
00504 %
00505 %  The format of the ComparePixels method is:
00506 %
00507 %      MagickBooleanType *ComparePixels(const ImageLayerMethod method,
00508 %        const MagickPixelPacket *p,const MagickPixelPacket *q)
00509 %
00510 %  A description of each parameter follows:
00511 %
00512 %    o method: What differences to look for. Must be one of
00513 %              CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
00514 %
00515 %    o p, q: the pixels to test for appropriate differences.
00516 %
00517 */
00518 
00519 static MagickBooleanType ComparePixels(const ImageLayerMethod method,
00520   const MagickPixelPacket *p,const MagickPixelPacket *q)
00521 {
00522   MagickRealType
00523     o1,
00524     o2;
00525 
00526   /*
00527     Any change in pixel values
00528   */
00529   if (method == CompareAnyLayer)
00530     return(IsMagickColorSimilar(p,q) == MagickFalse ? MagickTrue : MagickFalse);
00531 
00532   o1 = (p->matte != MagickFalse) ? p->opacity : OpaqueOpacity;
00533   o2 = (q->matte != MagickFalse) ? q->opacity : OpaqueOpacity;
00534 
00535   /*
00536     Pixel goes from opaque to transprency
00537   */
00538   if (method == CompareClearLayer)
00539     return((MagickBooleanType) ( (o1 <= ((MagickRealType) QuantumRange/2.0)) &&
00540       (o2 > ((MagickRealType) QuantumRange/2.0)) ) );
00541 
00542   /*
00543     overlay would change first pixel by second
00544   */
00545   if (method == CompareOverlayLayer)
00546     {
00547       if (o2 > ((MagickRealType) QuantumRange/2.0))
00548         return MagickFalse;
00549       return((MagickBooleanType) (IsMagickColorSimilar(p,q) == MagickFalse));
00550     }
00551   return(MagickFalse);
00552 }
00553 
00554 
00555 /*
00556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00557 %                                                                             %
00558 %                                                                             %
00559 %                                                                             %
00560 +     C o m p a r e I m a g e B o u n d s                                     %
00561 %                                                                             %
00562 %                                                                             %
00563 %                                                                             %
00564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00565 %
00566 %  CompareImageBounds() Given two images return the smallest rectangular area
00567 %  by which the two images differ, accourding to the given 'Compare...'
00568 %  layer method.
00569 %
00570 %  This currently only used internally in this module, but may eventually
00571 %  be used by other modules.
00572 %
00573 %  The format of the CompareImageBounds method is:
00574 %
00575 %      RectangleInfo *CompareImageBounds(const ImageLayerMethod method,
00576 %        const Image *image1, const Image *image2, ExceptionInfo *exception)
00577 %
00578 %  A description of each parameter follows:
00579 %
00580 %    o method: What differences to look for. Must be one of
00581 %              CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
00582 %
00583 %    o image1, image2: the two images to compare.
00584 %
00585 %    o exception: return any errors or warnings in this structure.
00586 %
00587 */
00588 
00589 static RectangleInfo CompareImageBounds(const Image *image1,const Image *image2,
00590   const ImageLayerMethod method,ExceptionInfo *exception)
00591 {
00592   RectangleInfo
00593     bounds;
00594 
00595   MagickPixelPacket
00596     pixel1,
00597     pixel2;
00598 
00599   register const IndexPacket
00600     *indexes1,
00601     *indexes2;
00602 
00603   register const PixelPacket
00604     *p,
00605     *q;
00606 
00607   long
00608     y;
00609 
00610   register long
00611     x;
00612 
00613   /*
00614     Set bounding box of the differences between images
00615   */
00616   GetMagickPixelPacket(image1,&pixel1);
00617   GetMagickPixelPacket(image2,&pixel2);
00618   for (x=0; x < (long) image1->columns; x++)
00619   {
00620     p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
00621     q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
00622     if ((p == (const PixelPacket *) NULL) ||
00623         (q == (const PixelPacket *) NULL))
00624       break;
00625     indexes1=GetVirtualIndexQueue(image1);
00626     indexes2=GetVirtualIndexQueue(image2);
00627     for (y=0; y < (long) image1->rows; y++)
00628     {
00629       SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
00630       SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
00631       if (ComparePixels(method,&pixel1,&pixel2))
00632         break;
00633       p++;
00634       q++;
00635     }
00636     if (y < (long) image1->rows)
00637       break;
00638   }
00639   if ( x >= (long) image1->columns) {
00640     /*
00641       Images are identical! - No need to look further
00642       Result in the null 'miss' image.
00643     */
00644     bounds.x=-1;
00645     bounds.y=-1;
00646     bounds.width=1;
00647     bounds.height=1;
00648     return(bounds);
00649   }
00650   bounds.x=x;
00651   for (x=(long) image1->columns-1; x >= 0; x--)
00652   {
00653     p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
00654     q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
00655     if ((p == (const PixelPacket *) NULL) ||
00656         (q == (const PixelPacket *) NULL))
00657       break;
00658     indexes1=GetVirtualIndexQueue(image1);
00659     indexes2=GetVirtualIndexQueue(image2);
00660     for (y=0; y < (long) image1->rows; y++)
00661     {
00662       SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
00663       SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
00664       if (ComparePixels(method,&pixel1,&pixel2))
00665         break;
00666       p++;
00667       q++;
00668     }
00669     if (y < (long) image1->rows)
00670       break;
00671   }
00672   bounds.width=(unsigned long) (x-bounds.x+1);
00673   for (y=0; y < (long) image1->rows; y++)
00674   {
00675     p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
00676     q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
00677     if ((p == (const PixelPacket *) NULL) ||
00678         (q == (const PixelPacket *) NULL))
00679       break;
00680     indexes1=GetVirtualIndexQueue(image1);
00681     indexes2=GetVirtualIndexQueue(image2);
00682     for (x=0; x < (long) image1->columns; x++)
00683     {
00684       SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
00685       SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
00686       if (ComparePixels(method,&pixel1,&pixel2))
00687         break;
00688       p++;
00689       q++;
00690     }
00691     if (x < (long) image1->columns)
00692       break;
00693   }
00694   bounds.y=y;
00695   for (y=(long) image1->rows-1; y >= 0; y--)
00696   {
00697     p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
00698     q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
00699     if ((p == (const PixelPacket *) NULL) ||
00700         (q == (const PixelPacket *) NULL))
00701       break;
00702     indexes1=GetVirtualIndexQueue(image1);
00703     indexes2=GetVirtualIndexQueue(image2);
00704     for (x=0; x < (long) image1->columns; x++)
00705     {
00706       SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
00707       SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
00708       if (ComparePixels(method,&pixel1,&pixel2))
00709         break;
00710       p++;
00711       q++;
00712     }
00713     if (x < (long) image1->columns)
00714       break;
00715   }
00716   bounds.height=(unsigned long) (y-bounds.y+1);
00717   return(bounds);
00718 }
00719 
00720 /*
00721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00722 %                                                                             %
00723 %                                                                             %
00724 %                                                                             %
00725 %     C o m p a r e I m a g e L a y e r s                                     %
00726 %                                                                             %
00727 %                                                                             %
00728 %                                                                             %
00729 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00730 %
00731 %  CompareImageLayers() compares each image with the next in a sequence and
00732 %  returns the minimum bounding region of all the pixel differences (of the
00733 %  ImageLayerMethod specified) it discovers.
00734 %
00735 %  Images do NOT have to be the same size, though it is best that all the
00736 %  images are 'coalesced' (images are all the same size, on a flattened
00737 %  canvas, so as to represent exactly how an specific frame should look).
00738 %
00739 %  No GIF dispose methods are applied, so GIF animations must be coalesced
00740 %  before applying this image operator to find differences to them.
00741 %
00742 %  The format of the CompareImageLayers method is:
00743 %
00744 %      Image *CompareImageLayers(const Image *images,
00745 %        const ImageLayerMethod method,ExceptionInfo *exception)
00746 %
00747 %  A description of each parameter follows:
00748 %
00749 %    o image: the image.
00750 %
00751 %    o method: the layers type to compare images with. Must be one of...
00752 %              CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
00753 %
00754 %    o exception: return any errors or warnings in this structure.
00755 %
00756 */
00757 
00758 MagickExport Image *CompareImageLayers(const Image *image,
00759   const ImageLayerMethod method, ExceptionInfo *exception)
00760 {
00761   Image
00762     *image_a,
00763     *image_b,
00764     *layers;
00765 
00766   RectangleInfo
00767     *bounds;
00768 
00769   register const Image
00770     *next;
00771 
00772   register long
00773     i;
00774 
00775   assert(image != (const Image *) NULL);
00776   assert(image->signature == MagickSignature);
00777   if (image->debug != MagickFalse)
00778     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00779   assert(exception != (ExceptionInfo *) NULL);
00780   assert(exception->signature == MagickSignature);
00781   assert((method == CompareAnyLayer) ||
00782          (method == CompareClearLayer) ||
00783          (method == CompareOverlayLayer));
00784   /*
00785     Allocate bounds memory.
00786   */
00787   next=GetFirstImageInList(image);
00788   bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
00789     GetImageListLength(next),sizeof(*bounds));
00790   if (bounds == (RectangleInfo *) NULL)
00791     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00792   /*
00793     Set up first comparision images.
00794   */
00795   image_a=CloneImage(next,next->page.width,next->page.height,
00796     MagickTrue,exception);
00797   if (image_a == (Image *) NULL)
00798     {
00799       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00800       return((Image *) NULL);
00801     }
00802   image_a->background_color.opacity=(Quantum) TransparentOpacity;
00803   (void) SetImageBackgroundColor(image_a);
00804   image_a->page=next->page;
00805   image_a->page.x=0;
00806   image_a->page.y=0;
00807   (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,next->page.y);
00808   /*
00809     Compute the bounding box of changes for the later images
00810   */
00811   i=0;
00812   next=GetNextImageInList(next);
00813   for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
00814   {
00815     image_b=CloneImage(image_a,0,0,MagickTrue,exception);
00816     if (image_b == (Image *) NULL)
00817       {
00818         image_a=DestroyImage(image_a);
00819         bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00820         return((Image *) NULL);
00821       }
00822     (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,
00823                            next->page.y);
00824     bounds[i]=CompareImageBounds(image_b,image_a,method,exception);
00825 
00826     image_b=DestroyImage(image_b);
00827     i++;
00828   }
00829   image_a=DestroyImage(image_a);
00830   /*
00831     Clone first image in sequence.
00832   */
00833   next=GetFirstImageInList(image);
00834   layers=CloneImage(next,0,0,MagickTrue,exception);
00835   if (layers == (Image *) NULL)
00836     {
00837       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00838       return((Image *) NULL);
00839     }
00840   /*
00841     Deconstruct the image sequence.
00842   */
00843   i=0;
00844   next=GetNextImageInList(next);
00845   for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
00846   {
00847     image_a=CloneImage(next,0,0,MagickTrue,exception);
00848     if (image_a == (Image *) NULL)
00849       break;
00850     image_b=CropImage(image_a,&bounds[i],exception);
00851     image_a=DestroyImage(image_a);
00852     if (image_b == (Image *) NULL)
00853       break;
00854     AppendImageToList(&layers,image_b);
00855     i++;
00856   }
00857   bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00858   if (next != (Image *) NULL)
00859     {
00860       layers=DestroyImageList(layers);
00861       return((Image *) NULL);
00862     }
00863   return(GetFirstImageInList(layers));
00864 }
00865 
00866 /*
00867 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00868 %                                                                             %
00869 %                                                                             %
00870 %                                                                             %
00871 %     D e c o n s t r u c t I m a g e s                                       %
00872 %                                                                             %
00873 %                                                                             %
00874 %                                                                             %
00875 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00876 %
00877 %  DeconstructImages() compares each image with the next in a sequence and
00878 %  returns the minimum bounding region of all differences from the first image.
00879 %
00880 %  This function is depreciated in favor of the more universal
00881 %  CompareImageLayers() function.
00882 %
00883 %  The format of the DeconstructImages method is:
00884 %
00885 %      Image *DeconstructImages(const Image *images, ExceptionInfo *exception)
00886 %
00887 %  A description of each parameter follows:
00888 %
00889 %    o image: the image.
00890 %
00891 %    o exception: return any errors or warnings in this structure.
00892 %
00893 */
00894 
00895 MagickExport Image *DeconstructImages(const Image *images,
00896   ExceptionInfo *exception)
00897 {
00898   return(CompareImageLayers(images,CompareAnyLayer,exception));
00899 }
00900 
00901 /*
00902 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00903 %                                                                             %
00904 %                                                                             %
00905 %                                                                             %
00906 +     O p t i m i z e L a y e r F r a m e s                                   %
00907 %                                                                             %
00908 %                                                                             %
00909 %                                                                             %
00910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00911 %
00912 %  OptimizeLayerFrames() compares each image the GIF disposed forms of the
00913 %  previous image in the sequence.  From this it attempts to select the
00914 %  smallest cropped image to replace each frame, while preserving the results
00915 %  of the animation.
00916 %
00917 %  Note that this not easy, and may require the expandsion of the bounds
00918 %  of previous frame, to clear pixels for the next animation frame,
00919 %  using GIF Background Dispose method.
00920 %
00921 %  Currently this only used internally, with external wrappers below.
00922 %
00923 %  The format of the OptimizeLayerFrames method is:
00924 %
00925 %      static Image *OptimizeLayerFrames(const Image *image,
00926 %               const ImageLayerMethod method, ExceptionInfo *exception)
00927 %
00928 %  A description of each parameter follows:
00929 %
00930 %    o image: the image.
00931 %
00932 %    o method: the layers type to optimize with. Must be one of...
00933 %             OptimizeImageLayer, or  OptimizePlusLayer
00934 %
00935 %    o exception: return any errors or warnings in this structure.
00936 %
00937 */
00938 /*
00939   Define a 'fake' dispose method where the frame is duplicated, with a
00940   extra zero time delay frame which does a BackgroundDisposal to clear the
00941   pixels that need to be cleared.
00942 */
00943 #define DupDispose  ((DisposeType)9)
00944 /*
00945   Another 'fake' dispose method used to removed frames that don't change.
00946 */
00947 #define DelDispose  ((DisposeType)8)
00948 
00949 static Image *OptimizeLayerFrames(const Image *image,
00950   const ImageLayerMethod method, ExceptionInfo *exception)
00951 {
00952   ExceptionInfo
00953     *sans_exception;
00954 
00955   Image
00956     *prev_image,
00957     *dup_image,
00958     *bgnd_image,
00959     *optimized_image;
00960 
00961   RectangleInfo
00962     try_bounds,
00963     bgnd_bounds,
00964     dup_bounds,
00965     *bounds;
00966 
00967   MagickBooleanType
00968     add_frames,
00969     try_cleared,
00970     cleared;
00971 
00972   DisposeType
00973     *disposals;
00974 
00975   register const Image
00976     *next;
00977 
00978   register long
00979     i;
00980 
00981   assert(image != (const Image *) NULL);
00982   assert(image->signature == MagickSignature);
00983   if (image->debug != MagickFalse)
00984     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00985   assert(exception != (ExceptionInfo *) NULL);
00986   assert(exception->signature == MagickSignature);
00987   assert(method == OptimizeLayer ||
00988          method == OptimizeImageLayer ||
00989          method == OptimizePlusLayer);
00990 
00991   /*
00992     Are we allowed to add/remove frames from animation
00993   */
00994   add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
00995   /*
00996     Ensure  all the images are the same size
00997   */
00998   next=GetFirstImageInList(image);
00999   for (; next != (Image *) NULL; next=GetNextImageInList(next))
01000   {
01001     if ((next->columns != image->columns) || (next->rows != image->rows))
01002       ThrowImageException(OptionError,"ImagesAreNotTheSameSize");
01003     /*
01004       FUTURE: also check they are fully coalesced (full page settings)
01005     */
01006   }
01007   /*
01008     Allocate memory (times 2 if we allow frame additions)
01009   */
01010   next=GetFirstImageInList(image);
01011   bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
01012     GetImageListLength(next),(add_frames != MagickFalse ? 2UL : 1UL)*
01013     sizeof(*bounds));
01014   if (bounds == (RectangleInfo *) NULL)
01015     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01016   disposals=(DisposeType *) AcquireQuantumMemory((size_t)
01017     GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
01018     sizeof(*disposals));
01019   if (disposals == (DisposeType *) NULL)
01020     {
01021       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01022       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01023     }
01024   /*
01025     Initialise Previous Image as fully transparent
01026   */
01027   prev_image=CloneImage(next,next->page.