2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5 % L AAA Y Y EEEEE RRRR %
9 % LLLLL A A Y EEEEE R R %
11 % MagickCore Image Layering Methods %
19 % Copyright 1999-2015 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/channel.h"
44 #include "MagickCore/color.h"
45 #include "MagickCore/color-private.h"
46 #include "MagickCore/composite.h"
47 #include "MagickCore/effect.h"
48 #include "MagickCore/exception.h"
49 #include "MagickCore/exception-private.h"
50 #include "MagickCore/geometry.h"
51 #include "MagickCore/image.h"
52 #include "MagickCore/layer.h"
53 #include "MagickCore/list.h"
54 #include "MagickCore/memory_.h"
55 #include "MagickCore/monitor.h"
56 #include "MagickCore/monitor-private.h"
57 #include "MagickCore/option.h"
58 #include "MagickCore/pixel-accessor.h"
59 #include "MagickCore/property.h"
60 #include "MagickCore/profile.h"
61 #include "MagickCore/resource_.h"
62 #include "MagickCore/resize.h"
63 #include "MagickCore/statistic.h"
64 #include "MagickCore/string_.h"
65 #include "MagickCore/transform.h"
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72 + C l e a r B o u n d s %
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78 % ClearBounds() Clear the area specified by the bounds in an image to
79 % transparency. This typically used to handle Background Disposal for the
80 % previous frame in an animation sequence.
82 % Warning: no bounds checks are performed, except for the null or missed
83 % image, for images that don't change. in all other cases bound must fall
88 % void ClearBounds(Image *image,RectangleInfo *bounds,
89 % ExceptionInfo *exception)
91 % A description of each parameter follows:
93 % o image: the image to had the area cleared in
95 % o bounds: the area to be clear within the imag image
97 % o exception: return any errors or warnings in this structure.
100 static void ClearBounds(Image *image,RectangleInfo *bounds,
101 ExceptionInfo *exception)
108 if (image->alpha_trait == UndefinedPixelTrait)
109 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
110 for (y=0; y < (ssize_t) bounds->height; y++)
118 q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
119 if (q == (Quantum *) NULL)
121 for (x=0; x < (ssize_t) bounds->width; x++)
123 SetPixelAlpha(image,TransparentAlpha,q);
124 q+=GetPixelChannels(image);
126 if (SyncAuthenticPixels(image,exception) == MagickFalse)
132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
136 + I s B o u n d s C l e a r e d %
140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142 % IsBoundsCleared() tests whether any pixel in the bounds given, gets cleared
143 % when going from the first image to the second image. This typically used
144 % to check if a proposed disposal method will work successfully to generate
145 % the second frame image from the first disposed form of the previous frame.
147 % Warning: no bounds checks are performed, except for the null or missed
148 % image, for images that don't change. in all other cases bound must fall
153 % MagickBooleanType IsBoundsCleared(const Image *image1,
154 % const Image *image2,RectangleInfo bounds,ExceptionInfo *exception)
156 % A description of each parameter follows:
158 % o image1, image 2: the images to check for cleared pixels
160 % o bounds: the area to be clear within the imag image
162 % o exception: return any errors or warnings in this structure.
165 static MagickBooleanType IsBoundsCleared(const Image *image1,
166 const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception)
168 register const Quantum
180 for (y=0; y < (ssize_t) bounds->height; y++)
182 p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,exception);
183 q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,exception);
184 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
186 for (x=0; x < (ssize_t) bounds->width; x++)
188 if ((GetPixelAlpha(image1,p) >= (Quantum) (QuantumRange/2)) &&
189 (GetPixelAlpha(image2,q) < (Quantum) (QuantumRange/2)))
191 p+=GetPixelChannels(image1);
192 q+=GetPixelChannels(image2);
194 if (x < (ssize_t) bounds->width)
197 return(y < (ssize_t) bounds->height ? MagickTrue : MagickFalse);
201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
205 % C o a l e s c e I m a g e s %
209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
211 % CoalesceImages() composites a set of images while respecting any page
212 % offsets and disposal methods. GIF, MIFF, and MNG animation sequences
213 % typically start with an image background and each subsequent image
214 % varies in size and offset. A new image sequence is returned with all
215 % images the same size as the first images virtual canvas and composited
216 % with the next image in the sequence.
218 % The format of the CoalesceImages method is:
220 % Image *CoalesceImages(Image *image,ExceptionInfo *exception)
222 % A description of each parameter follows:
224 % o image: the image sequence.
226 % o exception: return any errors or warnings in this structure.
229 MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
243 Coalesce the image sequence.
245 assert(image != (Image *) NULL);
246 assert(image->signature == MagickCoreSignature);
247 if (image->debug != MagickFalse)
248 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
249 assert(exception != (ExceptionInfo *) NULL);
250 assert(exception->signature == MagickCoreSignature);
251 next=GetFirstImageInList(image);
253 if (bounds.width == 0)
255 bounds.width=next->columns;
257 bounds.width+=bounds.x;
259 if (bounds.height == 0)
261 bounds.height=next->rows;
263 bounds.height+=bounds.y;
267 coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue,
269 if (coalesce_image == (Image *) NULL)
270 return((Image *) NULL);
271 (void) SetImageBackgroundColor(coalesce_image,exception);
272 coalesce_image->alpha_trait=next->alpha_trait;
273 coalesce_image->page=bounds;
274 coalesce_image->dispose=NoneDispose;
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,
331 next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp,
332 MagickTrue,next->page.x,next->page.y,exception);
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) != MagickFalse)
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 is
363 % 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 == MagickCoreSignature);
394 if (images->debug != MagickFalse)
395 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
396 assert(exception != (ExceptionInfo *) NULL);
397 assert(exception->signature == MagickCoreSignature);
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,
426 next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp,
427 MagickTrue,next->page.x,next->page.y,exception);
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 LayerMethod 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 LayerMethod 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->alpha_trait != UndefinedPixelTrait) ? p->alpha : OpaqueAlpha;
535 o2 = (q->alpha_trait != UndefinedPixelTrait) ? q->alpha : OpaqueAlpha;
537 Pixel goes from opaque to transprency.
539 if (method == CompareClearLayer)
540 return((MagickBooleanType) ( (o1 <= ((double) QuantumRange/2.0)) &&
541 (o2 > ((double) QuantumRange/2.0)) ) );
543 Overlay would change first pixel by second.
545 if (method == CompareOverlayLayer)
547 if (o2 > ((double) QuantumRange/2.0))
549 return((MagickBooleanType) (IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560 + C o m p a r e I m a g e B o u n d s %
564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566 % CompareImagesBounds() Given two images return the smallest rectangular area
567 % by which the two images differ, accourding to the given 'Compare...'
570 % This currently only used internally in this module, but may eventually
571 % be used by other modules.
573 % The format of the CompareImagesBounds method is:
575 % RectangleInfo *CompareImagesBounds(const LayerMethod method,
576 % const Image *image1, const Image *image2, ExceptionInfo *exception)
578 % A description of each parameter follows:
580 % o method: What differences to look for. Must be one of CompareAnyLayer,
581 % CompareClearLayer, CompareOverlayLayer.
583 % o image1, image2: the two images to compare.
585 % o exception: return any errors or warnings in this structure.
589 static RectangleInfo CompareImagesBounds(const Image *image1,
590 const Image *image2,const LayerMethod method,ExceptionInfo *exception)
599 register const Quantum
610 Set bounding box of the differences between images.
612 GetPixelInfo(image1,&pixel1);
613 GetPixelInfo(image2,&pixel2);
614 for (x=0; x < (ssize_t) image1->columns; x++)
616 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
617 q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
618 if ((p == (const Quantum *) NULL) ||
619 (q == (Quantum *) NULL))
621 for (y=0; y < (ssize_t) image1->rows; y++)
623 GetPixelInfoPixel(image1,p,&pixel1);
624 GetPixelInfoPixel(image2,q,&pixel2);
625 if (ComparePixels(method,&pixel1,&pixel2))
627 p+=GetPixelChannels(image1);
628 q+=GetPixelChannels(image2);
630 if (y < (ssize_t) image1->rows)
633 if (x >= (ssize_t) image1->columns)
636 Images are identical, return a null image.
645 for (x=(ssize_t) image1->columns-1; x >= 0; x--)
647 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
648 q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
649 if ((p == (const Quantum *) NULL) ||
650 (q == (Quantum *) NULL))
652 for (y=0; y < (ssize_t) image1->rows; y++)
654 GetPixelInfoPixel(image1,p,&pixel1);
655 GetPixelInfoPixel(image2,q,&pixel2);
656 if (ComparePixels(method,&pixel1,&pixel2))
658 p+=GetPixelChannels(image1);
659 q+=GetPixelChannels(image2);
661 if (y < (ssize_t) image1->rows)
664 bounds.width=(size_t) (x-bounds.x+1);
665 for (y=0; y < (ssize_t) image1->rows; y++)
667 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
668 q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
669 if ((p == (const Quantum *) NULL) ||
670 (q == (Quantum *) NULL))
672 for (x=0; x < (ssize_t) image1->columns; x++)
674 GetPixelInfoPixel(image1,p,&pixel1);
675 GetPixelInfoPixel(image2,q,&pixel2);
676 if (ComparePixels(method,&pixel1,&pixel2))
678 p+=GetPixelChannels(image1);
679 q+=GetPixelChannels(image2);
681 if (x < (ssize_t) image1->columns)
685 for (y=(ssize_t) image1->rows-1; y >= 0; y--)
687 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
688 q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
689 if ((p == (const Quantum *) NULL) ||
690 (q == (Quantum *) NULL))
692 for (x=0; x < (ssize_t) image1->columns; x++)
694 GetPixelInfoPixel(image1,p,&pixel1);
695 GetPixelInfoPixel(image2,q,&pixel2);
696 if (ComparePixels(method,&pixel1,&pixel2))
698 p+=GetPixelChannels(image1);
699 q+=GetPixelChannels(image2);
701 if (x < (ssize_t) image1->columns)
704 bounds.height=(size_t) (y-bounds.y+1);
709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
713 % C o m p a r e I m a g e L a y e r s %
717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719 % CompareImagesLayers() compares each image with the next in a sequence and
720 % returns the minimum bounding region of all the pixel differences (of the
721 % LayerMethod specified) it discovers.
723 % Images do NOT have to be the same size, though it is best that all the
724 % images are 'coalesced' (images are all the same size, on a flattened
725 % canvas, so as to represent exactly how an specific frame should look).
727 % No GIF dispose methods are applied, so GIF animations must be coalesced
728 % before applying this image operator to find differences to them.
730 % The format of the CompareImagesLayers method is:
732 % Image *CompareImagesLayers(const Image *images,
733 % const LayerMethod method,ExceptionInfo *exception)
735 % A description of each parameter follows:
737 % o image: the image.
739 % o method: the layers type to compare images with. Must be one of...
740 % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
742 % o exception: return any errors or warnings in this structure.
746 MagickExport Image *CompareImagesLayers(const Image *image,
747 const LayerMethod method, ExceptionInfo *exception)
763 assert(image != (const Image *) NULL);
764 assert(image->signature == MagickCoreSignature);
765 if (image->debug != MagickFalse)
766 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
767 assert(exception != (ExceptionInfo *) NULL);
768 assert(exception->signature == MagickCoreSignature);
769 assert((method == CompareAnyLayer) ||
770 (method == CompareClearLayer) ||
771 (method == CompareOverlayLayer));
773 Allocate bounds memory.
775 next=GetFirstImageInList(image);
776 bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
777 GetImageListLength(next),sizeof(*bounds));
778 if (bounds == (RectangleInfo *) NULL)
779 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
781 Set up first comparision images.
783 image_a=CloneImage(next,next->page.width,next->page.height,
784 MagickTrue,exception);
785 if (image_a == (Image *) NULL)
787 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
788 return((Image *) NULL);
790 image_a->background_color.alpha=(Quantum) TransparentAlpha;
791 (void) SetImageBackgroundColor(image_a,exception);
792 image_a->page=next->page;
795 (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
796 next->page.y,exception);
798 Compute the bounding box of changes for the later images
801 next=GetNextImageInList(next);
802 for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
804 image_b=CloneImage(image_a,0,0,MagickTrue,exception);
805 if (image_b == (Image *) NULL)
807 image_a=DestroyImage(image_a);
808 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
809 return((Image *) NULL);
811 (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
812 next->page.y,exception);
813 bounds[i]=CompareImagesBounds(image_b,image_a,method,exception);
814 image_b=DestroyImage(image_b);
817 image_a=DestroyImage(image_a);
819 Clone first image in sequence.
821 next=GetFirstImageInList(image);
822 layers=CloneImage(next,0,0,MagickTrue,exception);
823 if (layers == (Image *) NULL)
825 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
826 return((Image *) NULL);
829 Deconstruct the image sequence.
832 next=GetNextImageInList(next);
833 for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
835 if ((bounds[i].x == -1) && (bounds[i].y == -1) &&
836 (bounds[i].width == 1) && (bounds[i].height == 1))
839 An empty frame is returned from CompareImageBounds(), which means the
840 current frame is identical to the previous frame.
845 image_a=CloneImage(next,0,0,MagickTrue,exception);
846 if (image_a == (Image *) NULL)
848 image_b=CropImage(image_a,&bounds[i],exception);
849 image_a=DestroyImage(image_a);
850 if (image_b == (Image *) NULL)
852 AppendImageToList(&layers,image_b);
855 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
856 if (next != (Image *) NULL)
858 layers=DestroyImageList(layers);
859 return((Image *) NULL);
861 return(GetFirstImageInList(layers));
865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
869 + O p t i m i z e L a y e r F r a m e s %
873 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
875 % OptimizeLayerFrames() takes a coalesced GIF animation, and compares each
876 % frame against the three different 'disposal' forms of the previous frame.
877 % From this it then attempts to select the smallest cropped image and
878 % disposal method needed to reproduce the resulting image.
880 % Note that this not easy, and may require the expansion of the bounds
881 % of previous frame, simply clear pixels for the next animation frame to
882 % transparency according to the selected dispose method.
884 % The format of the OptimizeLayerFrames method is:
886 % Image *OptimizeLayerFrames(const Image *image,
887 % const LayerMethod method, ExceptionInfo *exception)
889 % A description of each parameter follows:
891 % o image: the image.
893 % o method: the layers technique to optimize with. Must be one of...
894 % OptimizeImageLayer, or OptimizePlusLayer. The Plus form allows
895 % the addition of extra 'zero delay' frames to clear pixels from
896 % the previous frame, and the removal of frames that done change,
897 % merging the delay times together.
899 % o exception: return any errors or warnings in this structure.
903 Define a 'fake' dispose method where the frame is duplicated, (for
904 OptimizePlusLayer) with a extra zero time delay frame which does a
905 BackgroundDisposal to clear the pixels that need to be cleared.
907 #define DupDispose ((DisposeType)9)
909 Another 'fake' dispose method used to removed frames that don't change.
911 #define DelDispose ((DisposeType)8)
913 #define DEBUG_OPT_FRAME 0
915 static Image *OptimizeLayerFrames(const Image *image,
916 const LayerMethod method, ExceptionInfo *exception)
947 assert(image != (const Image *) NULL);
948 assert(image->signature == MagickCoreSignature);
949 if (image->debug != MagickFalse)
950 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
951 assert(exception != (ExceptionInfo *) NULL);
952 assert(exception->signature == MagickCoreSignature);
953 assert(method == OptimizeLayer ||
954 method == OptimizeImageLayer ||
955 method == OptimizePlusLayer);
957 Are we allowed to add/remove frames from animation?
959 add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
961 Ensure all the images are the same size.
963 curr=GetFirstImageInList(image);
964 for (; curr != (Image *) NULL; curr=GetNextImageInList(curr))
966 if ((curr->columns != image->columns) || (curr->rows != image->rows))
967 ThrowImageException(OptionError,"ImagesAreNotTheSameSize");
969 FUTURE: also check that image is also fully coalesced (full page)
970 Though as long as they are the same size it should not matter.
974 Allocate memory (times 2 if we allow the use of frame duplications)
976 curr=GetFirstImageInList(image);
977 bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
978 GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)*
980 if (bounds == (RectangleInfo *) NULL)
981 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
982 disposals=(DisposeType *) AcquireQuantumMemory((size_t)
983 GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
985 if (disposals == (DisposeType *) NULL)
987 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
988 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
991 Initialise Previous Image as fully transparent
993 prev_image=CloneImage(curr,curr->page.width,curr->page.height,
994 MagickTrue,exception);
995 if (prev_image == (Image *) NULL)
997 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
998 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
999 return((Image *) NULL);
1001 prev_image->page=curr->page; /* ERROR: <-- should not be need, but is! */
1002 prev_image->page.x=0;
1003 prev_image->page.y=0;
1004 prev_image->dispose=NoneDispose;
1006 prev_image->background_color.alpha=(Quantum) TransparentAlpha;
1007 (void) SetImageBackgroundColor(prev_image,exception);
1009 Figure out the area of overlay of the first frame
1010 No pixel could be cleared as all pixels are already cleared.
1014 (void) FormatLocaleFile(stderr, "frame %.20g :-\n", (double) i);
1016 disposals[0]=NoneDispose;
1017 bounds[0]=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
1019 (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g\n\n",
1020 (double) bounds[i].width,(double) bounds[i].height,
1021 (double) bounds[i].x,(double) bounds[i].y );
1024 Compute the bounding box of changes for each pair of images.
1027 bgnd_image=(Image *) NULL;
1028 dup_image=(Image *) NULL;
1030 dup_bounds.height=0;
1033 curr=GetNextImageInList(curr);
1034 for ( ; curr != (const Image *) NULL; curr=GetNextImageInList(curr))
1037 (void) FormatLocaleFile(stderr, "frame %.20g :-\n", (double) i);
1040 Assume none disposal is the best
1042 bounds[i]=CompareImagesBounds(curr->previous,curr,CompareAnyLayer,exception);
1043 cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception);
1044 disposals[i-1]=NoneDispose;
1046 (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g%s%s\n",
1047 (double) bounds[i].width,(double) bounds[i].height,
1048 (double) bounds[i].x,(double) bounds[i].y,
1049 bounds[i].x < 0?" (unchanged)":"",
1050 cleared?" (pixels cleared)":"");
1052 if ( bounds[i].x < 0 ) {
1054 Image frame is exactly the same as the previous frame!
1055 If not adding frames leave it to be cropped down to a null image.
1056 Otherwise mark previous image for deleted, transfering its crop bounds
1057 to the current image.
1059 if ( add_frames && i>=2 ) {
1060 disposals[i-1]=DelDispose;
1061 disposals[i]=NoneDispose;
1062 bounds[i]=bounds[i-1];
1070 Compare a none disposal against a previous disposal
1072 try_bounds=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
1073 try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception);
1075 (void) FormatLocaleFile(stderr, "test_prev: %.20gx%.20g%+.20g%+.20g%s\n",
1076 (double) try_bounds.width,(double) try_bounds.height,
1077 (double) try_bounds.x,(double) try_bounds.y,
1078 try_cleared?" (pixels were cleared)":"");
1080 if ( (!try_cleared && cleared ) ||
1081 try_bounds.width * try_bounds.height
1082 < bounds[i].width * bounds[i].height )
1084 cleared=try_cleared;
1085 bounds[i]=try_bounds;
1086 disposals[i-1]=PreviousDispose;
1088 (void) FormatLocaleFile(stderr, "previous: accepted\n");
1090 (void) FormatLocaleFile(stderr, "previous: rejected\n");
1095 If we are allowed lets try a complex frame duplication.
1096 It is useless if the previous image already clears pixels correctly.
1097 This method will always clear all the pixels that need to be cleared.
1099 dup_bounds.width=dup_bounds.height=0; /* no dup, no pixel added */
1102 dup_image=CloneImage(curr->previous,curr->previous->page.width,
1103 curr->previous->page.height,MagickTrue,exception);
1104 if (dup_image == (Image *) NULL)
1106 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1107 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1108 prev_image=DestroyImage(prev_image);
1109 return((Image *) NULL);
1111 dup_bounds=CompareImagesBounds(dup_image,curr,CompareClearLayer,exception);
1112 ClearBounds(dup_image,&dup_bounds,exception);
1113 try_bounds=CompareImagesBounds(dup_image,curr,CompareAnyLayer,exception);
1115 dup_bounds.width*dup_bounds.height
1116 +try_bounds.width*try_bounds.height
1117 < bounds[i].width * bounds[i].height )
1119 cleared=MagickFalse;
1120 bounds[i]=try_bounds;
1121 disposals[i-1]=DupDispose;
1122 /* to be finalised later, if found to be optimial */
1125 dup_bounds.width=dup_bounds.height=0;
1128 Now compare against a simple background disposal
1130 bgnd_image=CloneImage(curr->previous,curr->previous->page.width,
1131 curr->previous->page.height,MagickTrue,exception);
1132 if (bgnd_image == (Image *) NULL)
1134 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1135 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1136 prev_image=DestroyImage(prev_image);
1137 if ( dup_image != (Image *) NULL)
1138 dup_image=DestroyImage(dup_image);
1139 return((Image *) NULL);
1141 bgnd_bounds=bounds[i-1]; /* interum bounds of the previous image */
1142 ClearBounds(bgnd_image,&bgnd_bounds,exception);
1143 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
1144 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1146 (void) FormatLocaleFile(stderr, "background: %s\n",
1147 try_cleared?"(pixels cleared)":"");
1152 Straight background disposal failed to clear pixels needed!
1153 Lets try expanding the disposal area of the previous frame, to
1154 include the pixels that are cleared. This guaranteed
1155 to work, though may not be the most optimized solution.
1157 try_bounds=CompareImagesBounds(curr->previous,curr,CompareClearLayer,exception);
1159 (void) FormatLocaleFile(stderr, "expand_clear: %.20gx%.20g%+.20g%+.20g%s\n",
1160 (double) try_bounds.width,(double) try_bounds.height,
1161 (double) try_bounds.x,(double) try_bounds.y,
1162 try_bounds.x<0?" (no expand nessary)":"");
1164 if ( bgnd_bounds.x < 0 )
1165 bgnd_bounds = try_bounds;
1169 (void) FormatLocaleFile(stderr, "expand_bgnd: %.20gx%.20g%+.20g%+.20g\n",
1170 (double) bgnd_bounds.width,(double) bgnd_bounds.height,
1171 (double) bgnd_bounds.x,(double) bgnd_bounds.y );
1173 if ( try_bounds.x < bgnd_bounds.x )
1175 bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x;
1176 if ( bgnd_bounds.width < try_bounds.width )
1177 bgnd_bounds.width = try_bounds.width;
1178 bgnd_bounds.x = try_bounds.x;
1182 try_bounds.width += try_bounds.x - bgnd_bounds.x;
1183 if ( bgnd_bounds.width < try_bounds.width )
1184 bgnd_bounds.width = try_bounds.width;
1186 if ( try_bounds.y < bgnd_bounds.y )
1188 bgnd_bounds.height += bgnd_bounds.y - try_bounds.y;
1189 if ( bgnd_bounds.height < try_bounds.height )
1190 bgnd_bounds.height = try_bounds.height;
1191 bgnd_bounds.y = try_bounds.y;
1195 try_bounds.height += try_bounds.y - bgnd_bounds.y;
1196 if ( bgnd_bounds.height < try_bounds.height )
1197 bgnd_bounds.height = try_bounds.height;
1200 (void) FormatLocaleFile(stderr, " to : %.20gx%.20g%+.20g%+.20g\n",
1201 (double) bgnd_bounds.width,(double) bgnd_bounds.height,
1202 (double) bgnd_bounds.x,(double) bgnd_bounds.y );
1205 ClearBounds(bgnd_image,&bgnd_bounds,exception);
1207 /* Something strange is happening with a specific animation
1208 * CompareAnyLayers (normal method) and CompareClearLayers returns the whole
1209 * image, which is not posibly correct! As verified by previous tests.
1210 * Something changed beyond the bgnd_bounds clearing. But without being able
1211 * to see, or writet he image at this point it is hard to tell what is wrong!
1212 * Only CompareOverlay seemed to return something sensible.
1214 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareClearLayer,exception);
1215 (void) FormatLocaleFile(stderr, "expand_ctst: %.20gx%.20g%+.20g%+.20g\n",
1216 (double) try_bounds.width,(double) try_bounds.height,
1217 (double) try_bounds.x,(double) try_bounds.y );
1218 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
1219 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1220 (void) FormatLocaleFile(stderr, "expand_any : %.20gx%.20g%+.20g%+.20g%s\n",
1221 (double) try_bounds.width,(double) try_bounds.height,
1222 (double) try_bounds.x,(double) try_bounds.y,
1223 try_cleared?" (pixels cleared)":"");
1225 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareOverlayLayer,exception);
1227 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1228 (void) FormatLocaleFile(stderr, "expand_test: %.20gx%.20g%+.20g%+.20g%s\n",
1229 (double) try_bounds.width,(double) try_bounds.height,
1230 (double) try_bounds.x,(double) try_bounds.y,
1231 try_cleared?" (pixels cleared)":"");
1235 Test if this background dispose is smaller than any of the
1236 other methods we tryed before this (including duplicated frame)
1239 bgnd_bounds.width*bgnd_bounds.height
1240 +try_bounds.width*try_bounds.height
1241 < bounds[i-1].width*bounds[i-1].height
1242 +dup_bounds.width*dup_bounds.height
1243 +bounds[i].width*bounds[i].height )
1245 cleared=MagickFalse;
1246 bounds[i-1]=bgnd_bounds;
1247 bounds[i]=try_bounds;
1248 if ( disposals[i-1] == DupDispose )
1249 dup_image=DestroyImage(dup_image);
1250 disposals[i-1]=BackgroundDispose;
1252 (void) FormatLocaleFile(stderr, "expand_bgnd: accepted\n");
1254 (void) FormatLocaleFile(stderr, "expand_bgnd: reject\n");
1259 Finalise choice of dispose, set new prev_image,
1260 and junk any extra images as appropriate,
1262 if ( disposals[i-1] == DupDispose )
1264 if (bgnd_image != (Image *) NULL)
1265 bgnd_image=DestroyImage(bgnd_image);
1266 prev_image=DestroyImage(prev_image);
1267 prev_image=dup_image, dup_image=(Image *) NULL;
1268 bounds[i+1]=bounds[i];
1269 bounds[i]=dup_bounds;
1270 disposals[i-1]=DupDispose;
1271 disposals[i]=BackgroundDispose;
1276 if ( dup_image != (Image *) NULL)
1277 dup_image=DestroyImage(dup_image);
1278 if ( disposals[i-1] != PreviousDispose )
1279 prev_image=DestroyImage(prev_image);
1280 if ( disposals[i-1] == BackgroundDispose )
1281 prev_image=bgnd_image, bgnd_image=(Image *) NULL;
1282 if (bgnd_image != (Image *) NULL)
1283 bgnd_image=DestroyImage(bgnd_image);
1284 if ( disposals[i-1] == NoneDispose )
1286 prev_image=CloneImage(curr->previous,curr->previous->page.width,
1287 curr->previous->page.height,MagickTrue,exception);
1288 if (prev_image == (Image *) NULL)
1290 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1291 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1292 return((Image *) NULL);
1297 assert(prev_image != (Image *) NULL);
1298 disposals[i]=disposals[i-1];
1300 (void) FormatLocaleFile(stderr, "final %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1302 CommandOptionToMnemonic(MagickDisposeOptions, disposals[i-1]),
1303 (double) bounds[i-1].width, (double) bounds[i-1].height,
1304 (double) bounds[i-1].x, (double) bounds[i-1].y );
1307 (void) FormatLocaleFile(stderr, "interum %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1309 CommandOptionToMnemonic(MagickDisposeOptions, disposals[i]),
1310 (double) bounds[i].width, (double) bounds[i].height,
1311 (double) bounds[i].x, (double) bounds[i].y );
1312 (void) FormatLocaleFile(stderr, "\n");
1316 prev_image=DestroyImage(prev_image);
1318 Optimize all images in sequence.
1320 sans_exception=AcquireExceptionInfo();
1322 curr=GetFirstImageInList(image);
1323 optimized_image=NewImageList();
1324 while ( curr != (const Image *) NULL )
1326 prev_image=CloneImage(curr,0,0,MagickTrue,exception);
1327 if (prev_image == (Image *) NULL)
1329 if ( disposals[i] == DelDispose ) {
1331 while ( disposals[i] == DelDispose ) {
1332 time += curr->delay*1000/curr->ticks_per_second;
1333 curr=GetNextImageInList(curr);
1336 time += curr->delay*1000/curr->ticks_per_second;
1337 prev_image->ticks_per_second = 100L;
1338 prev_image->delay = time*prev_image->ticks_per_second/1000;
1340 bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
1341 prev_image=DestroyImage(prev_image);
1342 if (bgnd_image == (Image *) NULL)
1344 bgnd_image->dispose=disposals[i];
1345 if ( disposals[i] == DupDispose ) {
1346 bgnd_image->delay=0;
1347 bgnd_image->dispose=NoneDispose;
1350 curr=GetNextImageInList(curr);
1351 AppendImageToList(&optimized_image,bgnd_image);
1354 sans_exception=DestroyExceptionInfo(sans_exception);
1355 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1356 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1357 if (curr != (Image *) NULL)
1359 optimized_image=DestroyImageList(optimized_image);
1360 return((Image *) NULL);
1362 return(GetFirstImageInList(optimized_image));
1366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1370 % O p t i m i z e I m a g e L a y e r s %
1374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1376 % OptimizeImageLayers() compares each image the GIF disposed forms of the
1377 % previous image in the sequence. From this it attempts to select the
1378 % smallest cropped image to replace each frame, while preserving the results
1379 % of the GIF animation.
1381 % The format of the OptimizeImageLayers method is:
1383 % Image *OptimizeImageLayers(const Image *image,
1384 % ExceptionInfo *exception)
1386 % A description of each parameter follows:
1388 % o image: the image.
1390 % o exception: return any errors or warnings in this structure.
1393 MagickExport Image *OptimizeImageLayers(const Image *image,
1394 ExceptionInfo *exception)
1396 return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
1400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1404 % O p t i m i z e P l u s I m a g e L a y e r s %
1408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1410 % OptimizeImagePlusLayers() is exactly as OptimizeImageLayers(), but may
1411 % also add or even remove extra frames in the animation, if it improves
1412 % the total number of pixels in the resulting GIF animation.
1414 % The format of the OptimizePlusImageLayers method is:
1416 % Image *OptimizePlusImageLayers(const Image *image,
1417 % ExceptionInfo *exception)
1419 % A description of each parameter follows:
1421 % o image: the image.
1423 % o exception: return any errors or warnings in this structure.
1426 MagickExport Image *OptimizePlusImageLayers(const Image *image,
1427 ExceptionInfo *exception)
1429 return OptimizeLayerFrames(image, OptimizePlusLayer, exception);
1433 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437 % 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 %
1441 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1443 % OptimizeImageTransparency() takes a frame optimized GIF animation, and
1444 % compares the overlayed pixels against the disposal image resulting from all
1445 % the previous frames in the animation. Any pixel that does not change the
1446 % disposal image (and thus does not effect the outcome of an overlay) is made
1449 % WARNING: This modifies the current images directly, rather than generate
1450 % a new image sequence.
1452 % The format of the OptimizeImageTransperency method is:
1454 % void OptimizeImageTransperency(Image *image,ExceptionInfo *exception)
1456 % A description of each parameter follows:
1458 % o image: the image sequence
1460 % o exception: return any errors or warnings in this structure.
1463 MagickExport void OptimizeImageTransparency(const Image *image,
1464 ExceptionInfo *exception)
1473 Run the image through the animation sequence
1475 assert(image != (Image *) NULL);
1476 assert(image->signature == MagickCoreSignature);
1477 if (image->debug != MagickFalse)
1478 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1479 assert(exception != (ExceptionInfo *) NULL);
1480 assert(exception->signature == MagickCoreSignature);
1481 next=GetFirstImageInList(image);
1482 dispose_image=CloneImage(next,next->page.width,next->page.height,
1483 MagickTrue,exception);
1484 if (dispose_image == (Image *) NULL)
1486 dispose_image->page=next->page;
1487 dispose_image->page.x=0;
1488 dispose_image->page.y=0;
1489 dispose_image->dispose=NoneDispose;
1490 dispose_image->background_color.alpha=(Quantum) TransparentAlpha;
1491 (void) SetImageBackgroundColor(dispose_image,exception);
1493 while ( next != (Image *) NULL )
1499 Overlay this frame's image over the previous disposal image
1501 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
1502 if (current_image == (Image *) NULL)
1504 dispose_image=DestroyImage(dispose_image);
1507 (void) CompositeImage(current_image,next,next->alpha_trait != UndefinedPixelTrait ?
1508 OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
1511 At this point the image would be displayed, for the delay period
1513 Work out the disposal of the previous image
1515 if (next->dispose == BackgroundDispose)
1520 bounds.width=next->columns;
1521 bounds.height=next->rows;
1524 bounds.width+=bounds.x;
1527 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
1528 bounds.width=current_image->columns-bounds.x;
1531 bounds.height+=bounds.y;
1534 if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
1535 bounds.height=current_image->rows-bounds.y;
1536 ClearBounds(current_image, &bounds,exception);
1538 if (next->dispose != PreviousDispose)
1540 dispose_image=DestroyImage(dispose_image);
1541 dispose_image=current_image;
1544 current_image=DestroyImage(current_image);
1547 Optimize Transparency of the next frame (if present)
1549 next=GetNextImageInList(next);
1550 if (next != (Image *) NULL) {
1551 (void) CompositeImage(next,dispose_image,ChangeMaskCompositeOp,
1552 MagickTrue,-(next->page.x),-(next->page.y),exception);
1555 dispose_image=DestroyImage(dispose_image);
1560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1564 % R e m o v e D u p l i c a t e L a y e r s %
1568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1570 % RemoveDuplicateLayers() removes any image that is exactly the same as the
1571 % next image in the given image list. Image size and virtual canvas offset
1572 % must also match, though not the virtual canvas size itself.
1574 % No check is made with regards to image disposal setting, though it is the
1575 % dispose setting of later image that is kept. Also any time delays are also
1576 % added together. As such coalesced image animations should still produce the
1577 % same result, though with duplicte frames merged into a single frame.
1579 % The format of the RemoveDuplicateLayers method is:
1581 % void RemoveDuplicateLayers(Image **image, ExceptionInfo *exception)
1583 % A description of each parameter follows:
1585 % o images: the image list
1587 % o exception: return any errors or warnings in this structure.
1590 MagickExport void RemoveDuplicateLayers(Image **images,
1591 ExceptionInfo *exception)
1600 assert((*images) != (const Image *) NULL);
1601 assert((*images)->signature == MagickCoreSignature);
1602 if ((*images)->debug != MagickFalse)
1603 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
1604 assert(exception != (ExceptionInfo *) NULL);
1605 assert(exception->signature == MagickCoreSignature);
1607 curr=GetFirstImageInList(*images);
1608 for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next)
1610 if ( curr->columns != next->columns || curr->rows != next->rows
1611 || curr->page.x != next->page.x || curr->page.y != next->page.y )
1613 bounds=CompareImagesBounds(curr,next,CompareAnyLayer,exception);
1614 if ( bounds.x < 0 ) {
1616 the two images are the same, merge time delays and delete one.
1619 time = curr->delay*1000/curr->ticks_per_second;
1620 time += next->delay*1000/next->ticks_per_second;
1621 next->ticks_per_second = 100L;
1622 next->delay = time*curr->ticks_per_second/1000;
1623 next->iterations = curr->iterations;
1625 (void) DeleteImageFromList(images);
1628 *images = GetFirstImageInList(*images);
1632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1636 % R e m o v e Z e r o D e l a y L a y e r s %
1640 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1642 % RemoveZeroDelayLayers() removes any image that as a zero delay time. Such
1643 % images generally represent intermediate or partial updates in GIF
1644 % animations used for file optimization. They are not ment to be displayed
1645 % to users of the animation. Viewable images in an animation should have a
1646 % time delay of 3 or more centi-seconds (hundredths of a second).
1648 % However if all the frames have a zero time delay, then either the animation
1649 % is as yet incomplete, or it is not a GIF animation. This a non-sensible
1650 % situation, so no image will be removed and a 'Zero Time Animation' warning
1651 % (exception) given.
1653 % No warning will be given if no image was removed because all images had an
1654 % appropriate non-zero time delay set.
1656 % Due to the special requirements of GIF disposal handling, GIF animations
1657 % should be coalesced first, before calling this function, though that is not
1660 % The format of the RemoveZeroDelayLayers method is:
1662 % void RemoveZeroDelayLayers(Image **image, ExceptionInfo *exception)
1664 % A description of each parameter follows:
1666 % o images: the image list
1668 % o exception: return any errors or warnings in this structure.
1671 MagickExport void RemoveZeroDelayLayers(Image **images,
1672 ExceptionInfo *exception)
1677 assert((*images) != (const Image *) NULL);
1678 assert((*images)->signature == MagickCoreSignature);
1679 if ((*images)->debug != MagickFalse)
1680 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
1681 assert(exception != (ExceptionInfo *) NULL);
1682 assert(exception->signature == MagickCoreSignature);
1684 i=GetFirstImageInList(*images);
1685 for ( ; i != (Image *) NULL; i=GetNextImageInList(i))
1686 if ( i->delay != 0L ) break;
1687 if ( i == (Image *) NULL ) {
1688 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1689 "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename);
1692 i=GetFirstImageInList(*images);
1693 while ( i != (Image *) NULL )
1695 if ( i->delay == 0L ) {
1696 (void) DeleteImageFromList(&i);
1700 i=GetNextImageInList(i);
1702 *images=GetFirstImageInList(*images);
1706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1710 % C o m p o s i t e L a y e r s %
1714 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716 % CompositeLayers() compose the source image sequence over the destination
1717 % image sequence, starting with the current image in both lists.
1719 % Each layer from the two image lists are composted together until the end of
1720 % one of the image lists is reached. The offset of each composition is also
1721 % adjusted to match the virtual canvas offsets of each layer. As such the
1722 % given offset is relative to the virtual canvas, and not the actual image.
1724 % Composition uses given x and y offsets, as the 'origin' location of the
1725 % source images virtual canvas (not the real image) allowing you to compose a
1726 % list of 'layer images' into the destiantioni images. This makes it well
1727 % sutiable for directly composing 'Clears Frame Animations' or 'Coaleased
1728 % Animations' onto a static or other 'Coaleased Animation' destination image
1729 % list. GIF disposal handling is not looked at.
1731 % Special case:- If one of the image sequences is the last image (just a
1732 % single image remaining), that image is repeatally composed with all the
1733 % images in the other image list. Either the source or destination lists may
1734 % be the single image, for this situation.
1736 % In the case of a single destination image (or last image given), that image
1737 % will ve cloned to match the number of images remaining in the source image
1740 % This is equivelent to the "-layer Composite" Shell API operator.
1743 % The format of the CompositeLayers method is:
1745 % void CompositeLayers(Image *destination, const CompositeOperator
1746 % compose, Image *source, const ssize_t x_offset, const ssize_t y_offset,
1747 % ExceptionInfo *exception);
1749 % A description of each parameter follows:
1751 % o destination: the destination images and results
1753 % o source: source image(s) for the layer composition
1755 % o compose, x_offset, y_offset: arguments passed on to CompositeImages()
1757 % o exception: return any errors or warnings in this structure.
1761 static inline void CompositeCanvas(Image *destination,
1762 const CompositeOperator compose,Image *source,ssize_t x_offset,
1763 ssize_t y_offset,ExceptionInfo *exception)
1765 x_offset+=source->page.x-destination->page.x;
1766 y_offset+=source->page.y-destination->page.y;
1767 (void) CompositeImage(destination,source,compose,MagickTrue,x_offset,
1768 y_offset,exception);
1771 MagickExport void CompositeLayers(Image *destination,
1772 const CompositeOperator compose, Image *source,const ssize_t x_offset,
1773 const ssize_t y_offset,ExceptionInfo *exception)
1775 assert(destination != (Image *) NULL);
1776 assert(destination->signature == MagickCoreSignature);
1777 assert(source != (Image *) NULL);
1778 assert(source->signature == MagickCoreSignature);
1779 assert(exception != (ExceptionInfo *) NULL);
1780 assert(exception->signature == MagickCoreSignature);
1781 if (source->debug != MagickFalse || destination->debug != MagickFalse)
1782 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
1783 source->filename, destination->filename);
1786 Overlay single source image over destation image/list
1788 if ( source->next == (Image *) NULL )
1789 while ( destination != (Image *) NULL )
1791 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1793 destination=GetNextImageInList(destination);
1797 Overlay source image list over single destination.
1798 Multiple clones of destination image are created to match source list.
1799 Original Destination image becomes first image of generated list.
1800 As such the image list pointer does not require any change in caller.
1801 Some animation attributes however also needs coping in this case.
1803 else if ( destination->next == (Image *) NULL )
1805 Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
1807 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1809 /* copy source image attributes ? */
1810 if ( source->next != (Image *) NULL )
1812 destination->delay = source->delay;
1813 destination->iterations = source->iterations;
1815 source=GetNextImageInList(source);
1817 while ( source != (Image *) NULL )
1819 AppendImageToList(&destination,
1820 CloneImage(dest,0,0,MagickTrue,exception));
1821 destination=GetLastImageInList(destination);
1823 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1825 destination->delay = source->delay;
1826 destination->iterations = source->iterations;
1827 source=GetNextImageInList(source);
1829 dest=DestroyImage(dest);
1833 Overlay a source image list over a destination image list
1834 until either list runs out of images. (Does not repeat)
1837 while ( source != (Image *) NULL && destination != (Image *) NULL )
1839 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1841 source=GetNextImageInList(source);
1842 destination=GetNextImageInList(destination);
1847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1851 % M e r g e I m a g e L a y e r s %
1855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1857 % MergeImageLayers() composes all the image layers from the current given
1858 % image onward to produce a single image of the merged layers.
1860 % The inital canvas's size depends on the given LayerMethod, and is
1861 % initialized using the first images background color. The images
1862 % are then compositied onto that image in sequence using the given
1863 % composition that has been assigned to each individual image.
1865 % The format of the MergeImageLayers is:
1867 % Image *MergeImageLayers(const Image *image,
1868 % const LayerMethod method, ExceptionInfo *exception)
1870 % A description of each parameter follows:
1872 % o image: the image list to be composited together
1874 % o method: the method of selecting the size of the initial canvas.
1876 % MergeLayer: Merge all layers onto a canvas just large enough
1877 % to hold all the actual images. The virtual canvas of the
1878 % first image is preserved but otherwise ignored.
1880 % FlattenLayer: Use the virtual canvas size of first image.
1881 % Images which fall outside this canvas is clipped.
1882 % This can be used to 'fill out' a given virtual canvas.
1884 % MosaicLayer: Start with the virtual canvas of the first image,
1885 % enlarging left and right edges to contain all images.
1886 % Images with negative offsets will be clipped.
1888 % TrimBoundsLayer: Determine the overall bounds of all the image
1889 % layers just as in "MergeLayer", then adjust the the canvas
1890 % and offsets to be relative to those bounds, without overlaying
1893 % WARNING: a new image is not returned, the original image
1894 % sequence page data is modified instead.
1896 % o exception: return any errors or warnings in this structure.
1899 MagickExport Image *MergeImageLayers(Image *image,const LayerMethod method,
1900 ExceptionInfo *exception)
1902 #define MergeLayersTag "Merge/Layers"
1913 register const Image
1924 assert(image != (Image *) NULL);
1925 assert(image->signature == MagickCoreSignature);
1926 if (image->debug != MagickFalse)
1927 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1928 assert(exception != (ExceptionInfo *) NULL);
1929 assert(exception->signature == MagickCoreSignature);
1931 Determine canvas image size, and its virtual canvas size and offset
1934 width=image->columns;
1938 case TrimBoundsLayer:
1942 next=GetNextImageInList(image);
1943 for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
1945 if (page.x > next->page.x)
1947 width+=page.x-next->page.x;
1948 page.x=next->page.x;
1950 if (page.y > next->page.y)
1952 height+=page.y-next->page.y;
1953 page.y=next->page.y;
1955 if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x))
1956 width=(size_t) next->page.x+(ssize_t) next->columns-page.x;
1957 if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y))
1958 height=(size_t) next->page.y+(ssize_t) next->rows-page.y;
1966 if (page.height > 0)
1976 if (page.height > 0)
1978 for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
1980 if (method == MosaicLayer)
1982 page.x=next->page.x;
1983 page.y=next->page.y;
1984 if ((ssize_t) width < (next->page.x+(ssize_t) next->columns))
1985 width=(size_t) next->page.x+next->columns;
1986 if ((ssize_t) height < (next->page.y+(ssize_t) next->rows))
1987 height=(size_t) next->page.y+next->rows;
1998 Set virtual canvas size if not defined.
2000 if (page.width == 0)
2001 page.width=page.x < 0 ? width : width+page.x;
2002 if (page.height == 0)
2003 page.height=page.y < 0 ? height : height+page.y;
2005 Handle "TrimBoundsLayer" method separately to normal 'layer merge'.
2007 if (method == TrimBoundsLayer)
2009 number_images=GetImageListLength(image);
2010 for (scene=0; scene < (ssize_t) number_images; scene++)
2012 image->page.x-=page.x;
2013 image->page.y-=page.y;
2014 image->page.width=width;
2015 image->page.height=height;
2016 proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2018 if (proceed == MagickFalse)
2020 image=GetNextImageInList(image);
2021 if (image == (Image *) NULL)
2024 return((Image *) NULL);
2027 Create canvas size of width and height, and background color.
2029 canvas=CloneImage(image,width,height,MagickTrue,exception);
2030 if (canvas == (Image *) NULL)
2031 return((Image *) NULL);
2032 (void) SetImageBackgroundColor(canvas,exception);
2034 canvas->dispose=UndefinedDispose;
2036 Compose images onto canvas, with progress monitor
2038 number_images=GetImageListLength(image);
2039 for (scene=0; scene < (ssize_t) number_images; scene++)
2041 (void) CompositeImage(canvas,image,image->compose,MagickTrue,image->page.x-
2042 canvas->page.x,image->page.y-canvas->page.y,exception);
2043 proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2045 if (proceed == MagickFalse)
2047 image=GetNextImageInList(image);
2048 if (image == (Image *) NULL)