2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5 % L AAA Y Y EEEEE RRRR %
9 % LLLLL A A Y EEEEE R R %
11 % MagickCore Image Layering Methods %
19 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
20 % dedicated to making software imaging solutions freely available. %
22 % You may not use this file except in compliance with the License. You may %
23 % obtain a copy of the License at %
25 % http://www.imagemagick.org/script/license.php %
27 % Unless required by applicable law or agreed to in writing, software %
28 % distributed under the License is distributed on an "AS IS" BASIS, %
29 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
30 % See the License for the specific language governing permissions and %
31 % limitations under the License. %
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 #include "magick/studio.h"
41 #include "magick/artifact.h"
42 #include "magick/cache.h"
43 #include "magick/color.h"
44 #include "magick/color-private.h"
45 #include "magick/composite.h"
46 #include "magick/effect.h"
47 #include "magick/exception.h"
48 #include "magick/exception-private.h"
49 #include "magick/geometry.h"
50 #include "magick/image.h"
51 #include "magick/layer.h"
52 #include "magick/list.h"
53 #include "magick/memory_.h"
54 #include "magick/monitor.h"
55 #include "magick/monitor-private.h"
56 #include "magick/option.h"
57 #include "magick/pixel-private.h"
58 #include "magick/property.h"
59 #include "magick/profile.h"
60 #include "magick/resource_.h"
61 #include "magick/resize.h"
62 #include "magick/statistic.h"
63 #include "magick/string_.h"
64 #include "magick/transform.h"
67 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71 + C l e a r B o u n d s %
75 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 % ClearBounds() Clear the area specified by the bounds in an image to
78 % transparency. This typically used to handle Background Disposal
79 % for the previous frame in an animation sequence.
81 % WARNING: no bounds checks are performed, except for the null or
82 % missed image, for images that don't change. in all other cases
83 % bound must fall within the image.
87 % void ClearBounds(Image *image,RectangleInfo *bounds)
89 % A description of each parameter follows:
91 % o image: the image to had the area cleared in
93 % o bounds: the area to be clear within the imag image
96 static void ClearBounds(Image *image,RectangleInfo *bounds)
106 if (image->matte == MagickFalse)
107 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
108 exception=(&image->exception);
109 for (y=0; y < (ssize_t) bounds->height; y++)
117 q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
118 if (q == (PixelPacket *) NULL)
120 for (x=0; x < (ssize_t) bounds->width; x++)
122 q->opacity=(Quantum) TransparentOpacity;
125 if (SyncAuthenticPixels(image,exception) == MagickFalse)
131 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
135 + I s B o u n d s C l e a r e d %
139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141 % IsBoundsCleared() tests whether any pixel in the bounds given, gets cleared
142 % when going from the first image to the second image. This typically used
143 % to check if a proposed disposal method will work successfully to generate
144 % the second frame image from the first disposed form of the previous frame.
148 % MagickBooleanType IsBoundsCleared(const Image *image1,
149 % const Image *image2,RectangleInfo bounds,ExceptionInfo *exception)
151 % A description of each parameter follows:
153 % o image1, image 2: the images to check for cleared pixels
155 % o bounds: the area to be clear within the imag image
157 % o exception: return any errors or warnings in this structure.
159 % WARNING: no bounds checks are performed, except for the null or
160 % missed image, for images that don't change. in all other cases
161 % bound must fall within the image.
164 static MagickBooleanType IsBoundsCleared(const Image *image1,
165 const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception)
170 register const PixelPacket
178 assert(image1->matte==MagickTrue);
179 assert(image2->matte==MagickTrue);
182 if ( bounds->x< 0 ) return(MagickFalse);
184 for (y=0; y < (ssize_t) bounds->height; y++)
186 p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,
188 q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,
190 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
192 for (x=0; x < (ssize_t) bounds->width; x++)
194 if ((GetOpacityPixelComponent(p) <= (Quantum) (QuantumRange/2)) &&
195 (q->opacity > (Quantum) (QuantumRange/2)))
200 if (x < (ssize_t) bounds->width)
203 return(y < (ssize_t) bounds->height ? MagickTrue : MagickFalse);
207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
211 % C o a l e s c e I m a g e s %
215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
217 % CoalesceImages() composites a set of images while respecting any page
218 % offsets and disposal methods. GIF, MIFF, and MNG animation sequences
219 % typically start with an image background and each subsequent image
220 % varies in size and offset. A new image sequence is returned with all
221 % images the same size as the first images virtual canvas and composited
222 % with the next image in the sequence.
224 % The format of the CoalesceImages method is:
226 % Image *CoalesceImages(Image *image,ExceptionInfo *exception)
228 % A description of each parameter follows:
230 % o image: the image sequence.
232 % o exception: return any errors or warnings in this structure.
235 MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
249 Coalesce the image sequence.
251 assert(image != (Image *) NULL);
252 assert(image->signature == MagickSignature);
253 if (image->debug != MagickFalse)
254 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
255 assert(exception != (ExceptionInfo *) NULL);
256 assert(exception->signature == MagickSignature);
258 /* initialise first image */
259 next=GetFirstImageInList(image);
261 if (bounds.width == 0)
263 bounds.width=next->columns;
265 bounds.width+=bounds.x;
267 if (bounds.height == 0)
269 bounds.height=next->rows;
271 bounds.height+=bounds.y;
275 coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue,
277 if (coalesce_image == (Image *) NULL)
278 return((Image *) NULL);
279 coalesce_image->page=bounds;
280 coalesce_image->dispose=NoneDispose;
281 coalesce_image->background_color.opacity=(Quantum) TransparentOpacity;
282 (void) SetImageBackgroundColor(coalesce_image);
284 Coalesce rest of the images.
286 dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
287 (void) CompositeImage(coalesce_image,CopyCompositeOp,next,next->page.x,
289 next=GetNextImageInList(next);
290 for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
293 Determine the bounds that was overlaid in the previous image.
295 previous=GetPreviousImageInList(next);
296 bounds=previous->page;
297 bounds.width=previous->columns;
298 bounds.height=previous->rows;
301 bounds.width+=bounds.x;
304 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) coalesce_image->columns)
305 bounds.width=coalesce_image->columns-bounds.x;
308 bounds.height+=bounds.y;
311 if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) coalesce_image->rows)
312 bounds.height=coalesce_image->rows-bounds.y;
314 Replace the dispose image with the new coalesced image.
316 if (GetPreviousImageInList(next)->dispose != PreviousDispose)
318 dispose_image=DestroyImage(dispose_image);
319 dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
320 if (dispose_image == (Image *) NULL)
322 coalesce_image=DestroyImageList(coalesce_image);
323 return((Image *) NULL);
327 Clear the overlaid area of the coalesced bounds for background disposal
329 if (next->previous->dispose == BackgroundDispose)
330 ClearBounds(dispose_image, &bounds);
332 Next image is the dispose image, overlaid with next frame in sequence.
334 coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception);
335 coalesce_image->next->previous=coalesce_image;
336 previous=coalesce_image;
337 coalesce_image=GetNextImageInList(coalesce_image);
338 coalesce_image->matte=MagickTrue;
339 (void) CompositeImage(coalesce_image,next->matte != MagickFalse ?
340 OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
341 (void) CloneImageProfiles(coalesce_image,next);
342 (void) CloneImageProperties(coalesce_image,next);
343 (void) CloneImageArtifacts(coalesce_image,next);
344 coalesce_image->page=previous->page;
346 If a pixel goes opaque to transparent, use background dispose.
348 if (IsBoundsCleared(previous,coalesce_image,&bounds,exception))
349 coalesce_image->dispose=BackgroundDispose;
351 coalesce_image->dispose=NoneDispose;
352 previous->dispose=coalesce_image->dispose;
354 dispose_image=DestroyImage(dispose_image);
355 return(GetFirstImageInList(coalesce_image));
359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
363 % D i s p o s e I m a g e s %
367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
369 % DisposeImages() returns the coalesced frames of a GIF animation as it would
370 % appear after the GIF dispose method of that frame has been applied. That
371 % is it returned the appearance of each frame before the next is overlaid.
373 % The format of the DisposeImages method is:
375 % Image *DisposeImages(Image *image,ExceptionInfo *exception)
377 % A description of each parameter follows:
379 % o image: the image sequence.
381 % o exception: return any errors or warnings in this structure.
384 MagickExport Image *DisposeImages(const Image *image,ExceptionInfo *exception)
397 Run the image through the animation sequence
399 assert(image != (Image *) NULL);
400 assert(image->signature == MagickSignature);
401 if (image->debug != MagickFalse)
402 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
403 assert(exception != (ExceptionInfo *) NULL);
404 assert(exception->signature == MagickSignature);
405 curr=GetFirstImageInList(image);
406 dispose_image=CloneImage(curr,curr->page.width,curr->page.height,MagickTrue,
408 if (dispose_image == (Image *) NULL)
409 return((Image *) NULL);
410 dispose_image->page=curr->page;
411 dispose_image->page.x=0;
412 dispose_image->page.y=0;
413 dispose_image->dispose=NoneDispose;
414 dispose_image->background_color.opacity=(Quantum) TransparentOpacity;
415 (void) SetImageBackgroundColor(dispose_image);
416 dispose_images=NewImageList();
417 for ( ; curr != (Image *) NULL; curr=GetNextImageInList(curr))
423 Overlay this frame's image over the previous disposal image.
425 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
426 if (current_image == (Image *) NULL)
428 dispose_images=DestroyImageList(dispose_images);
429 dispose_image=DestroyImage(dispose_image);
430 return((Image *) NULL);
432 (void) CompositeImage(current_image,curr->matte != MagickFalse ?
433 OverCompositeOp : CopyCompositeOp,curr,curr->page.x,curr->page.y);
436 Handle Background dispose: image is displayed for the delay period.
438 if (curr->dispose == BackgroundDispose)
441 bounds.width=curr->columns;
442 bounds.height=curr->rows;
445 bounds.width+=bounds.x;
448 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
449 bounds.width=current_image->columns-bounds.x;
452 bounds.height+=bounds.y;
455 if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
456 bounds.height=current_image->rows-bounds.y;
457 ClearBounds(current_image,&bounds);
460 Select the appropriate previous/disposed image.
462 if (curr->dispose == PreviousDispose)
463 current_image=DestroyImage(current_image);
466 dispose_image=DestroyImage(dispose_image);
467 dispose_image=current_image;
468 current_image=(Image *)NULL;
471 Save the dispose image just calculated for return.
477 dispose=CloneImage(dispose_image,0,0,MagickTrue,exception);
478 if (dispose == (Image *) NULL)
480 dispose_images=DestroyImageList(dispose_images);
481 dispose_image=DestroyImage(dispose_image);
482 return((Image *) NULL);
484 (void) CloneImageProfiles(dispose,curr);
485 (void) CloneImageProperties(dispose,curr);
486 (void) CloneImageArtifacts(dispose,curr);
489 dispose->dispose=curr->dispose;
490 AppendImageToList(&dispose_images,dispose);
493 dispose_image=DestroyImage(dispose_image);
494 return(GetFirstImageInList(dispose_images));
498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
502 + C o m p a r e P i x e l s %
506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
508 % ComparePixels() Compare the two pixels and return true if the pixels
509 % differ according to the given LayerType comparision method.
511 % This currently only used internally by CompareImageBounds(). It is
512 % doubtful that this sub-routine will be useful outside this module.
514 % The format of the ComparePixels method is:
516 % MagickBooleanType *ComparePixels(const ImageLayerMethod method,
517 % const MagickPixelPacket *p,const MagickPixelPacket *q)
519 % A description of each parameter follows:
521 % o method: What differences to look for. Must be one of
522 % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
524 % o p, q: the pixels to test for appropriate differences.
528 static MagickBooleanType ComparePixels(const ImageLayerMethod method,
529 const MagickPixelPacket *p,const MagickPixelPacket *q)
536 Any change in pixel values
538 if (method == CompareAnyLayer)
539 return((MagickBooleanType)(IsMagickColorSimilar(p,q) == MagickFalse));
541 o1 = (p->matte != MagickFalse) ? GetOpacityPixelComponent(p) : OpaqueOpacity;
542 o2 = (q->matte != MagickFalse) ? q->opacity : OpaqueOpacity;
545 Pixel goes from opaque to transprency
547 if (method == CompareClearLayer)
548 return((MagickBooleanType) ( (o1 <= ((MagickRealType) QuantumRange/2.0)) &&
549 (o2 > ((MagickRealType) QuantumRange/2.0)) ) );
552 overlay would change first pixel by second
554 if (method == CompareOverlayLayer)
556 if (o2 > ((MagickRealType) QuantumRange/2.0))
558 return((MagickBooleanType) (IsMagickColorSimilar(p,q) == MagickFalse));
565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
569 + C o m p a r e I m a g e B o u n d s %
573 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
575 % CompareImageBounds() Given two images return the smallest rectangular area
576 % by which the two images differ, accourding to the given 'Compare...'
579 % This currently only used internally in this module, but may eventually
580 % be used by other modules.
582 % The format of the CompareImageBounds method is:
584 % RectangleInfo *CompareImageBounds(const ImageLayerMethod method,
585 % const Image *image1, const Image *image2, ExceptionInfo *exception)
587 % A description of each parameter follows:
589 % o method: What differences to look for. Must be one of
590 % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
592 % o image1, image2: the two images to compare.
594 % o exception: return any errors or warnings in this structure.
598 static RectangleInfo CompareImageBounds(const Image *image1,const Image *image2,
599 const ImageLayerMethod method,ExceptionInfo *exception)
608 register const IndexPacket
612 register const PixelPacket
623 /* only same sized images can be compared */
624 assert(image1->columns == image2->columns);
625 assert(image1->rows == image2->rows);
629 Set bounding box of the differences between images
631 GetMagickPixelPacket(image1,&pixel1);
632 GetMagickPixelPacket(image2,&pixel2);
633 for (x=0; x < (ssize_t) image1->columns; x++)
635 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
636 q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
637 if ((p == (const PixelPacket *) NULL) ||
638 (q == (const PixelPacket *) NULL))
640 indexes1=GetVirtualIndexQueue(image1);
641 indexes2=GetVirtualIndexQueue(image2);
642 for (y=0; y < (ssize_t) image1->rows; y++)
644 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
645 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
646 if (ComparePixels(method,&pixel1,&pixel2))
651 if (y < (ssize_t) image1->rows)
654 if (x >= (ssize_t) image1->columns)
657 Images are identical, return a null image.
666 for (x=(ssize_t) image1->columns-1; x >= 0; x--)
668 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
669 q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
670 if ((p == (const PixelPacket *) NULL) ||
671 (q == (const PixelPacket *) NULL))
673 indexes1=GetVirtualIndexQueue(image1);
674 indexes2=GetVirtualIndexQueue(image2);
675 for (y=0; y < (ssize_t) image1->rows; y++)
677 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
678 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
679 if (ComparePixels(method,&pixel1,&pixel2))
684 if (y < (ssize_t) image1->rows)
687 bounds.width=(size_t) (x-bounds.x+1);
688 for (y=0; y < (ssize_t) image1->rows; y++)
690 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
691 q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
692 if ((p == (const PixelPacket *) NULL) ||
693 (q == (const PixelPacket *) NULL))
695 indexes1=GetVirtualIndexQueue(image1);
696 indexes2=GetVirtualIndexQueue(image2);
697 for (x=0; x < (ssize_t) image1->columns; x++)
699 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
700 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
701 if (ComparePixels(method,&pixel1,&pixel2))
706 if (x < (ssize_t) image1->columns)
710 for (y=(ssize_t) image1->rows-1; y >= 0; y--)
712 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
713 q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
714 if ((p == (const PixelPacket *) NULL) ||
715 (q == (const PixelPacket *) NULL))
717 indexes1=GetVirtualIndexQueue(image1);
718 indexes2=GetVirtualIndexQueue(image2);
719 for (x=0; x < (ssize_t) image1->columns; x++)
721 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
722 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
723 if (ComparePixels(method,&pixel1,&pixel2))
728 if (x < (ssize_t) image1->columns)
731 bounds.height=(size_t) (y-bounds.y+1);
736 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
740 % C o m p a r e I m a g e L a y e r s %
744 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
746 % CompareImageLayers() compares each image with the next in a sequence and
747 % returns the minimum bounding region of all the pixel differences (of the
748 % ImageLayerMethod specified) it discovers.
750 % Images do NOT have to be the same size, though it is best that all the
751 % images are 'coalesced' (images are all the same size, on a flattened
752 % canvas, so as to represent exactly how an specific frame should look).
754 % No GIF dispose methods are applied, so GIF animations must be coalesced
755 % before applying this image operator to find differences to them.
757 % The format of the CompareImageLayers method is:
759 % Image *CompareImageLayers(const Image *images,
760 % const ImageLayerMethod method,ExceptionInfo *exception)
762 % A description of each parameter follows:
764 % o image: the image.
766 % o method: the layers type to compare images with. Must be one of...
767 % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
769 % o exception: return any errors or warnings in this structure.
773 MagickExport Image *CompareImageLayers(const Image *image,
774 const ImageLayerMethod method, ExceptionInfo *exception)
790 assert(image != (const Image *) NULL);
791 assert(image->signature == MagickSignature);
792 if (image->debug != MagickFalse)
793 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
794 assert(exception != (ExceptionInfo *) NULL);
795 assert(exception->signature == MagickSignature);
796 assert((method == CompareAnyLayer) ||
797 (method == CompareClearLayer) ||
798 (method == CompareOverlayLayer));
800 Allocate bounds memory.
802 next=GetFirstImageInList(image);
803 bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
804 GetImageListLength(next),sizeof(*bounds));
805 if (bounds == (RectangleInfo *) NULL)
806 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
808 Set up first comparision images.
810 image_a=CloneImage(next,next->page.width,next->page.height,
811 MagickTrue,exception);
812 if (image_a == (Image *) NULL)
814 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
815 return((Image *) NULL);
817 image_a->background_color.opacity=(Quantum) TransparentOpacity;
818 (void) SetImageBackgroundColor(image_a);
819 image_a->page=next->page;
822 (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,next->page.y);
824 Compute the bounding box of changes for the later images
827 next=GetNextImageInList(next);
828 for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
830 image_b=CloneImage(image_a,0,0,MagickTrue,exception);
831 if (image_b == (Image *) NULL)
833 image_a=DestroyImage(image_a);
834 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
835 return((Image *) NULL);
837 (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,
839 bounds[i]=CompareImageBounds(image_b,image_a,method,exception);
841 image_b=DestroyImage(image_b);
844 image_a=DestroyImage(image_a);
846 Clone first image in sequence.
848 next=GetFirstImageInList(image);
849 layers=CloneImage(next,0,0,MagickTrue,exception);
850 if (layers == (Image *) NULL)
852 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
853 return((Image *) NULL);
856 Deconstruct the image sequence.
859 next=GetNextImageInList(next);
860 for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
862 image_a=CloneImage(next,0,0,MagickTrue,exception);
863 if (image_a == (Image *) NULL)
865 image_b=CropImage(image_a,&bounds[i],exception);
866 image_a=DestroyImage(image_a);
867 if (image_b == (Image *) NULL)
869 AppendImageToList(&layers,image_b);
872 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
873 if (next != (Image *) NULL)
875 layers=DestroyImageList(layers);
876 return((Image *) NULL);
878 return(GetFirstImageInList(layers));
882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
886 % D e c o n s t r u c t I m a g e s %
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
892 % DeconstructImages() compares each image with the next in a sequence and
893 % returns the minimum bounding region of all differences from the first image.
895 % This function is deprecated in favor of the more universal
896 % CompareImageLayers() function.
898 % The format of the DeconstructImages method is:
900 % Image *DeconstructImages(const Image *images, ExceptionInfo *exception)
902 % A description of each parameter follows:
904 % o image: the image.
906 % o exception: return any errors or warnings in this structure.
910 MagickExport Image *DeconstructImages(const Image *images,
911 ExceptionInfo *exception)
913 return(CompareImageLayers(images,CompareAnyLayer,exception));
917 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
921 + O p t i m i z e L a y e r F r a m e s %
925 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
927 % OptimizeLayerFrames() takes a coalesced GIF animation, and compares each
928 % frame against the three different 'disposal' forms of the previous frame.
929 % From this it then attempts to select the smallest cropped image and
930 % disposal method needed to reproduce the resulting image.
932 % Note that this not easy, and may require the expandsion of the bounds
933 % of previous frame, simply clear pixels for the next animation frame to
934 % transparency according to the selected dispose method.
936 % The format of the OptimizeLayerFrames method is:
938 % static Image *OptimizeLayerFrames(const Image *image,
939 % const ImageLayerMethod method, ExceptionInfo *exception)
941 % A description of each parameter follows:
943 % o image: the image.
945 % o method: the layers technique to optimize with. Must be one of...
946 % OptimizeImageLayer, or OptimizePlusLayer. The Plus form allows
947 % the addition of extra 'zero delay' frames to clear pixels from
948 % the previous frame, and the removal of frames that done change,
949 % merging the delay times together.
951 % o exception: return any errors or warnings in this structure.
955 Define a 'fake' dispose method where the frame is duplicated, (for
956 OptimizePlusLayer) with a extra zero time delay frame which does a
957 BackgroundDisposal to clear the pixels that need to be cleared.
959 #define DupDispose ((DisposeType)9)
961 Another 'fake' dispose method used to removed frames that don't change.
963 #define DelDispose ((DisposeType)8)
965 #define DEBUG_OPT_FRAME 0
967 static Image *OptimizeLayerFrames(const Image *image,
968 const ImageLayerMethod method, ExceptionInfo *exception)
999 assert(image != (const Image *) NULL);
1000 assert(image->signature == MagickSignature);
1001 if (image->debug != MagickFalse)
1002 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1003 assert(exception != (ExceptionInfo *) NULL);
1004 assert(exception->signature == MagickSignature);
1005 assert(method == OptimizeLayer ||
1006 method == OptimizeImageLayer ||
1007 method == OptimizePlusLayer);
1010 Are we allowed to add/remove frames from animation
1012 add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
1014 Ensure all the images are the same size
1016 curr=GetFirstImageInList(image);
1017 for (; curr != (Image *) NULL; curr=GetNextImageInList(curr))
1019 if ((curr->columns != image->columns) || (curr->rows != image->rows))
1020 ThrowImageException(OptionError,"ImagesAreNotTheSameSize");
1022 FUTURE: also check that image is also fully coalesced (full page)
1023 Though as long as they are the same size it should not matter.
1027 Allocate memory (times 2 if we allow the use of frame duplications)
1029 curr=GetFirstImageInList(image);
1030 bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
1031 GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)*
1033 if (bounds == (RectangleInfo *) NULL)
1034 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1035 disposals=(DisposeType *) AcquireQuantumMemory((size_t)
1036 GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
1037 sizeof(*disposals));
1038 if (disposals == (DisposeType *) NULL)
1040 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1041 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1044 Initialise Previous Image as fully transparent
1046 prev_image=CloneImage(curr,curr->page.width,curr->page.height,
1047 MagickTrue,exception);
1048 if (prev_image == (Image *) NULL)
1050 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1051 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1052 return((Image *) NULL);
1054 prev_image->page=curr->page; /* ERROR: <-- should not be need, but is! */
1055 prev_image->page.x=0;
1056 prev_image->page.y=0;
1057 prev_image->dispose=NoneDispose;
1059 prev_image->background_color.opacity=(Quantum) TransparentOpacity;
1060 (void) SetImageBackgroundColor(prev_image);
1062 Figure out the area of overlay of the first frame
1063 No pixel could be cleared as all pixels are already cleared.
1067 fprintf(stderr, "frame %.20g :-\n", (double) i);
1069 disposals[0]=NoneDispose;
1070 bounds[0]=CompareImageBounds(prev_image,curr,CompareAnyLayer,exception);
1072 fprintf(stderr, "overlay: %.20gx%.20g%+.20g%+.20g\n\n",
1073 (double) bounds[i].width,(double) bounds[i].height,
1074 (double) bounds[i].x,(double) bounds[i].y );
1077 Compute the bounding box of changes for each pair of images.
1080 bgnd_image=(Image *)NULL;
1081 dup_image=(Image *)NULL;
1083 dup_bounds.height=0;
1086 curr=GetNextImageInList(curr);
1087 for ( ; curr != (const Image *) NULL; curr=GetNextImageInList(curr))
1090 fprintf(stderr, "frame %.20g :-\n", (double) i);
1093 Assume none disposal is the best
1095 bounds[i]=CompareImageBounds(curr->previous,curr,CompareAnyLayer,exception);
1096 cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception);
1097 disposals[i-1]=NoneDispose;
1099 fprintf(stderr, "overlay: %.20gx%.20g%+.20g%+.20g%s%s\n",
1100 (double) bounds[i].width,(double) bounds[i].height,
1101 (double) bounds[i].x,(double) bounds[i].y,
1102 bounds[i].x < 0?" (unchanged)":"",
1103 cleared?" (pixels cleared)":"");
1105 if ( bounds[i].x < 0 ) {
1107 Image frame is exactly the same as the previous frame!
1108 If not adding frames leave it to be cropped down to a null image.
1109 Otherwise mark previous image for deleted, transfering its crop bounds
1110 to the current image.
1112 if ( add_frames && i>=2 ) {
1113 disposals[i-1]=DelDispose;
1114 disposals[i]=NoneDispose;
1115 bounds[i]=bounds[i-1];
1123 Compare a none disposal against a previous disposal
1125 try_bounds=CompareImageBounds(prev_image,curr,CompareAnyLayer,exception);
1126 try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception);
1128 fprintf(stderr, "test_prev: %.20gx%.20g%+.20g%+.20g%s\n",
1129 (double) try_bounds.width,(double) try_bounds.height,
1130 (double) try_bounds.x,(double) try_bounds.y,
1131 try_cleared?" (pixels were cleared)":"");
1133 if ( (!try_cleared && cleared ) ||
1134 try_bounds.width * try_bounds.height
1135 < bounds[i].width * bounds[i].height )
1137 cleared=try_cleared;
1138 bounds[i]=try_bounds;
1139 disposals[i-1]=PreviousDispose;
1141 fprintf(stderr, "previous: accepted\n");
1143 fprintf(stderr, "previous: rejected\n");
1148 If we are allowed lets try a complex frame duplication.
1149 It is useless if the previous image already clears pixels correctly.
1150 This method will always clear all the pixels that need to be cleared.
1152 dup_bounds.width=dup_bounds.height=0; /* no dup, no pixel added */
1155 dup_image=CloneImage(curr->previous,curr->previous->page.width,
1156 curr->previous->page.height,MagickTrue,exception);
1157 if (dup_image == (Image *) NULL)
1159 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1160 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1161 prev_image=DestroyImage(prev_image);
1162 return((Image *) NULL);
1164 dup_bounds=CompareImageBounds(dup_image,curr,CompareClearLayer,exception);
1165 ClearBounds(dup_image,&dup_bounds);
1166 try_bounds=CompareImageBounds(dup_image,curr,CompareAnyLayer,exception);
1168 dup_bounds.width*dup_bounds.height
1169 +try_bounds.width*try_bounds.height
1170 < bounds[i].width * bounds[i].height )
1172 cleared=MagickFalse;
1173 bounds[i]=try_bounds;
1174 disposals[i-1]=DupDispose;
1175 /* to be finalised later, if found to be optimial */
1178 dup_bounds.width=dup_bounds.height=0;
1181 Now compare against a simple background disposal
1183 bgnd_image=CloneImage(curr->previous,curr->previous->page.width,
1184 curr->previous->page.height,MagickTrue,exception);
1185 if (bgnd_image == (Image *) NULL)
1187 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1188 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1189 prev_image=DestroyImage(prev_image);
1190 if ( dup_image != (Image *) NULL)
1191 dup_image=DestroyImage(dup_image);
1192 return((Image *) NULL);
1194 bgnd_bounds=bounds[i-1]; /* interum bounds of the previous image */
1195 ClearBounds(bgnd_image,&bgnd_bounds);
1196 try_bounds=CompareImageBounds(bgnd_image,curr,CompareAnyLayer,exception);
1197 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1199 fprintf(stderr, "background: %s\n",
1200 try_cleared?"(pixels cleared)":"");
1205 Straight background disposal failed to clear pixels needed!
1206 Lets try expanding the disposal area of the previous frame, to
1207 include the pixels that are cleared. This guaranteed
1208 to work, though may not be the most optimized solution.
1210 try_bounds=CompareImageBounds(curr->previous,curr,CompareClearLayer,exception);
1212 fprintf(stderr, "expand_clear: %.20gx%.20g%+.20g%+.20g%s\n",
1213 (double) try_bounds.width,(double) try_bounds.height,
1214 (double) try_bounds.x,(double) try_bounds.y,
1215 try_bounds.x<0?" (no expand nessary)":"");
1217 if ( bgnd_bounds.x < 0 )
1218 bgnd_bounds = try_bounds;
1222 fprintf(stderr, "expand_bgnd: %.20gx%.20g%+.20g%+.20g\n",
1223 (double) bgnd_bounds.width,(double) bgnd_bounds.height,
1224 (double) bgnd_bounds.x,(double) bgnd_bounds.y );
1226 if ( try_bounds.x < bgnd_bounds.x )
1228 bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x;
1229 if ( bgnd_bounds.width < try_bounds.width )
1230 bgnd_bounds.width = try_bounds.width;
1231 bgnd_bounds.x = try_bounds.x;
1235 try_bounds.width += try_bounds.x - bgnd_bounds.x;
1236 if ( bgnd_bounds.width < try_bounds.width )
1237 bgnd_bounds.width = try_bounds.width;
1239 if ( try_bounds.y < bgnd_bounds.y )
1241 bgnd_bounds.height += bgnd_bounds.y - try_bounds.y;
1242 if ( bgnd_bounds.height < try_bounds.height )
1243 bgnd_bounds.height = try_bounds.height;
1244 bgnd_bounds.y = try_bounds.y;
1248 try_bounds.height += try_bounds.y - bgnd_bounds.y;
1249 if ( bgnd_bounds.height < try_bounds.height )
1250 bgnd_bounds.height = try_bounds.height;
1253 fprintf(stderr, " to : %.20gx%.20g%+.20g%+.20g\n",
1254 (double) bgnd_bounds.width,(double) bgnd_bounds.height,
1255 (double) bgnd_bounds.x,(double) bgnd_bounds.y );
1258 ClearBounds(bgnd_image,&bgnd_bounds);
1260 /* Something strange is happening with a specific animation
1261 * CompareAnyLayers (normal method) and CompareClearLayers returns the whole
1262 * image, which is not posibly correct! As verified by previous tests.
1263 * Something changed beyond the bgnd_bounds clearing. But without being able
1264 * to see, or writet he image at this point it is hard to tell what is wrong!
1265 * Only CompareOverlay seemed to return something sensible.
1267 try_bounds=CompareImageBounds(bgnd_image,curr,CompareClearLayer,exception);
1268 fprintf(stderr, "expand_ctst: %.20gx%.20g%+.20g%+.20g\n",
1269 (double) try_bounds.width,(double) try_bounds.height,
1270 (double) try_bounds.x,(double) try_bounds.y );
1271 try_bounds=CompareImageBounds(bgnd_image,curr,CompareAnyLayer,exception);
1272 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1273 fprintf(stderr, "expand_any : %.20gx%.20g%+.20g%+.20g%s\n",
1274 (double) try_bounds.width,(double) try_bounds.height,
1275 (double) try_bounds.x,(double) try_bounds.y,
1276 try_cleared?" (pixels cleared)":"");
1278 try_bounds=CompareImageBounds(bgnd_image,curr,CompareOverlayLayer,exception);
1280 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1281 fprintf(stderr, "expand_test: %.20gx%.20g%+.20g%+.20g%s\n",
1282 (double) try_bounds.width,(double) try_bounds.height,
1283 (double) try_bounds.x,(double) try_bounds.y,
1284 try_cleared?" (pixels cleared)":"");
1288 Test if this background dispose is smaller than any of the
1289 other methods we tryed before this (including duplicated frame)
1292 bgnd_bounds.width*bgnd_bounds.height
1293 +try_bounds.width*try_bounds.height
1294 < bounds[i-1].width*bounds[i-1].height
1295 +dup_bounds.width*dup_bounds.height
1296 +bounds[i].width*bounds[i].height )
1298 cleared=MagickFalse;
1299 bounds[i-1]=bgnd_bounds;
1300 bounds[i]=try_bounds;
1301 if ( disposals[i-1] == DupDispose )
1302 dup_image=DestroyImage(dup_image);
1303 disposals[i-1]=BackgroundDispose;
1305 fprintf(stderr, "expand_bgnd: accepted\n");
1307 fprintf(stderr, "expand_bgnd: reject\n");
1312 Finalise choice of dispose, set new prev_image,
1313 and junk any extra images as appropriate,
1315 if ( disposals[i-1] == DupDispose )
1317 if (bgnd_image != (Image *) NULL)
1318 bgnd_image=DestroyImage(bgnd_image);
1319 prev_image=DestroyImage(prev_image);
1320 prev_image=dup_image, dup_image=(Image *) NULL;
1321 bounds[i+1]=bounds[i];
1322 bounds[i]=dup_bounds;
1323 disposals[i-1]=DupDispose;
1324 disposals[i]=BackgroundDispose;
1329 if ( dup_image != (Image *) NULL)
1330 dup_image=DestroyImage(dup_image);
1331 if ( disposals[i-1] != PreviousDispose )
1332 prev_image=DestroyImage(prev_image);
1333 if ( disposals[i-1] == BackgroundDispose )
1334 prev_image=bgnd_image, bgnd_image=(Image *)NULL;
1335 if (bgnd_image != (Image *) NULL)
1336 bgnd_image=DestroyImage(bgnd_image);
1337 if ( disposals[i-1] == NoneDispose )
1339 prev_image=CloneImage(curr->previous,curr->previous->page.width,
1340 curr->previous->page.height,MagickTrue,exception);
1341 if (prev_image == (Image *) NULL)
1343 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1344 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1345 return((Image *) NULL);
1350 assert(prev_image != (Image *) NULL);
1351 disposals[i]=disposals[i-1];
1353 fprintf(stderr, "final %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1355 CommandOptionToMnemonic(MagickDisposeOptions, disposals[i-1]),
1356 (double) bounds[i-1].width, (double) bounds[i-1].height,
1357 (double) bounds[i-1].x, (double) bounds[i-1].y );
1360 fprintf(stderr, "interum %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1362 CommandOptionToMnemonic(MagickDisposeOptions, disposals[i]),
1363 (double) bounds[i].width, (double) bounds[i].height,
1364 (double) bounds[i].x, (double) bounds[i].y );
1365 fprintf(stderr, "\n");
1369 prev_image=DestroyImage(prev_image);
1371 Optimize all images in sequence.
1373 sans_exception=AcquireExceptionInfo();
1375 curr=GetFirstImageInList(image);
1376 optimized_image=NewImageList();
1377 while ( curr != (const Image *) NULL )
1379 prev_image=CloneImage(curr,0,0,MagickTrue,exception);
1380 if (prev_image == (Image *) NULL)
1382 if ( disposals[i] == DelDispose ) {
1384 while ( disposals[i] == DelDispose ) {
1385 time += curr->delay*1000/curr->ticks_per_second;
1386 curr=GetNextImageInList(curr);
1389 time += curr->delay*1000/curr->ticks_per_second;
1390 prev_image->ticks_per_second = 100L;
1391 prev_image->delay = time*prev_image->ticks_per_second/1000;
1393 bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
1394 prev_image=DestroyImage(prev_image);
1395 if (bgnd_image == (Image *) NULL)
1397 bgnd_image->dispose=disposals[i];
1398 if ( disposals[i] == DupDispose ) {
1399 bgnd_image->delay=0;
1400 bgnd_image->dispose=NoneDispose;
1403 curr=GetNextImageInList(curr);
1404 AppendImageToList(&optimized_image,bgnd_image);
1407 sans_exception=DestroyExceptionInfo(sans_exception);
1408 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1409 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1410 if (curr != (Image *) NULL)
1412 optimized_image=DestroyImageList(optimized_image);
1413 return((Image *) NULL);
1415 return(GetFirstImageInList(optimized_image));
1419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1423 % O p t i m i z e I m a g e L a y e r s %
1427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429 % OptimizeImageLayers() compares each image the GIF disposed forms of the
1430 % previous image in the sequence. From this it attempts to select the
1431 % smallest cropped image to replace each frame, while preserving the results
1432 % of the GIF animation.
1434 % The format of the OptimizeImageLayers method is:
1436 % Image *OptimizeImageLayers(const Image *image,
1437 % ExceptionInfo *exception)
1439 % A description of each parameter follows:
1441 % o image: the image.
1443 % o exception: return any errors or warnings in this structure.
1446 MagickExport Image *OptimizeImageLayers(const Image *image,
1447 ExceptionInfo *exception)
1449 return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
1453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1457 % O p t i m i z e P l u s I m a g e L a y e r s %
1461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1463 % OptimizeImagePlusLayers() is exactly as OptimizeImageLayers(), but may
1464 % also add or even remove extra frames in the animation, if it improves
1465 % the total number of pixels in the resulting GIF animation.
1467 % The format of the OptimizePlusImageLayers method is:
1469 % Image *OptimizePlusImageLayers(const Image *image,
1470 % ExceptionInfo *exception)
1472 % A description of each parameter follows:
1474 % o image: the image.
1476 % o exception: return any errors or warnings in this structure.
1479 MagickExport Image *OptimizePlusImageLayers(const Image *image,
1480 ExceptionInfo *exception)
1482 return OptimizeLayerFrames(image, OptimizePlusLayer, exception);
1486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1490 % 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 %
1494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1496 % OptimizeImageTransparency() takes a frame optimized GIF animation, and
1497 % compares the overlayed pixels against the disposal image resulting from all
1498 % the previous frames in the animation. Any pixel that does not change the
1499 % disposal image (and thus does not effect the outcome of an overlay) is made
1502 % WARNING: This modifies the current images directly, rather than generate
1503 % a new image sequence.
1505 % The format of the OptimizeImageTransperency method is:
1507 % void OptimizeImageTransperency(Image *image,ExceptionInfo *exception)
1509 % A description of each parameter follows:
1511 % o image: the image sequence
1513 % o exception: return any errors or warnings in this structure.
1516 MagickExport void OptimizeImageTransparency(const Image *image,
1517 ExceptionInfo *exception)
1526 Run the image through the animation sequence
1528 assert(image != (Image *) NULL);
1529 assert(image->signature == MagickSignature);
1530 if (image->debug != MagickFalse)
1531 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1532 assert(exception != (ExceptionInfo *) NULL);
1533 assert(exception->signature == MagickSignature);
1534 next=GetFirstImageInList(image);
1535 dispose_image=CloneImage(next,next->page.width,next->page.height,
1536 MagickTrue,exception);
1537 if (dispose_image == (Image *) NULL)
1539 dispose_image->page=next->page;
1540 dispose_image->page.x=0;
1541 dispose_image->page.y=0;
1542 dispose_image->dispose=NoneDispose;
1543 dispose_image->background_color.opacity=(Quantum) TransparentOpacity;
1544 (void) SetImageBackgroundColor(dispose_image);
1546 while ( next != (Image *) NULL )
1552 Overlay this frame's image over the previous disposal image
1554 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
1555 if (current_image == (Image *) NULL)
1557 dispose_image=DestroyImage(dispose_image);
1560 (void) CompositeImage(current_image,next->matte != MagickFalse ?
1561 OverCompositeOp : CopyCompositeOp, next,next->page.x,next->page.y);
1563 At this point the image would be displayed, for the delay period
1565 Work out the disposal of the previous image
1567 if (next->dispose == BackgroundDispose)
1572 bounds.width=next->columns;
1573 bounds.height=next->rows;
1576 bounds.width+=bounds.x;
1579 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
1580 bounds.width=current_image->columns-bounds.x;
1583 bounds.height+=bounds.y;
1586 if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
1587 bounds.height=current_image->rows-bounds.y;
1588 ClearBounds(current_image, &bounds);
1590 if (next->dispose != PreviousDispose)
1592 dispose_image=DestroyImage(dispose_image);
1593 dispose_image=current_image;
1596 current_image=DestroyImage(current_image);
1599 Optimize Transparency of the next frame (if present)
1601 next=GetNextImageInList(next);
1602 if ( next != (Image *) NULL ) {
1603 (void) CompositeImage(next, ChangeMaskCompositeOp,
1604 dispose_image, -(next->page.x), -(next->page.y) );
1607 dispose_image=DestroyImage(dispose_image);
1612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616 % R e m o v e D u p l i c a t e L a y e r s %
1620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622 % RemoveDuplicateLayers() removes any image that is exactly the same as the
1623 % next image in the given image list. Image size and virtual canvas offset
1624 % must also match, though not the virtual canvas size itself.
1626 % No check is made with regards to image disposal setting, though it is the
1627 % dispose setting of later image that is kept. Also any time delays are also
1628 % added together. As such coalesced image animations should still produce the
1629 % same result, though with duplicte frames merged into a single frame.
1631 % The format of the RemoveDuplicateLayers method is:
1633 % void RemoveDuplicateLayers(Image **image, ExceptionInfo *exception)
1635 % A description of each parameter follows:
1637 % o images: the image list
1639 % o exception: return any errors or warnings in this structure.
1642 MagickExport void RemoveDuplicateLayers(Image **images,
1643 ExceptionInfo *exception)
1652 assert((*images) != (const Image *) NULL);
1653 assert((*images)->signature == MagickSignature);
1654 if ((*images)->debug != MagickFalse)
1655 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
1656 assert(exception != (ExceptionInfo *) NULL);
1657 assert(exception->signature == MagickSignature);
1659 curr=GetFirstImageInList(*images);
1660 for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next)
1662 if ( curr->columns != next->columns || curr->rows != next->rows
1663 || curr->page.x != next->page.x || curr->page.y != next->page.y )
1665 bounds=CompareImageBounds(curr,next,CompareAnyLayer,exception);
1666 if ( bounds.x < 0 ) {
1668 the two images are the same, merge time delays and delete one.
1671 time = curr->delay*1000/curr->ticks_per_second;
1672 time += next->delay*1000/next->ticks_per_second;
1673 next->ticks_per_second = 100L;
1674 next->delay = time*curr->ticks_per_second/1000;
1675 next->iterations = curr->iterations;
1677 (void) DeleteImageFromList(images);
1680 *images = GetFirstImageInList(*images);
1684 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1688 % R e m o v e Z e r o D e l a y L a y e r s %
1692 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1694 % RemoveZeroDelayLayers() removes any image that as a zero delay time. Such
1695 % images generally represent intermediate or partial updates in GIF
1696 % animations used for file optimization. They are not ment to be displayed
1697 % to users of the animation. Viewable images in an animation should have a
1698 % time delay of 3 or more centi-seconds (hundredths of a second).
1700 % However if all the frames have a zero time delay, then either the animation
1701 % is as yet incomplete, or it is not a GIF animation. This a non-sensible
1702 % situation, so no image will be removed and a 'Zero Time Animation' warning
1703 % (exception) given.
1705 % No warning will be given if no image was removed because all images had an
1706 % appropriate non-zero time delay set.
1708 % Due to the special requirements of GIF disposal handling, GIF animations
1709 % should be coalesced first, before calling this function, though that is not
1712 % The format of the RemoveZeroDelayLayers method is:
1714 % void RemoveZeroDelayLayers(Image **image, ExceptionInfo *exception)
1716 % A description of each parameter follows:
1718 % o images: the image list
1720 % o exception: return any errors or warnings in this structure.
1723 MagickExport void RemoveZeroDelayLayers(Image **images,
1724 ExceptionInfo *exception)
1729 assert((*images) != (const Image *) NULL);
1730 assert((*images)->signature == MagickSignature);
1731 if ((*images)->debug != MagickFalse)
1732 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
1733 assert(exception != (ExceptionInfo *) NULL);
1734 assert(exception->signature == MagickSignature);
1736 i=GetFirstImageInList(*images);
1737 for ( ; i != (Image *) NULL; i=GetNextImageInList(i))
1738 if ( i->delay != 0L ) break;
1739 if ( i == (Image *) NULL ) {
1740 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1741 "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename);
1744 i=GetFirstImageInList(*images);
1745 while ( i != (Image *) NULL )
1747 if ( i->delay == 0L ) {
1748 (void) DeleteImageFromList(&i);
1752 i=GetNextImageInList(i);
1754 *images=GetFirstImageInList(*images);
1758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1762 % C o m p o s i t e L a y e r s %
1766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1768 % CompositeLayers() compose first image sequence (source) over the second
1769 % image sequence (destination), using the given compose method and offsets.
1771 % The pointers to the image list does not have to be the start of that image
1772 % list, but may start somewhere in the middle. Each layer from the two image
1773 % lists are composted together until the end of one of the image lists is
1774 % reached. The offset of each composition is also adjusted to match the
1775 % virtual canvas offsets of each layer. As such the given offset is relative
1776 % to the virtual canvas, and not the actual image.
1778 % No GIF disposal handling is performed, so GIF animations should be
1779 % coalesced before use. However this not a requirement, and individual
1780 % layer images may have any size or offset, for special compositions.
1782 % Special case:- If one of the image sequences is just a single image that
1783 % image is repeatally composed with all the images in the other image list.
1784 % Either the source or destination lists may be the single image, for this
1787 % The destination list will be expanded as needed to match number of source
1788 % image overlaid (from current position to end of list).
1790 % The format of the CompositeLayers method is:
1792 % void CompositeLayers(Image *destination,
1793 % const CompositeOperator compose, Image *source,
1794 % const ssize_t x_offset, const ssize_t y_offset,
1795 % ExceptionInfo *exception);
1797 % A description of each parameter follows:
1799 % o destination: the destination images and results
1801 % o source: source image(s) for the layer composition
1803 % o compose, x_offset, y_offset: arguments passed on to CompositeImages()
1805 % o exception: return any errors or warnings in this structure.
1808 static inline void CompositeCanvas(Image *destination,
1809 const CompositeOperator compose, Image *source,ssize_t x_offset,
1812 x_offset+=source->page.x-destination->page.x;
1813 y_offset+=source->page.y-destination->page.y;
1814 (void) CompositeImage(destination,compose,source,x_offset,y_offset);
1817 MagickExport void CompositeLayers(Image *destination,
1818 const CompositeOperator compose, Image *source,const ssize_t x_offset,
1819 const ssize_t y_offset,ExceptionInfo *exception)
1821 assert(destination != (Image *) NULL);
1822 assert(destination->signature == MagickSignature);
1823 assert(source != (Image *) NULL);
1824 assert(source->signature == MagickSignature);
1825 assert(exception != (ExceptionInfo *) NULL);
1826 assert(exception->signature == MagickSignature);
1827 if (source->debug != MagickFalse || destination->debug != MagickFalse)
1828 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
1829 source->filename, destination->filename);
1832 Overlay single source image over destation image/list
1834 if ( source->previous == (Image *) NULL && source->next == (Image *) NULL )
1835 while ( destination != (Image *) NULL )
1837 CompositeCanvas(destination, compose, source, x_offset, y_offset);
1838 destination=GetNextImageInList(destination);
1842 Overlay source image list over single destination
1843 Generating multiple clones of destination image to match source list.
1844 Original Destination image becomes first image of generated list.
1845 As such the image list pointer does not require any change in caller.
1846 Some animation attributes however also needs coping in this case.
1848 else if ( destination->previous == (Image *) NULL &&
1849 destination->next == (Image *) NULL )
1851 Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
1853 CompositeCanvas(destination, compose, source, x_offset, y_offset);
1854 /* copy source image attributes ? */
1855 if ( source->next != (Image *) NULL )
1857 destination->delay = source->delay;
1858 destination->iterations = source->iterations;
1860 source=GetNextImageInList(source);
1862 while ( source != (Image *) NULL )
1864 AppendImageToList(&destination,
1865 CloneImage(dest,0,0,MagickTrue,exception));
1866 destination=GetLastImageInList(destination);
1868 CompositeCanvas(destination, compose, source, x_offset, y_offset);
1869 destination->delay = source->delay;
1870 destination->iterations = source->iterations;
1871 source=GetNextImageInList(source);
1873 dest=DestroyImage(dest);
1877 Overlay a source image list over a destination image list
1878 until either list runs out of images. (Does not repeat)
1881 while ( source != (Image *) NULL && destination != (Image *) NULL )
1883 CompositeCanvas(destination, compose, source, x_offset, y_offset);
1884 source=GetNextImageInList(source);
1885 destination=GetNextImageInList(destination);
1890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1894 % M e r g e I m a g e L a y e r s %
1898 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1900 % MergeImageLayers() composes all the image layers from the current given
1901 % image onward to produce a single image of the merged layers.
1903 % The inital canvas's size depends on the given ImageLayerMethod, and is
1904 % initialized using the first images background color. The images
1905 % are then compositied onto that image in sequence using the given
1906 % composition that has been assigned to each individual image.
1908 % The format of the MergeImageLayers is:
1910 % Image *MergeImageLayers(const Image *image,
1911 % const ImageLayerMethod method, ExceptionInfo *exception)
1913 % A description of each parameter follows:
1915 % o image: the image list to be composited together
1917 % o method: the method of selecting the size of the initial canvas.
1919 % MergeLayer: Merge all layers onto a canvas just large enough
1920 % to hold all the actual images. The virtual canvas of the
1921 % first image is preserved but otherwise ignored.
1923 % FlattenLayer: Use the virtual canvas size of first image.
1924 % Images which fall outside this canvas is clipped.
1925 % This can be used to 'fill out' a given virtual canvas.
1927 % MosaicLayer: Start with the virtual canvas of the first image,
1928 % enlarging left and right edges to contain all images.
1929 % Images with negative offsets will be clipped.
1931 % TrimBoundsLayer: Determine the overall bounds of all the image
1932 % layers just as in "MergeLayer", then adjust the the canvas
1933 % and offsets to be relative to those bounds, without overlaying
1936 % WARNING: a new image is not returned, the original image
1937 % sequence page data is modified instead.
1939 % o exception: return any errors or warnings in this structure.
1942 MagickExport Image *MergeImageLayers(Image *image,
1943 const ImageLayerMethod method,ExceptionInfo *exception)
1945 #define MergeLayersTag "Merge/Layers"
1956 register const Image
1967 assert(image != (Image *) NULL);
1968 assert(image->signature == MagickSignature);
1969 if (image->debug != MagickFalse)
1970 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1971 assert(exception != (ExceptionInfo *) NULL);
1972 assert(exception->signature == MagickSignature);
1974 Determine canvas image size, and its virtual canvas size and offset
1977 width=image->columns;
1981 case TrimBoundsLayer:
1985 next = GetNextImageInList(image);
1986 for ( ; next != (Image *) NULL; next=GetNextImageInList(next)) {
1987 if ( page.x > next->page.x ) {
1988 width += page.x-next->page.x;
1989 page.x = next->page.x;
1991 if ( page.y > next->page.y ) {
1992 height += page.y-next->page.y;
1993 page.y = next->page.y;
1995 if ( (ssize_t) width < (next->page.x + (ssize_t)next->columns - page.x) )
1996 width = (size_t) next->page.x + (ssize_t)next->columns - page.x;
1997 if ( (ssize_t) height < (next->page.y + (ssize_t)next->rows - page.y) )
1998 height = (size_t) next->page.y + (ssize_t)next->rows - page.y;
2004 if ( page.width > 0 )
2006 if ( page.height > 0 )
2014 if ( page.width > 0 )
2016 if ( page.height > 0 )
2018 for (next=image; next != (Image *) NULL; next=GetNextImageInList(next)) {
2019 if (method == MosaicLayer) {
2020 page.x=next->page.x;
2021 page.y=next->page.y;
2022 if ( (ssize_t) width < (next->page.x + (ssize_t)next->columns) )
2023 width = (size_t) next->page.x + next->columns;
2024 if ( (ssize_t) height < (next->page.y + (ssize_t)next->rows) )
2025 height = (size_t) next->page.y + next->rows;
2035 /* set virtual canvas size if not defined */
2036 if ( page.width == 0 )
2037 page.width = (page.x < 0) ? width : width+page.x;
2038 if ( page.height == 0 )
2039 page.height = (page.y < 0) ? height : height+page.y;
2042 Handle "TrimBoundsLayer" method separately to normal 'layer merge'
2044 if ( method == TrimBoundsLayer ) {
2045 number_images=GetImageListLength(image);
2046 for (scene=0; scene < (ssize_t) number_images; scene++)
2048 image->page.x -= page.x;
2049 image->page.y -= page.y;
2050 image->page.width = width;
2051 image->page.height = height;
2052 proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2054 if (proceed == MagickFalse)
2056 image=GetNextImageInList(image);
2058 return((Image *) NULL);
2062 Create canvas size of width and height, and background color.
2064 canvas=CloneImage(image,width,height,MagickTrue,exception);
2065 if (canvas == (Image *) NULL)
2066 return((Image *) NULL);
2067 (void) SetImageBackgroundColor(canvas);
2069 canvas->dispose=UndefinedDispose;
2072 Compose images onto canvas, with progress monitor
2074 number_images=GetImageListLength(image);
2075 for (scene=0; scene < (ssize_t) number_images; scene++)
2077 (void) CompositeImage(canvas,image->compose,image,image->page.x-
2078 canvas->page.x,image->page.y-canvas->page.y);
2079 proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2081 if (proceed == MagickFalse)
2083 image=GetNextImageInList(image);