2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5 % L AAA Y Y EEEEE RRRR %
9 % LLLLL A A Y EEEEE R R %
11 % MagickCore Image Layering Methods %
19 % Copyright 1999-2012 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 "MagickCore/studio.h"
41 #include "MagickCore/artifact.h"
42 #include "MagickCore/cache.h"
43 #include "MagickCore/color.h"
44 #include "MagickCore/color-private.h"
45 #include "MagickCore/composite.h"
46 #include "MagickCore/effect.h"
47 #include "MagickCore/exception.h"
48 #include "MagickCore/exception-private.h"
49 #include "MagickCore/geometry.h"
50 #include "MagickCore/image.h"
51 #include "MagickCore/layer.h"
52 #include "MagickCore/list.h"
53 #include "MagickCore/memory_.h"
54 #include "MagickCore/monitor.h"
55 #include "MagickCore/monitor-private.h"
56 #include "MagickCore/option.h"
57 #include "MagickCore/pixel-accessor.h"
58 #include "MagickCore/property.h"
59 #include "MagickCore/profile.h"
60 #include "MagickCore/resource_.h"
61 #include "MagickCore/resize.h"
62 #include "MagickCore/statistic.h"
63 #include "MagickCore/string_.h"
64 #include "MagickCore/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
88 % ExceptionInfo *exception)
90 % A description of each parameter follows:
92 % o image: the image to had the area cleared in
94 % o bounds: the area to be clear within the imag image
96 % o exception: return any errors or warnings in this structure.
99 static void ClearBounds(Image *image,RectangleInfo *bounds,
100 ExceptionInfo *exception)
107 if (image->matte == MagickFalse)
108 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,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 == (Quantum *) NULL)
120 for (x=0; x < (ssize_t) bounds->width; x++)
122 SetPixelAlpha(image,TransparentAlpha,q);
123 q+=GetPixelChannels(image);
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 Quantum
179 for (y=0; y < (ssize_t) bounds->height; y++)
181 p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,
183 q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,
185 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
187 for (x=0; x < (ssize_t) bounds->width; x++)
189 if ((GetPixelAlpha(image1,p) <= (Quantum) (QuantumRange/2)) &&
190 (GetPixelAlpha(image1,q) > (Quantum) (QuantumRange/2)))
192 p+=GetPixelChannels(image1);
195 if (x < (ssize_t) bounds->width)
198 return(y < (ssize_t) bounds->height ? MagickTrue : MagickFalse);
202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
206 % C o a l e s c e I m a g e s %
210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212 % CoalesceImages() composites a set of images while respecting any page
213 % offsets and disposal methods. GIF, MIFF, and MNG animation sequences
214 % typically start with an image background and each subsequent image
215 % varies in size and offset. A new image sequence is returned with all
216 % images the same size as the first images virtual canvas and composited
217 % with the next image in the sequence.
219 % The format of the CoalesceImages method is:
221 % Image *CoalesceImages(Image *image,ExceptionInfo *exception)
223 % A description of each parameter follows:
225 % o image: the image sequence.
227 % o exception: return any errors or warnings in this structure.
230 MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
244 Coalesce the image sequence.
246 assert(image != (Image *) NULL);
247 assert(image->signature == MagickSignature);
248 if (image->debug != MagickFalse)
249 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
250 assert(exception != (ExceptionInfo *) NULL);
251 assert(exception->signature == MagickSignature);
252 next=GetFirstImageInList(image);
254 if (bounds.width == 0)
256 bounds.width=next->columns;
258 bounds.width+=bounds.x;
260 if (bounds.height == 0)
262 bounds.height=next->rows;
264 bounds.height+=bounds.y;
268 coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue,
270 if (coalesce_image == (Image *) NULL)
271 return((Image *) NULL);
272 coalesce_image->page=bounds;
273 coalesce_image->dispose=NoneDispose;
274 (void) SetImageBackgroundColor(coalesce_image,exception);
276 Coalesce rest of the images.
278 dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
279 (void) CompositeImage(coalesce_image,next,CopyCompositeOp,MagickTrue,
280 next->page.x,next->page.y,exception);
281 next=GetNextImageInList(next);
282 for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
285 Determine the bounds that was overlaid in the previous image.
287 previous=GetPreviousImageInList(next);
288 bounds=previous->page;
289 bounds.width=previous->columns;
290 bounds.height=previous->rows;
293 bounds.width+=bounds.x;
296 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) coalesce_image->columns)
297 bounds.width=coalesce_image->columns-bounds.x;
300 bounds.height+=bounds.y;
303 if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) coalesce_image->rows)
304 bounds.height=coalesce_image->rows-bounds.y;
306 Replace the dispose image with the new coalesced image.
308 if (GetPreviousImageInList(next)->dispose != PreviousDispose)
310 dispose_image=DestroyImage(dispose_image);
311 dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
312 if (dispose_image == (Image *) NULL)
314 coalesce_image=DestroyImageList(coalesce_image);
315 return((Image *) NULL);
319 Clear the overlaid area of the coalesced bounds for background disposal
321 if (next->previous->dispose == BackgroundDispose)
322 ClearBounds(dispose_image,&bounds,exception);
324 Next image is the dispose image, overlaid with next frame in sequence.
326 coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception);
327 coalesce_image->next->previous=coalesce_image;
328 previous=coalesce_image;
329 coalesce_image=GetNextImageInList(coalesce_image);
330 (void) CompositeImage(coalesce_image,next,next->matte != MagickFalse ?
331 OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
333 (void) CloneImageProfiles(coalesce_image,next);
334 (void) CloneImageProperties(coalesce_image,next);
335 (void) CloneImageArtifacts(coalesce_image,next);
336 coalesce_image->page=previous->page;
338 If a pixel goes opaque to transparent, use background dispose.
340 if (IsBoundsCleared(previous,coalesce_image,&bounds,exception))
341 coalesce_image->dispose=BackgroundDispose;
343 coalesce_image->dispose=NoneDispose;
344 previous->dispose=coalesce_image->dispose;
346 dispose_image=DestroyImage(dispose_image);
347 return(GetFirstImageInList(coalesce_image));
351 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
355 % D i s p o s e I m a g e s %
359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
361 % DisposeImages() returns the coalesced frames of a GIF animation as it would
362 % appear after the GIF dispose method of that frame has been applied. That
363 % is it returned the appearance of each frame before the next is overlaid.
365 % The format of the DisposeImages method is:
367 % Image *DisposeImages(Image *image,ExceptionInfo *exception)
369 % A description of each parameter follows:
371 % o images: the image sequence.
373 % o exception: return any errors or warnings in this structure.
376 MagickExport Image *DisposeImages(const Image *images,ExceptionInfo *exception)
390 Run the image through the animation sequence
392 assert(images != (Image *) NULL);
393 assert(images->signature == MagickSignature);
394 if (images->debug != MagickFalse)
395 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
396 assert(exception != (ExceptionInfo *) NULL);
397 assert(exception->signature == MagickSignature);
398 image=GetFirstImageInList(images);
399 dispose_image=CloneImage(image,image->page.width,image->page.height,
400 MagickTrue,exception);
401 if (dispose_image == (Image *) NULL)
402 return((Image *) NULL);
403 dispose_image->page=image->page;
404 dispose_image->page.x=0;
405 dispose_image->page.y=0;
406 dispose_image->dispose=NoneDispose;
407 dispose_image->background_color.alpha=(Quantum) TransparentAlpha;
408 (void) SetImageBackgroundColor(dispose_image,exception);
409 dispose_images=NewImageList();
410 for (next=image; image != (Image *) NULL; image=GetNextImageInList(image))
416 Overlay this frame's image over the previous disposal image.
418 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
419 if (current_image == (Image *) NULL)
421 dispose_images=DestroyImageList(dispose_images);
422 dispose_image=DestroyImage(dispose_image);
423 return((Image *) NULL);
425 (void) CompositeImage(current_image,next,next->matte != MagickFalse ?
426 OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
429 Handle Background dispose: image is displayed for the delay period.
431 if (next->dispose == BackgroundDispose)
434 bounds.width=next->columns;
435 bounds.height=next->rows;
438 bounds.width+=bounds.x;
441 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
442 bounds.width=current_image->columns-bounds.x;
445 bounds.height+=bounds.y;
448 if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
449 bounds.height=current_image->rows-bounds.y;
450 ClearBounds(current_image,&bounds,exception);
453 Select the appropriate previous/disposed image.
455 if (next->dispose == PreviousDispose)
456 current_image=DestroyImage(current_image);
459 dispose_image=DestroyImage(dispose_image);
460 dispose_image=current_image;
461 current_image=(Image *)NULL;
464 Save the dispose image just calculated for return.
470 dispose=CloneImage(dispose_image,0,0,MagickTrue,exception);
471 if (dispose == (Image *) NULL)
473 dispose_images=DestroyImageList(dispose_images);
474 dispose_image=DestroyImage(dispose_image);
475 return((Image *) NULL);
477 (void) CloneImageProfiles(dispose,next);
478 (void) CloneImageProperties(dispose,next);
479 (void) CloneImageArtifacts(dispose,next);
482 dispose->dispose=next->dispose;
483 AppendImageToList(&dispose_images,dispose);
486 dispose_image=DestroyImage(dispose_image);
487 return(GetFirstImageInList(dispose_images));
491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
495 + C o m p a r e P i x e l s %
499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
501 % ComparePixels() Compare the two pixels and return true if the pixels
502 % differ according to the given LayerType comparision method.
504 % This currently only used internally by CompareImagesBounds(). It is
505 % doubtful that this sub-routine will be useful outside this module.
507 % The format of the ComparePixels method is:
509 % MagickBooleanType *ComparePixels(const ImageLayerMethod method,
510 % const PixelInfo *p,const PixelInfo *q)
512 % A description of each parameter follows:
514 % o method: What differences to look for. Must be one of
515 % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
517 % o p, q: the pixels to test for appropriate differences.
521 static MagickBooleanType ComparePixels(const ImageLayerMethod method,
522 const PixelInfo *p,const PixelInfo *q)
529 Any change in pixel values
531 if (method == CompareAnyLayer)
532 return((MagickBooleanType)(IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
534 o1 = (p->matte != MagickFalse) ? p->alpha : OpaqueAlpha;
535 o2 = (q->matte != MagickFalse) ? q->alpha : OpaqueAlpha;
538 Pixel goes from opaque to transprency
540 if (method == CompareClearLayer)
541 return((MagickBooleanType) ( (o1 <= ((MagickRealType) QuantumRange/2.0)) &&
542 (o2 > ((MagickRealType) QuantumRange/2.0)) ) );
545 overlay would change first pixel by second
547 if (method == CompareOverlayLayer)
549 if (o2 > ((MagickRealType) QuantumRange/2.0))
551 return((MagickBooleanType) (IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562 + C o m p a r e I m a g e B o u n d s %
566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
568 % CompareImagesBounds() Given two images return the smallest rectangular area
569 % by which the two images differ, accourding to the given 'Compare...'
572 % This currently only used internally in this module, but may eventually
573 % be used by other modules.
575 % The format of the CompareImagesBounds method is:
577 % RectangleInfo *CompareImagesBounds(const ImageLayerMethod method,
578 % const Image *image1, const Image *image2, ExceptionInfo *exception)
580 % A description of each parameter follows:
582 % o method: What differences to look for. Must be one of CompareAnyLayer,
583 % CompareClearLayer, CompareOverlayLayer.
585 % o image1, image2: the two images to compare.
587 % o exception: return any errors or warnings in this structure.
591 static RectangleInfo CompareImagesBounds(const Image *image1,const Image *image2,
592 const ImageLayerMethod method,ExceptionInfo *exception)
601 register const Quantum
612 Set bounding box of the differences between images.
614 GetPixelInfo(image1,&pixel1);
615 GetPixelInfo(image2,&pixel2);
616 for (x=0; x < (ssize_t) image1->columns; x++)
618 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
619 q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
620 if ((p == (const Quantum *) NULL) ||
621 (q == (Quantum *) NULL))
623 for (y=0; y < (ssize_t) image1->rows; y++)
625 GetPixelInfoPixel(image1,p,&pixel1);
626 GetPixelInfoPixel(image2,q,&pixel2);
627 if (ComparePixels(method,&pixel1,&pixel2))
629 p+=GetPixelChannels(image1);
632 if (y < (ssize_t) image1->rows)
635 if (x >= (ssize_t) image1->columns)
638 Images are identical, return a null image.
647 for (x=(ssize_t) image1->columns-1; x >= 0; x--)
649 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
650 q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
651 if ((p == (const Quantum *) NULL) ||
652 (q == (Quantum *) NULL))
654 for (y=0; y < (ssize_t) image1->rows; y++)
656 GetPixelInfoPixel(image1,p,&pixel1);
657 GetPixelInfoPixel(image2,q,&pixel2);
658 if (ComparePixels(method,&pixel1,&pixel2))
660 p+=GetPixelChannels(image1);
663 if (y < (ssize_t) image1->rows)
666 bounds.width=(size_t) (x-bounds.x+1);
667 for (y=0; y < (ssize_t) image1->rows; y++)
669 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
670 q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
671 if ((p == (const Quantum *) NULL) ||
672 (q == (Quantum *) NULL))
674 for (x=0; x < (ssize_t) image1->columns; x++)
676 GetPixelInfoPixel(image1,p,&pixel1);
677 GetPixelInfoPixel(image2,q,&pixel2);
678 if (ComparePixels(method,&pixel1,&pixel2))
680 p+=GetPixelChannels(image1);
683 if (x < (ssize_t) image1->columns)
687 for (y=(ssize_t) image1->rows-1; y >= 0; y--)
689 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
690 q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
691 if ((p == (const Quantum *) NULL) ||
692 (q == (Quantum *) NULL))
694 for (x=0; x < (ssize_t) image1->columns; x++)
696 GetPixelInfoPixel(image1,p,&pixel1);
697 GetPixelInfoPixel(image2,q,&pixel2);
698 if (ComparePixels(method,&pixel1,&pixel2))
700 p+=GetPixelChannels(image1);
703 if (x < (ssize_t) image1->columns)
706 bounds.height=(size_t) (y-bounds.y+1);
711 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
715 % C o m p a r e I m a g e L a y e r s %
719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
721 % CompareImagesLayers() compares each image with the next in a sequence and
722 % returns the minimum bounding region of all the pixel differences (of the
723 % ImageLayerMethod specified) it discovers.
725 % Images do NOT have to be the same size, though it is best that all the
726 % images are 'coalesced' (images are all the same size, on a flattened
727 % canvas, so as to represent exactly how an specific frame should look).
729 % No GIF dispose methods are applied, so GIF animations must be coalesced
730 % before applying this image operator to find differences to them.
732 % The format of the CompareImagesLayers method is:
734 % Image *CompareImagesLayers(const Image *images,
735 % const ImageLayerMethod method,ExceptionInfo *exception)
737 % A description of each parameter follows:
739 % o image: the image.
741 % o method: the layers type to compare images with. Must be one of...
742 % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
744 % o exception: return any errors or warnings in this structure.
748 MagickExport Image *CompareImagesLayers(const Image *image,
749 const ImageLayerMethod method, ExceptionInfo *exception)
765 assert(image != (const Image *) NULL);
766 assert(image->signature == MagickSignature);
767 if (image->debug != MagickFalse)
768 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
769 assert(exception != (ExceptionInfo *) NULL);
770 assert(exception->signature == MagickSignature);
771 assert((method == CompareAnyLayer) ||
772 (method == CompareClearLayer) ||
773 (method == CompareOverlayLayer));
775 Allocate bounds memory.
777 next=GetFirstImageInList(image);
778 bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
779 GetImageListLength(next),sizeof(*bounds));
780 if (bounds == (RectangleInfo *) NULL)
781 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
783 Set up first comparision images.
785 image_a=CloneImage(next,next->page.width,next->page.height,
786 MagickTrue,exception);
787 if (image_a == (Image *) NULL)
789 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
790 return((Image *) NULL);
792 image_a->background_color.alpha=(Quantum) TransparentAlpha;
793 (void) SetImageBackgroundColor(image_a,exception);
794 image_a->page=next->page;
797 (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
798 next->page.y,exception);
800 Compute the bounding box of changes for the later images
803 next=GetNextImageInList(next);
804 for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
806 image_b=CloneImage(image_a,0,0,MagickTrue,exception);
807 if (image_b == (Image *) NULL)
809 image_a=DestroyImage(image_a);
810 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
811 return((Image *) NULL);
813 (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
814 next->page.y,exception);
815 bounds[i]=CompareImagesBounds(image_b,image_a,method,exception);
817 image_b=DestroyImage(image_b);
820 image_a=DestroyImage(image_a);
822 Clone first image in sequence.
824 next=GetFirstImageInList(image);
825 layers=CloneImage(next,0,0,MagickTrue,exception);
826 if (layers == (Image *) NULL)
828 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
829 return((Image *) NULL);
832 Deconstruct the image sequence.
835 next=GetNextImageInList(next);
836 for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
838 image_a=CloneImage(next,0,0,MagickTrue,exception);
839 if (image_a == (Image *) NULL)
841 image_b=CropImage(image_a,&bounds[i],exception);
842 image_a=DestroyImage(image_a);
843 if (image_b == (Image *) NULL)
845 AppendImageToList(&layers,image_b);
848 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
849 if (next != (Image *) NULL)
851 layers=DestroyImageList(layers);
852 return((Image *) NULL);
854 return(GetFirstImageInList(layers));
858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
862 + O p t i m i z e L a y e r F r a m e s %
866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
868 % OptimizeLayerFrames() takes a coalesced GIF animation, and compares each
869 % frame against the three different 'disposal' forms of the previous frame.
870 % From this it then attempts to select the smallest cropped image and
871 % disposal method needed to reproduce the resulting image.
873 % Note that this not easy, and may require the expansion of the bounds
874 % of previous frame, simply clear pixels for the next animation frame to
875 % transparency according to the selected dispose method.
877 % The format of the OptimizeLayerFrames method is:
879 % Image *OptimizeLayerFrames(const Image *image,
880 % const ImageLayerMethod method, ExceptionInfo *exception)
882 % A description of each parameter follows:
884 % o image: the image.
886 % o method: the layers technique to optimize with. Must be one of...
887 % OptimizeImageLayer, or OptimizePlusLayer. The Plus form allows
888 % the addition of extra 'zero delay' frames to clear pixels from
889 % the previous frame, and the removal of frames that done change,
890 % merging the delay times together.
892 % o exception: return any errors or warnings in this structure.
896 Define a 'fake' dispose method where the frame is duplicated, (for
897 OptimizePlusLayer) with a extra zero time delay frame which does a
898 BackgroundDisposal to clear the pixels that need to be cleared.
900 #define DupDispose ((DisposeType)9)
902 Another 'fake' dispose method used to removed frames that don't change.
904 #define DelDispose ((DisposeType)8)
906 #define DEBUG_OPT_FRAME 0
908 static Image *OptimizeLayerFrames(const Image *image,
909 const ImageLayerMethod method, ExceptionInfo *exception)
940 assert(image != (const Image *) NULL);
941 assert(image->signature == MagickSignature);
942 if (image->debug != MagickFalse)
943 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
944 assert(exception != (ExceptionInfo *) NULL);
945 assert(exception->signature == MagickSignature);
946 assert(method == OptimizeLayer ||
947 method == OptimizeImageLayer ||
948 method == OptimizePlusLayer);
951 Are we allowed to add/remove frames from animation
953 add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
955 Ensure all the images are the same size
957 curr=GetFirstImageInList(image);
958 for (; curr != (Image *) NULL; curr=GetNextImageInList(curr))
960 if ((curr->columns != image->columns) || (curr->rows != image->rows))
961 ThrowImageException(OptionError,"ImagesAreNotTheSameSize");
963 FUTURE: also check that image is also fully coalesced (full page)
964 Though as long as they are the same size it should not matter.
968 Allocate memory (times 2 if we allow the use of frame duplications)
970 curr=GetFirstImageInList(image);
971 bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
972 GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)*
974 if (bounds == (RectangleInfo *) NULL)
975 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
976 disposals=(DisposeType *) AcquireQuantumMemory((size_t)
977 GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
979 if (disposals == (DisposeType *) NULL)
981 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
982 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
985 Initialise Previous Image as fully transparent
987 prev_image=CloneImage(curr,curr->page.width,curr->page.height,
988 MagickTrue,exception);
989 if (prev_image == (Image *) NULL)
991 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
992 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
993 return((Image *) NULL);
995 prev_image->page=curr->page; /* ERROR: <-- should not be need, but is! */
996 prev_image->page.x=0;
997 prev_image->page.y=0;
998 prev_image->dispose=NoneDispose;
1000 prev_image->background_color.alpha=(Quantum) TransparentAlpha;
1001 (void) SetImageBackgroundColor(prev_image,exception);
1003 Figure out the area of overlay of the first frame
1004 No pixel could be cleared as all pixels are already cleared.
1008 (void) FormatLocaleFile(stderr, "frame %.20g :-\n", (double) i);
1010 disposals[0]=NoneDispose;
1011 bounds[0]=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
1013 (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g\n\n",
1014 (double) bounds[i].width,(double) bounds[i].height,
1015 (double) bounds[i].x,(double) bounds[i].y );
1018 Compute the bounding box of changes for each pair of images.
1021 bgnd_image=(Image *)NULL;
1022 dup_image=(Image *)NULL;
1024 dup_bounds.height=0;
1027 curr=GetNextImageInList(curr);
1028 for ( ; curr != (const Image *) NULL; curr=GetNextImageInList(curr))
1031 (void) FormatLocaleFile(stderr, "frame %.20g :-\n", (double) i);
1034 Assume none disposal is the best
1036 bounds[i]=CompareImagesBounds(curr->previous,curr,CompareAnyLayer,exception);
1037 cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception);
1038 disposals[i-1]=NoneDispose;
1040 (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g%s%s\n",
1041 (double) bounds[i].width,(double) bounds[i].height,
1042 (double) bounds[i].x,(double) bounds[i].y,
1043 bounds[i].x < 0?" (unchanged)":"",
1044 cleared?" (pixels cleared)":"");
1046 if ( bounds[i].x < 0 ) {
1048 Image frame is exactly the same as the previous frame!
1049 If not adding frames leave it to be cropped down to a null image.
1050 Otherwise mark previous image for deleted, transfering its crop bounds
1051 to the current image.
1053 if ( add_frames && i>=2 ) {
1054 disposals[i-1]=DelDispose;
1055 disposals[i]=NoneDispose;
1056 bounds[i]=bounds[i-1];
1064 Compare a none disposal against a previous disposal
1066 try_bounds=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
1067 try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception);
1069 (void) FormatLocaleFile(stderr, "test_prev: %.20gx%.20g%+.20g%+.20g%s\n",
1070 (double) try_bounds.width,(double) try_bounds.height,
1071 (double) try_bounds.x,(double) try_bounds.y,
1072 try_cleared?" (pixels were cleared)":"");
1074 if ( (!try_cleared && cleared ) ||
1075 try_bounds.width * try_bounds.height
1076 < bounds[i].width * bounds[i].height )
1078 cleared=try_cleared;
1079 bounds[i]=try_bounds;
1080 disposals[i-1]=PreviousDispose;
1082 (void) FormatLocaleFile(stderr, "previous: accepted\n");
1084 (void) FormatLocaleFile(stderr, "previous: rejected\n");
1089 If we are allowed lets try a complex frame duplication.
1090 It is useless if the previous image already clears pixels correctly.
1091 This method will always clear all the pixels that need to be cleared.
1093 dup_bounds.width=dup_bounds.height=0; /* no dup, no pixel added */
1096 dup_image=CloneImage(curr->previous,curr->previous->page.width,
1097 curr->previous->page.height,MagickTrue,exception);
1098 if (dup_image == (Image *) NULL)
1100 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1101 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1102 prev_image=DestroyImage(prev_image);
1103 return((Image *) NULL);
1105 dup_bounds=CompareImagesBounds(dup_image,curr,CompareClearLayer,exception);
1106 ClearBounds(dup_image,&dup_bounds,exception);
1107 try_bounds=CompareImagesBounds(dup_image,curr,CompareAnyLayer,exception);
1109 dup_bounds.width*dup_bounds.height
1110 +try_bounds.width*try_bounds.height
1111 < bounds[i].width * bounds[i].height )
1113 cleared=MagickFalse;
1114 bounds[i]=try_bounds;
1115 disposals[i-1]=DupDispose;
1116 /* to be finalised later, if found to be optimial */
1119 dup_bounds.width=dup_bounds.height=0;
1122 Now compare against a simple background disposal
1124 bgnd_image=CloneImage(curr->previous,curr->previous->page.width,
1125 curr->previous->page.height,MagickTrue,exception);
1126 if (bgnd_image == (Image *) NULL)
1128 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1129 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1130 prev_image=DestroyImage(prev_image);
1131 if ( dup_image != (Image *) NULL)
1132 dup_image=DestroyImage(dup_image);
1133 return((Image *) NULL);
1135 bgnd_bounds=bounds[i-1]; /* interum bounds of the previous image */
1136 ClearBounds(bgnd_image,&bgnd_bounds,exception);
1137 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
1138 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1140 (void) FormatLocaleFile(stderr, "background: %s\n",
1141 try_cleared?"(pixels cleared)":"");
1146 Straight background disposal failed to clear pixels needed!
1147 Lets try expanding the disposal area of the previous frame, to
1148 include the pixels that are cleared. This guaranteed
1149 to work, though may not be the most optimized solution.
1151 try_bounds=CompareImagesBounds(curr->previous,curr,CompareClearLayer,exception);
1153 (void) FormatLocaleFile(stderr, "expand_clear: %.20gx%.20g%+.20g%+.20g%s\n",
1154 (double) try_bounds.width,(double) try_bounds.height,
1155 (double) try_bounds.x,(double) try_bounds.y,
1156 try_bounds.x<0?" (no expand nessary)":"");
1158 if ( bgnd_bounds.x < 0 )
1159 bgnd_bounds = try_bounds;
1163 (void) FormatLocaleFile(stderr, "expand_bgnd: %.20gx%.20g%+.20g%+.20g\n",
1164 (double) bgnd_bounds.width,(double) bgnd_bounds.height,
1165 (double) bgnd_bounds.x,(double) bgnd_bounds.y );
1167 if ( try_bounds.x < bgnd_bounds.x )
1169 bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x;
1170 if ( bgnd_bounds.width < try_bounds.width )
1171 bgnd_bounds.width = try_bounds.width;
1172 bgnd_bounds.x = try_bounds.x;
1176 try_bounds.width += try_bounds.x - bgnd_bounds.x;
1177 if ( bgnd_bounds.width < try_bounds.width )
1178 bgnd_bounds.width = try_bounds.width;
1180 if ( try_bounds.y < bgnd_bounds.y )
1182 bgnd_bounds.height += bgnd_bounds.y - try_bounds.y;
1183 if ( bgnd_bounds.height < try_bounds.height )
1184 bgnd_bounds.height = try_bounds.height;
1185 bgnd_bounds.y = try_bounds.y;
1189 try_bounds.height += try_bounds.y - bgnd_bounds.y;
1190 if ( bgnd_bounds.height < try_bounds.height )
1191 bgnd_bounds.height = try_bounds.height;
1194 (void) FormatLocaleFile(stderr, " to : %.20gx%.20g%+.20g%+.20g\n",
1195 (double) bgnd_bounds.width,(double) bgnd_bounds.height,
1196 (double) bgnd_bounds.x,(double) bgnd_bounds.y );
1199 ClearBounds(bgnd_image,&bgnd_bounds,exception);
1201 /* Something strange is happening with a specific animation
1202 * CompareAnyLayers (normal method) and CompareClearLayers returns the whole
1203 * image, which is not posibly correct! As verified by previous tests.
1204 * Something changed beyond the bgnd_bounds clearing. But without being able
1205 * to see, or writet he image at this point it is hard to tell what is wrong!
1206 * Only CompareOverlay seemed to return something sensible.
1208 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareClearLayer,exception);
1209 (void) FormatLocaleFile(stderr, "expand_ctst: %.20gx%.20g%+.20g%+.20g\n",
1210 (double) try_bounds.width,(double) try_bounds.height,
1211 (double) try_bounds.x,(double) try_bounds.y );
1212 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
1213 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1214 (void) FormatLocaleFile(stderr, "expand_any : %.20gx%.20g%+.20g%+.20g%s\n",
1215 (double) try_bounds.width,(double) try_bounds.height,
1216 (double) try_bounds.x,(double) try_bounds.y,
1217 try_cleared?" (pixels cleared)":"");
1219 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareOverlayLayer,exception);
1221 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1222 (void) FormatLocaleFile(stderr, "expand_test: %.20gx%.20g%+.20g%+.20g%s\n",
1223 (double) try_bounds.width,(double) try_bounds.height,
1224 (double) try_bounds.x,(double) try_bounds.y,
1225 try_cleared?" (pixels cleared)":"");
1229 Test if this background dispose is smaller than any of the
1230 other methods we tryed before this (including duplicated frame)
1233 bgnd_bounds.width*bgnd_bounds.height
1234 +try_bounds.width*try_bounds.height
1235 < bounds[i-1].width*bounds[i-1].height
1236 +dup_bounds.width*dup_bounds.height
1237 +bounds[i].width*bounds[i].height )
1239 cleared=MagickFalse;
1240 bounds[i-1]=bgnd_bounds;
1241 bounds[i]=try_bounds;
1242 if ( disposals[i-1] == DupDispose )
1243 dup_image=DestroyImage(dup_image);
1244 disposals[i-1]=BackgroundDispose;
1246 (void) FormatLocaleFile(stderr, "expand_bgnd: accepted\n");
1248 (void) FormatLocaleFile(stderr, "expand_bgnd: reject\n");
1253 Finalise choice of dispose, set new prev_image,
1254 and junk any extra images as appropriate,
1256 if ( disposals[i-1] == DupDispose )
1258 if (bgnd_image != (Image *) NULL)
1259 bgnd_image=DestroyImage(bgnd_image);
1260 prev_image=DestroyImage(prev_image);
1261 prev_image=dup_image, dup_image=(Image *) NULL;
1262 bounds[i+1]=bounds[i];
1263 bounds[i]=dup_bounds;
1264 disposals[i-1]=DupDispose;
1265 disposals[i]=BackgroundDispose;
1270 if ( dup_image != (Image *) NULL)
1271 dup_image=DestroyImage(dup_image);
1272 if ( disposals[i-1] != PreviousDispose )
1273 prev_image=DestroyImage(prev_image);
1274 if ( disposals[i-1] == BackgroundDispose )
1275 prev_image=bgnd_image, bgnd_image=(Image *)NULL;
1276 if (bgnd_image != (Image *) NULL)
1277 bgnd_image=DestroyImage(bgnd_image);
1278 if ( disposals[i-1] == NoneDispose )
1280 prev_image=CloneImage(curr->previous,curr->previous->page.width,
1281 curr->previous->page.height,MagickTrue,exception);
1282 if (prev_image == (Image *) NULL)
1284 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1285 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1286 return((Image *) NULL);
1291 assert(prev_image != (Image *) NULL);
1292 disposals[i]=disposals[i-1];
1294 (void) FormatLocaleFile(stderr, "final %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1296 CommandOptionToMnemonic(MagickDisposeOptions, disposals[i-1]),
1297 (double) bounds[i-1].width, (double) bounds[i-1].height,
1298 (double) bounds[i-1].x, (double) bounds[i-1].y );
1301 (void) FormatLocaleFile(stderr, "interum %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1303 CommandOptionToMnemonic(MagickDisposeOptions, disposals[i]),
1304 (double) bounds[i].width, (double) bounds[i].height,
1305 (double) bounds[i].x, (double) bounds[i].y );
1306 (void) FormatLocaleFile(stderr, "\n");
1310 prev_image=DestroyImage(prev_image);
1312 Optimize all images in sequence.
1314 sans_exception=AcquireExceptionInfo();
1316 curr=GetFirstImageInList(image);
1317 optimized_image=NewImageList();
1318 while ( curr != (const Image *) NULL )
1320 prev_image=CloneImage(curr,0,0,MagickTrue,exception);
1321 if (prev_image == (Image *) NULL)
1323 if ( disposals[i] == DelDispose ) {
1325 while ( disposals[i] == DelDispose ) {
1326 time += curr->delay*1000/curr->ticks_per_second;
1327 curr=GetNextImageInList(curr);
1330 time += curr->delay*1000/curr->ticks_per_second;
1331 prev_image->ticks_per_second = 100L;
1332 prev_image->delay = time*prev_image->ticks_per_second/1000;
1334 bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
1335 prev_image=DestroyImage(prev_image);
1336 if (bgnd_image == (Image *) NULL)
1338 bgnd_image->dispose=disposals[i];
1339 if ( disposals[i] == DupDispose ) {
1340 bgnd_image->delay=0;
1341 bgnd_image->dispose=NoneDispose;
1344 curr=GetNextImageInList(curr);
1345 AppendImageToList(&optimized_image,bgnd_image);
1348 sans_exception=DestroyExceptionInfo(sans_exception);
1349 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1350 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1351 if (curr != (Image *) NULL)
1353 optimized_image=DestroyImageList(optimized_image);
1354 return((Image *) NULL);
1356 return(GetFirstImageInList(optimized_image));
1360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1364 % O p t i m i z e I m a g e L a y e r s %
1368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1370 % OptimizeImageLayers() compares each image the GIF disposed forms of the
1371 % previous image in the sequence. From this it attempts to select the
1372 % smallest cropped image to replace each frame, while preserving the results
1373 % of the GIF animation.
1375 % The format of the OptimizeImageLayers method is:
1377 % Image *OptimizeImageLayers(const Image *image,
1378 % ExceptionInfo *exception)
1380 % A description of each parameter follows:
1382 % o image: the image.
1384 % o exception: return any errors or warnings in this structure.
1387 MagickExport Image *OptimizeImageLayers(const Image *image,
1388 ExceptionInfo *exception)
1390 return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
1394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1398 % O p t i m i z e P l u s I m a g e L a y e r s %
1402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1404 % OptimizeImagePlusLayers() is exactly as OptimizeImageLayers(), but may
1405 % also add or even remove extra frames in the animation, if it improves
1406 % the total number of pixels in the resulting GIF animation.
1408 % The format of the OptimizePlusImageLayers method is:
1410 % Image *OptimizePlusImageLayers(const Image *image,
1411 % ExceptionInfo *exception)
1413 % A description of each parameter follows:
1415 % o image: the image.
1417 % o exception: return any errors or warnings in this structure.
1420 MagickExport Image *OptimizePlusImageLayers(const Image *image,
1421 ExceptionInfo *exception)
1423 return OptimizeLayerFrames(image, OptimizePlusLayer, exception);
1427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1431 % 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 %
1435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437 % OptimizeImageTransparency() takes a frame optimized GIF animation, and
1438 % compares the overlayed pixels against the disposal image resulting from all
1439 % the previous frames in the animation. Any pixel that does not change the
1440 % disposal image (and thus does not effect the outcome of an overlay) is made
1443 % WARNING: This modifies the current images directly, rather than generate
1444 % a new image sequence.
1446 % The format of the OptimizeImageTransperency method is:
1448 % void OptimizeImageTransperency(Image *image,ExceptionInfo *exception)
1450 % A description of each parameter follows:
1452 % o image: the image sequence
1454 % o exception: return any errors or warnings in this structure.
1457 MagickExport void OptimizeImageTransparency(const Image *image,
1458 ExceptionInfo *exception)
1467 Run the image through the animation sequence
1469 assert(image != (Image *) NULL);
1470 assert(image->signature == MagickSignature);
1471 if (image->debug != MagickFalse)
1472 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1473 assert(exception != (ExceptionInfo *) NULL);
1474 assert(exception->signature == MagickSignature);
1475 next=GetFirstImageInList(image);
1476 dispose_image=CloneImage(next,next->page.width,next->page.height,
1477 MagickTrue,exception);
1478 if (dispose_image == (Image *) NULL)
1480 dispose_image->page=next->page;
1481 dispose_image->page.x=0;
1482 dispose_image->page.y=0;
1483 dispose_image->dispose=NoneDispose;
1484 dispose_image->background_color.alpha=(Quantum) TransparentAlpha;
1485 (void) SetImageBackgroundColor(dispose_image,exception);
1487 while ( next != (Image *) NULL )
1493 Overlay this frame's image over the previous disposal image
1495 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
1496 if (current_image == (Image *) NULL)
1498 dispose_image=DestroyImage(dispose_image);
1501 (void) CompositeImage(current_image,next,next->matte != MagickFalse ?
1502 OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
1505 At this point the image would be displayed, for the delay period
1507 Work out the disposal of the previous image
1509 if (next->dispose == BackgroundDispose)
1514 bounds.width=next->columns;
1515 bounds.height=next->rows;
1518 bounds.width+=bounds.x;
1521 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
1522 bounds.width=current_image->columns-bounds.x;
1525 bounds.height+=bounds.y;
1528 if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
1529 bounds.height=current_image->rows-bounds.y;
1530 ClearBounds(current_image, &bounds,exception);
1532 if (next->dispose != PreviousDispose)
1534 dispose_image=DestroyImage(dispose_image);
1535 dispose_image=current_image;
1538 current_image=DestroyImage(current_image);
1541 Optimize Transparency of the next frame (if present)
1543 next=GetNextImageInList(next);
1544 if (next != (Image *) NULL) {
1545 (void) CompositeImage(next,dispose_image,ChangeMaskCompositeOp,
1546 MagickTrue,-(next->page.x),-(next->page.y),exception);
1549 dispose_image=DestroyImage(dispose_image);
1554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1558 % R e m o v e D u p l i c a t e L a y e r s %
1562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1564 % RemoveDuplicateLayers() removes any image that is exactly the same as the
1565 % next image in the given image list. Image size and virtual canvas offset
1566 % must also match, though not the virtual canvas size itself.
1568 % No check is made with regards to image disposal setting, though it is the
1569 % dispose setting of later image that is kept. Also any time delays are also
1570 % added together. As such coalesced image animations should still produce the
1571 % same result, though with duplicte frames merged into a single frame.
1573 % The format of the RemoveDuplicateLayers method is:
1575 % void RemoveDuplicateLayers(Image **image, ExceptionInfo *exception)
1577 % A description of each parameter follows:
1579 % o images: the image list
1581 % o exception: return any errors or warnings in this structure.
1584 MagickExport void RemoveDuplicateLayers(Image **images,
1585 ExceptionInfo *exception)
1594 assert((*images) != (const Image *) NULL);
1595 assert((*images)->signature == MagickSignature);
1596 if ((*images)->debug != MagickFalse)
1597 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
1598 assert(exception != (ExceptionInfo *) NULL);
1599 assert(exception->signature == MagickSignature);
1601 curr=GetFirstImageInList(*images);
1602 for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next)
1604 if ( curr->columns != next->columns || curr->rows != next->rows
1605 || curr->page.x != next->page.x || curr->page.y != next->page.y )
1607 bounds=CompareImagesBounds(curr,next,CompareAnyLayer,exception);
1608 if ( bounds.x < 0 ) {
1610 the two images are the same, merge time delays and delete one.
1613 time = curr->delay*1000/curr->ticks_per_second;
1614 time += next->delay*1000/next->ticks_per_second;
1615 next->ticks_per_second = 100L;
1616 next->delay = time*curr->ticks_per_second/1000;
1617 next->iterations = curr->iterations;
1619 (void) DeleteImageFromList(images);
1622 *images = GetFirstImageInList(*images);
1626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630 % R e m o v e Z e r o D e l a y L a y e r s %
1634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1636 % RemoveZeroDelayLayers() removes any image that as a zero delay time. Such
1637 % images generally represent intermediate or partial updates in GIF
1638 % animations used for file optimization. They are not ment to be displayed
1639 % to users of the animation. Viewable images in an animation should have a
1640 % time delay of 3 or more centi-seconds (hundredths of a second).
1642 % However if all the frames have a zero time delay, then either the animation
1643 % is as yet incomplete, or it is not a GIF animation. This a non-sensible
1644 % situation, so no image will be removed and a 'Zero Time Animation' warning
1645 % (exception) given.
1647 % No warning will be given if no image was removed because all images had an
1648 % appropriate non-zero time delay set.
1650 % Due to the special requirements of GIF disposal handling, GIF animations
1651 % should be coalesced first, before calling this function, though that is not
1654 % The format of the RemoveZeroDelayLayers method is:
1656 % void RemoveZeroDelayLayers(Image **image, ExceptionInfo *exception)
1658 % A description of each parameter follows:
1660 % o images: the image list
1662 % o exception: return any errors or warnings in this structure.
1665 MagickExport void RemoveZeroDelayLayers(Image **images,
1666 ExceptionInfo *exception)
1671 assert((*images) != (const Image *) NULL);
1672 assert((*images)->signature == MagickSignature);
1673 if ((*images)->debug != MagickFalse)
1674 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
1675 assert(exception != (ExceptionInfo *) NULL);
1676 assert(exception->signature == MagickSignature);
1678 i=GetFirstImageInList(*images);
1679 for ( ; i != (Image *) NULL; i=GetNextImageInList(i))
1680 if ( i->delay != 0L ) break;
1681 if ( i == (Image *) NULL ) {
1682 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1683 "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename);
1686 i=GetFirstImageInList(*images);
1687 while ( i != (Image *) NULL )
1689 if ( i->delay == 0L ) {
1690 (void) DeleteImageFromList(&i);
1694 i=GetNextImageInList(i);
1696 *images=GetFirstImageInList(*images);
1700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1704 % C o m p o s i t e L a y e r s %
1708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1710 % CompositeLayers() compose the source image sequence over the destination
1711 % image sequence, starting with the current image in both lists.
1713 % Each layer from the two image lists are composted together until the end of
1714 % one of the image lists is reached. The offset of each composition is also
1715 % adjusted to match the virtual canvas offsets of each layer. As such the
1716 % given offset is relative to the virtual canvas, and not the actual image.
1718 % Composition uses given x and y offsets, as the 'origin' location of the
1719 % source images virtual canvas (not the real image) allowing you to compose a
1720 % list of 'layer images' into the destiantioni images. This makes it well
1721 % sutiable for directly composing 'Clears Frame Animations' or 'Coaleased
1722 % Animations' onto a static or other 'Coaleased Animation' destination image
1723 % list. GIF disposal handling is not looked at.
1725 % Special case:- If one of the image sequences is the last image (just a
1726 % single image remaining), that image is repeatally composed with all the
1727 % images in the other image list. Either the source or destination lists may
1728 % be the single image, for this situation.
1730 % In the case of a single destination image (or last image given), that image
1731 % will ve cloned to match the number of images remaining in the source image
1734 % This is equivelent to the "-layer Composite" Shell API operator.
1737 % The format of the CompositeLayers method is:
1739 % void CompositeLayers(Image *destination, const CompositeOperator
1740 % compose, Image *source, const ssize_t x_offset, const ssize_t y_offset,
1741 % ExceptionInfo *exception);
1743 % A description of each parameter follows:
1745 % o destination: the destination images and results
1747 % o source: source image(s) for the layer composition
1749 % o compose, x_offset, y_offset: arguments passed on to CompositeImages()
1751 % o exception: return any errors or warnings in this structure.
1755 static inline void CompositeCanvas(Image *destination,
1756 const CompositeOperator compose,Image *source,ssize_t x_offset,
1757 ssize_t y_offset,ExceptionInfo *exception)
1759 x_offset+=source->page.x-destination->page.x;
1760 y_offset+=source->page.y-destination->page.y;
1761 (void) CompositeImage(destination,source,compose,MagickTrue,x_offset,
1762 y_offset,exception);
1765 MagickExport void CompositeLayers(Image *destination,
1766 const CompositeOperator compose, Image *source,const ssize_t x_offset,
1767 const ssize_t y_offset,ExceptionInfo *exception)
1769 assert(destination != (Image *) NULL);
1770 assert(destination->signature == MagickSignature);
1771 assert(source != (Image *) NULL);
1772 assert(source->signature == MagickSignature);
1773 assert(exception != (ExceptionInfo *) NULL);
1774 assert(exception->signature == MagickSignature);
1775 if (source->debug != MagickFalse || destination->debug != MagickFalse)
1776 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
1777 source->filename, destination->filename);
1780 Overlay single source image over destation image/list
1782 if ( source->next == (Image *) NULL )
1783 while ( destination != (Image *) NULL )
1785 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1787 destination=GetNextImageInList(destination);
1791 Overlay source image list over single destination.
1792 Multiple clones of destination image are created to match source list.
1793 Original Destination image becomes first image of generated list.
1794 As such the image list pointer does not require any change in caller.
1795 Some animation attributes however also needs coping in this case.
1797 else if ( destination->next == (Image *) NULL )
1799 Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
1801 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1803 /* copy source image attributes ? */
1804 if ( source->next != (Image *) NULL )
1806 destination->delay = source->delay;
1807 destination->iterations = source->iterations;
1809 source=GetNextImageInList(source);
1811 while ( source != (Image *) NULL )
1813 AppendImageToList(&destination,
1814 CloneImage(dest,0,0,MagickTrue,exception));
1815 destination=GetLastImageInList(destination);
1817 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1819 destination->delay = source->delay;
1820 destination->iterations = source->iterations;
1821 source=GetNextImageInList(source);
1823 dest=DestroyImage(dest);
1827 Overlay a source image list over a destination image list
1828 until either list runs out of images. (Does not repeat)
1831 while ( source != (Image *) NULL && destination != (Image *) NULL )
1833 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1835 source=GetNextImageInList(source);
1836 destination=GetNextImageInList(destination);
1841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1845 % M e r g e I m a g e L a y e r s %
1849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1851 % MergeImageLayers() composes all the image layers from the current given
1852 % image onward to produce a single image of the merged layers.
1854 % The inital canvas's size depends on the given ImageLayerMethod, and is
1855 % initialized using the first images background color. The images
1856 % are then compositied onto that image in sequence using the given
1857 % composition that has been assigned to each individual image.
1859 % The format of the MergeImageLayers is:
1861 % Image *MergeImageLayers(const Image *image,
1862 % const ImageLayerMethod method, ExceptionInfo *exception)
1864 % A description of each parameter follows:
1866 % o image: the image list to be composited together
1868 % o method: the method of selecting the size of the initial canvas.
1870 % MergeLayer: Merge all layers onto a canvas just large enough
1871 % to hold all the actual images. The virtual canvas of the
1872 % first image is preserved but otherwise ignored.
1874 % FlattenLayer: Use the virtual canvas size of first image.
1875 % Images which fall outside this canvas is clipped.
1876 % This can be used to 'fill out' a given virtual canvas.
1878 % MosaicLayer: Start with the virtual canvas of the first image,
1879 % enlarging left and right edges to contain all images.
1880 % Images with negative offsets will be clipped.
1882 % TrimBoundsLayer: Determine the overall bounds of all the image
1883 % layers just as in "MergeLayer", then adjust the the canvas
1884 % and offsets to be relative to those bounds, without overlaying
1887 % WARNING: a new image is not returned, the original image
1888 % sequence page data is modified instead.
1890 % o exception: return any errors or warnings in this structure.
1893 MagickExport Image *MergeImageLayers(Image *image,const ImageLayerMethod method,
1894 ExceptionInfo *exception)
1896 #define MergeLayersTag "Merge/Layers"
1907 register const Image
1918 assert(image != (Image *) NULL);
1919 assert(image->signature == MagickSignature);
1920 if (image->debug != MagickFalse)
1921 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1922 assert(exception != (ExceptionInfo *) NULL);
1923 assert(exception->signature == MagickSignature);
1925 Determine canvas image size, and its virtual canvas size and offset
1928 width=image->columns;
1932 case TrimBoundsLayer:
1936 next=GetNextImageInList(image);
1937 for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
1939 if (page.x > next->page.x)
1941 width+=page.x-next->page.x;
1942 page.x=next->page.x;
1944 if (page.y > next->page.y)
1946 height+=page.y-next->page.y;
1947 page.y=next->page.y;
1949 if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x))
1950 width=(size_t) next->page.x+(ssize_t)next->columns-page.x;
1951 if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y))
1952 height=(size_t) next->page.y+(ssize_t) next->rows-page.y;
1960 if (page.height > 0)
1970 if (page.height > 0)
1972 for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
1974 if (method == MosaicLayer)
1976 page.x=next->page.x;
1977 page.y=next->page.y;
1978 if ((ssize_t) width < (next->page.x+(ssize_t) next->columns))
1979 width=(size_t) next->page.x+next->columns;
1980 if ((ssize_t) height < (next->page.y+(ssize_t) next->rows))
1981 height=(size_t) next->page.y+next->rows;
1992 Set virtual canvas size if not defined.
1994 if (page.width == 0)
1995 page.width=page.x < 0 ? width : width+page.x;
1996 if (page.height == 0)
1997 page.height=page.y < 0 ? height : height+page.y;
1999 Handle "TrimBoundsLayer" method separately to normal 'layer merge'.
2001 if (method == TrimBoundsLayer)
2003 number_images=GetImageListLength(image);
2004 for (scene=0; scene < (ssize_t) number_images; scene++)
2006 image->page.x-=page.x;
2007 image->page.y-=page.y;
2008 image->page.width=width;
2009 image->page.height=height;
2010 proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2012 if (proceed == MagickFalse)
2014 image=GetNextImageInList(image);
2015 if (image == (Image *) NULL)
2018 return((Image *) NULL);
2021 Create canvas size of width and height, and background color.
2023 canvas=CloneImage(image,width,height,MagickTrue,exception);
2024 if (canvas == (Image *) NULL)
2025 return((Image *) NULL);
2026 (void) SetImageBackgroundColor(canvas,exception);
2028 canvas->dispose=UndefinedDispose;
2030 Compose images onto canvas, with progress monitor
2032 number_images=GetImageListLength(image);
2033 for (scene=0; scene < (ssize_t) number_images; scene++)
2035 (void) CompositeImage(canvas,image,image->compose,MagicTrue,image->page.x-
2036 canvas->page.x,image->page.y-canvas->page.y,exception);
2037 proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2039 if (proceed == MagickFalse)
2041 image=GetNextImageInList(image);
2042 if (image == (Image *) NULL)