• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                     L       AAA   Y   Y  EEEEE  RRRR                        %
6 %                     L      A   A   Y Y   E      R   R                       %
7 %                     L      AAAAA    Y    EEE    RRRR                        %
8 %                     L      A   A    Y    E      R R                         %
9 %                     LLLLL  A   A    Y    EEEEE  R  R                        %
10 %                                                                             %
11 %                      MagickCore Image Layering Methods                      %
12 %                                                                             %
13 %                              Software Design                                %
14 %                                   Cristy                                    %
15 %                              Anthony Thyssen                                %
16 %                               January 2006                                  %
17 %                                                                             %
18 %                                                                             %
19 %  Copyright 1999-2020 ImageMagick Studio LLC, a non-profit organization      %
20 %  dedicated to making software imaging solutions freely available.           %
21 %                                                                             %
22 %  You may not use this file except in compliance with the License.  You may  %
23 %  obtain a copy of the License at                                            %
24 %                                                                             %
25 %    https://imagemagick.org/script/license.php                               %
26 %                                                                             %
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.                                             %
32 %                                                                             %
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34 %
35 */
36 
37 /*
38   Include declarations.
39 */
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"
66 
67 /*
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
69 %                                                                             %
70 %                                                                             %
71 %                                                                             %
72 +     C l e a r B o u n d s                                                   %
73 %                                                                             %
74 %                                                                             %
75 %                                                                             %
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 %
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.
81 %
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
84 %  within the image.
85 %
86 %  The format is:
87 %
88 %      void ClearBounds(Image *image,RectangleInfo *bounds,
89 %        ExceptionInfo *exception)
90 %
91 %  A description of each parameter follows:
92 %
93 %    o image: the image to had the area cleared in
94 %
95 %    o bounds: the area to be clear within the imag image
96 %
97 %    o exception: return any errors or warnings in this structure.
98 %
99 */
ClearBounds(Image * image,RectangleInfo * bounds,ExceptionInfo * exception)100 static void ClearBounds(Image *image,RectangleInfo *bounds,
101   ExceptionInfo *exception)
102 {
103   ssize_t
104     y;
105 
106   if (bounds->x < 0)
107     return;
108   if (image->alpha_trait == UndefinedPixelTrait)
109     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
110   for (y=0; y < (ssize_t) bounds->height; y++)
111   {
112     register ssize_t
113       x;
114 
115     register Quantum
116       *magick_restrict q;
117 
118     q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
119     if (q == (Quantum *) NULL)
120       break;
121     for (x=0; x < (ssize_t) bounds->width; x++)
122     {
123       SetPixelAlpha(image,TransparentAlpha,q);
124       q+=GetPixelChannels(image);
125     }
126     if (SyncAuthenticPixels(image,exception) == MagickFalse)
127       break;
128   }
129 }
130 
131 /*
132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133 %                                                                             %
134 %                                                                             %
135 %                                                                             %
136 +     I s B o u n d s C l e a r e d                                           %
137 %                                                                             %
138 %                                                                             %
139 %                                                                             %
140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141 %
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.
146 %
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
149 %  within the image.
150 %
151 %  The format is:
152 %
153 %      MagickBooleanType IsBoundsCleared(const Image *image1,
154 %        const Image *image2,RectangleInfo bounds,ExceptionInfo *exception)
155 %
156 %  A description of each parameter follows:
157 %
158 %    o image1, image 2: the images to check for cleared pixels
159 %
160 %    o bounds: the area to be clear within the imag image
161 %
162 %    o exception: return any errors or warnings in this structure.
163 %
164 */
IsBoundsCleared(const Image * image1,const Image * image2,RectangleInfo * bounds,ExceptionInfo * exception)165 static MagickBooleanType IsBoundsCleared(const Image *image1,
166   const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception)
167 {
168   register const Quantum
169     *p,
170     *q;
171 
172   register ssize_t
173     x;
174 
175   ssize_t
176     y;
177 
178   if (bounds->x < 0)
179     return(MagickFalse);
180   for (y=0; y < (ssize_t) bounds->height; y++)
181   {
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))
185       break;
186     for (x=0; x < (ssize_t) bounds->width; x++)
187     {
188       if ((GetPixelAlpha(image1,p) >= (Quantum) (QuantumRange/2)) &&
189           (GetPixelAlpha(image2,q) < (Quantum) (QuantumRange/2)))
190         break;
191       p+=GetPixelChannels(image1);
192       q+=GetPixelChannels(image2);
193     }
194     if (x < (ssize_t) bounds->width)
195       break;
196   }
197   return(y < (ssize_t) bounds->height ? MagickTrue : MagickFalse);
198 }
199 
200 /*
201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
202 %                                                                             %
203 %                                                                             %
204 %                                                                             %
205 %     C o a l e s c e I m a g e s                                             %
206 %                                                                             %
207 %                                                                             %
208 %                                                                             %
209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
210 %
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.
217 %
218 %  The format of the CoalesceImages method is:
219 %
220 %      Image *CoalesceImages(Image *image,ExceptionInfo *exception)
221 %
222 %  A description of each parameter follows:
223 %
224 %    o image: the image sequence.
225 %
226 %    o exception: return any errors or warnings in this structure.
227 %
228 */
CoalesceImages(const Image * image,ExceptionInfo * exception)229 MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
230 {
231   Image
232     *coalesce_image,
233     *dispose_image,
234     *previous;
235 
236   register Image
237     *next;
238 
239   RectangleInfo
240     bounds;
241 
242   /*
243     Coalesce the image sequence.
244   */
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);
252   bounds=next->page;
253   if (bounds.width == 0)
254     {
255       bounds.width=next->columns;
256       if (bounds.x > 0)
257         bounds.width+=bounds.x;
258     }
259   if (bounds.height == 0)
260     {
261       bounds.height=next->rows;
262       if (bounds.y > 0)
263         bounds.height+=bounds.y;
264     }
265   bounds.x=0;
266   bounds.y=0;
267   coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue,
268     exception);
269   if (coalesce_image == (Image *) NULL)
270     return((Image *) NULL);
271   coalesce_image->background_color.alpha_trait=BlendPixelTrait;
272   coalesce_image->background_color.alpha=(MagickRealType) TransparentAlpha;
273   (void) SetImageBackgroundColor(coalesce_image,exception);
274   coalesce_image->alpha_trait=next->alpha_trait;
275   coalesce_image->page=bounds;
276   coalesce_image->dispose=NoneDispose;
277   /*
278     Coalesce rest of the images.
279   */
280   dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
281   if (dispose_image == (Image *) NULL)
282     {
283       coalesce_image=DestroyImage(coalesce_image);
284       return((Image *) NULL);
285     }
286   dispose_image->background_color.alpha_trait=BlendPixelTrait;
287   (void) CompositeImage(coalesce_image,next,CopyCompositeOp,MagickTrue,
288     next->page.x,next->page.y,exception);
289   next=GetNextImageInList(next);
290   for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
291   {
292     /*
293       Determine the bounds that was overlaid in the previous image.
294     */
295     previous=GetPreviousImageInList(next);
296     bounds=previous->page;
297     bounds.width=previous->columns;
298     bounds.height=previous->rows;
299     if (bounds.x < 0)
300       {
301         bounds.width+=bounds.x;
302         bounds.x=0;
303       }
304     if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) coalesce_image->columns)
305       bounds.width=coalesce_image->columns-bounds.x;
306     if (bounds.y < 0)
307       {
308         bounds.height+=bounds.y;
309         bounds.y=0;
310       }
311     if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) coalesce_image->rows)
312       bounds.height=coalesce_image->rows-bounds.y;
313     /*
314       Replace the dispose image with the new coalesced image.
315     */
316     if (GetPreviousImageInList(next)->dispose != PreviousDispose)
317       {
318         dispose_image=DestroyImage(dispose_image);
319         dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
320         if (dispose_image == (Image *) NULL)
321           {
322             coalesce_image=DestroyImageList(coalesce_image);
323             return((Image *) NULL);
324           }
325         dispose_image->background_color.alpha_trait=BlendPixelTrait;
326       }
327     /*
328       Clear the overlaid area of the coalesced bounds for background disposal
329     */
330     if (next->previous->dispose == BackgroundDispose)
331       ClearBounds(dispose_image,&bounds,exception);
332     /*
333       Next image is the dispose image, overlaid with next frame in sequence.
334     */
335     coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception);
336     coalesce_image->next->previous=coalesce_image;
337     previous=coalesce_image;
338     coalesce_image=GetNextImageInList(coalesce_image);
339     coalesce_image->background_color.alpha_trait=BlendPixelTrait;
340     (void) CompositeImage(coalesce_image,next,
341       next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp,
342       MagickTrue,next->page.x,next->page.y,exception);
343     (void) CloneImageProfiles(coalesce_image,next);
344     (void) CloneImageProperties(coalesce_image,next);
345     (void) CloneImageArtifacts(coalesce_image,next);
346     coalesce_image->page=previous->page;
347     /*
348       If a pixel goes opaque to transparent, use background dispose.
349     */
350     if (IsBoundsCleared(previous,coalesce_image,&bounds,exception) != MagickFalse)
351       coalesce_image->dispose=BackgroundDispose;
352     else
353       coalesce_image->dispose=NoneDispose;
354     previous->dispose=coalesce_image->dispose;
355   }
356   dispose_image=DestroyImage(dispose_image);
357   return(GetFirstImageInList(coalesce_image));
358 }
359 
360 /*
361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
362 %                                                                             %
363 %                                                                             %
364 %                                                                             %
365 %     D i s p o s e I m a g e s                                               %
366 %                                                                             %
367 %                                                                             %
368 %                                                                             %
369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370 %
371 %  DisposeImages() returns the coalesced frames of a GIF animation as it would
372 %  appear after the GIF dispose method of that frame has been applied.  That is
373 %  it returned the appearance of each frame before the next is overlaid.
374 %
375 %  The format of the DisposeImages method is:
376 %
377 %      Image *DisposeImages(Image *image,ExceptionInfo *exception)
378 %
379 %  A description of each parameter follows:
380 %
381 %    o images: the image sequence.
382 %
383 %    o exception: return any errors or warnings in this structure.
384 %
385 */
DisposeImages(const Image * images,ExceptionInfo * exception)386 MagickExport Image *DisposeImages(const Image *images,ExceptionInfo *exception)
387 {
388   Image
389     *dispose_image,
390     *dispose_images;
391 
392   RectangleInfo
393     bounds;
394 
395   register Image
396     *image,
397     *next;
398 
399   /*
400     Run the image through the animation sequence
401   */
402   assert(images != (Image *) NULL);
403   assert(images->signature == MagickCoreSignature);
404   if (images->debug != MagickFalse)
405     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
406   assert(exception != (ExceptionInfo *) NULL);
407   assert(exception->signature == MagickCoreSignature);
408   image=GetFirstImageInList(images);
409   dispose_image=CloneImage(image,image->page.width,image->page.height,
410     MagickTrue,exception);
411   if (dispose_image == (Image *) NULL)
412     return((Image *) NULL);
413   dispose_image->page=image->page;
414   dispose_image->page.x=0;
415   dispose_image->page.y=0;
416   dispose_image->dispose=NoneDispose;
417   dispose_image->background_color.alpha_trait=BlendPixelTrait;
418   dispose_image->background_color.alpha=(MagickRealType) TransparentAlpha;
419   (void) SetImageBackgroundColor(dispose_image,exception);
420   dispose_images=NewImageList();
421   for (next=image; image != (Image *) NULL; image=GetNextImageInList(image))
422   {
423     Image
424       *current_image;
425 
426     /*
427       Overlay this frame's image over the previous disposal image.
428     */
429     current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
430     if (current_image == (Image *) NULL)
431       {
432         dispose_images=DestroyImageList(dispose_images);
433         dispose_image=DestroyImage(dispose_image);
434         return((Image *) NULL);
435       }
436     current_image->background_color.alpha_trait=BlendPixelTrait;
437     (void) CompositeImage(current_image,next,
438       next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp,
439       MagickTrue,next->page.x,next->page.y,exception);
440     /*
441       Handle Background dispose: image is displayed for the delay period.
442     */
443     if (next->dispose == BackgroundDispose)
444       {
445         bounds=next->page;
446         bounds.width=next->columns;
447         bounds.height=next->rows;
448         if (bounds.x < 0)
449           {
450             bounds.width+=bounds.x;
451             bounds.x=0;
452           }
453         if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
454           bounds.width=current_image->columns-bounds.x;
455         if (bounds.y < 0)
456           {
457             bounds.height+=bounds.y;
458             bounds.y=0;
459           }
460         if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
461           bounds.height=current_image->rows-bounds.y;
462         ClearBounds(current_image,&bounds,exception);
463       }
464     /*
465       Select the appropriate previous/disposed image.
466     */
467     if (next->dispose == PreviousDispose)
468       current_image=DestroyImage(current_image);
469     else
470       {
471         dispose_image=DestroyImage(dispose_image);
472         dispose_image=current_image;
473         current_image=(Image *) NULL;
474       }
475     /*
476       Save the dispose image just calculated for return.
477     */
478     {
479       Image
480         *dispose;
481 
482       dispose=CloneImage(dispose_image,0,0,MagickTrue,exception);
483       if (dispose == (Image *) NULL)
484         {
485           dispose_images=DestroyImageList(dispose_images);
486           dispose_image=DestroyImage(dispose_image);
487           return((Image *) NULL);
488         }
489       dispose_image->background_color.alpha_trait=BlendPixelTrait;
490       (void) CloneImageProfiles(dispose,next);
491       (void) CloneImageProperties(dispose,next);
492       (void) CloneImageArtifacts(dispose,next);
493       dispose->page.x=0;
494       dispose->page.y=0;
495       dispose->dispose=next->dispose;
496       AppendImageToList(&dispose_images,dispose);
497     }
498   }
499   dispose_image=DestroyImage(dispose_image);
500   return(GetFirstImageInList(dispose_images));
501 }
502 
503 /*
504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
505 %                                                                             %
506 %                                                                             %
507 %                                                                             %
508 +     C o m p a r e P i x e l s                                               %
509 %                                                                             %
510 %                                                                             %
511 %                                                                             %
512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
513 %
514 %  ComparePixels() Compare the two pixels and return true if the pixels
515 %  differ according to the given LayerType comparision method.
516 %
517 %  This currently only used internally by CompareImagesBounds(). It is
518 %  doubtful that this sub-routine will be useful outside this module.
519 %
520 %  The format of the ComparePixels method is:
521 %
522 %      MagickBooleanType *ComparePixels(const LayerMethod method,
523 %        const PixelInfo *p,const PixelInfo *q)
524 %
525 %  A description of each parameter follows:
526 %
527 %    o method: What differences to look for. Must be one of
528 %              CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
529 %
530 %    o p, q: the pixels to test for appropriate differences.
531 %
532 */
533 
ComparePixels(const LayerMethod method,const PixelInfo * p,const PixelInfo * q)534 static MagickBooleanType ComparePixels(const LayerMethod method,
535   const PixelInfo *p,const PixelInfo *q)
536 {
537   double
538     o1,
539     o2;
540 
541   /*
542     Any change in pixel values
543   */
544   if (method == CompareAnyLayer)
545     return((MagickBooleanType)(IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
546 
547   o1 = (p->alpha_trait != UndefinedPixelTrait) ? p->alpha : OpaqueAlpha;
548   o2 = (q->alpha_trait != UndefinedPixelTrait) ? q->alpha : OpaqueAlpha;
549   /*
550     Pixel goes from opaque to transprency.
551   */
552   if (method == CompareClearLayer)
553     return((MagickBooleanType) ( (o1 >= ((double) QuantumRange/2.0)) &&
554       (o2 < ((double) QuantumRange/2.0)) ) );
555   /*
556     Overlay would change first pixel by second.
557   */
558   if (method == CompareOverlayLayer)
559     {
560       if (o2 < ((double) QuantumRange/2.0))
561         return MagickFalse;
562       return((MagickBooleanType) (IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
563     }
564   return(MagickFalse);
565 }
566 
567 
568 /*
569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
570 %                                                                             %
571 %                                                                             %
572 %                                                                             %
573 +     C o m p a r e I m a g e B o u n d s                                     %
574 %                                                                             %
575 %                                                                             %
576 %                                                                             %
577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
578 %
579 %  CompareImagesBounds() Given two images return the smallest rectangular area
580 %  by which the two images differ, accourding to the given 'Compare...'
581 %  layer method.
582 %
583 %  This currently only used internally in this module, but may eventually
584 %  be used by other modules.
585 %
586 %  The format of the CompareImagesBounds method is:
587 %
588 %      RectangleInfo *CompareImagesBounds(const LayerMethod method,
589 %        const Image *image1,const Image *image2,ExceptionInfo *exception)
590 %
591 %  A description of each parameter follows:
592 %
593 %    o method: What differences to look for. Must be one of CompareAnyLayer,
594 %      CompareClearLayer, CompareOverlayLayer.
595 %
596 %    o image1, image2: the two images to compare.
597 %
598 %    o exception: return any errors or warnings in this structure.
599 %
600 */
601 
CompareImagesBounds(const Image * image1,const Image * image2,const LayerMethod method,ExceptionInfo * exception)602 static RectangleInfo CompareImagesBounds(const Image *image1,
603   const Image *image2,const LayerMethod method,ExceptionInfo *exception)
604 {
605   RectangleInfo
606     bounds;
607 
608   PixelInfo
609     pixel1,
610     pixel2;
611 
612   register const Quantum
613     *p,
614     *q;
615 
616   register ssize_t
617     x;
618 
619   ssize_t
620     y;
621 
622   /*
623     Set bounding box of the differences between images.
624   */
625   GetPixelInfo(image1,&pixel1);
626   GetPixelInfo(image2,&pixel2);
627   for (x=0; x < (ssize_t) image1->columns; x++)
628   {
629     p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
630     q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
631     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
632       break;
633     for (y=0; y < (ssize_t) image1->rows; y++)
634     {
635       GetPixelInfoPixel(image1,p,&pixel1);
636       GetPixelInfoPixel(image2,q,&pixel2);
637       if (ComparePixels(method,&pixel1,&pixel2))
638         break;
639       p+=GetPixelChannels(image1);
640       q+=GetPixelChannels(image2);
641     }
642     if (y < (ssize_t) image1->rows)
643       break;
644   }
645   if (x >= (ssize_t) image1->columns)
646     {
647       /*
648         Images are identical, return a null image.
649       */
650       bounds.x=-1;
651       bounds.y=-1;
652       bounds.width=1;
653       bounds.height=1;
654       return(bounds);
655     }
656   bounds.x=x;
657   for (x=(ssize_t) image1->columns-1; x >= 0; x--)
658   {
659     p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
660     q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
661     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
662       break;
663     for (y=0; y < (ssize_t) image1->rows; y++)
664     {
665       GetPixelInfoPixel(image1,p,&pixel1);
666       GetPixelInfoPixel(image2,q,&pixel2);
667       if (ComparePixels(method,&pixel1,&pixel2))
668         break;
669       p+=GetPixelChannels(image1);
670       q+=GetPixelChannels(image2);
671     }
672     if (y < (ssize_t) image1->rows)
673       break;
674   }
675   bounds.width=(size_t) (x-bounds.x+1);
676   for (y=0; y < (ssize_t) image1->rows; y++)
677   {
678     p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
679     q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
680     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
681       break;
682     for (x=0; x < (ssize_t) image1->columns; x++)
683     {
684       GetPixelInfoPixel(image1,p,&pixel1);
685       GetPixelInfoPixel(image2,q,&pixel2);
686       if (ComparePixels(method,&pixel1,&pixel2))
687         break;
688       p+=GetPixelChannels(image1);
689       q+=GetPixelChannels(image2);
690     }
691     if (x < (ssize_t) image1->columns)
692       break;
693   }
694   bounds.y=y;
695   for (y=(ssize_t) image1->rows-1; y >= 0; y--)
696   {
697     p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
698     q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
699     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
700       break;
701     for (x=0; x < (ssize_t) image1->columns; x++)
702     {
703       GetPixelInfoPixel(image1,p,&pixel1);
704       GetPixelInfoPixel(image2,q,&pixel2);
705       if (ComparePixels(method,&pixel1,&pixel2))
706         break;
707       p+=GetPixelChannels(image1);
708       q+=GetPixelChannels(image2);
709     }
710     if (x < (ssize_t) image1->columns)
711       break;
712   }
713   bounds.height=(size_t) (y-bounds.y+1);
714   return(bounds);
715 }
716 
717 /*
718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719 %                                                                             %
720 %                                                                             %
721 %                                                                             %
722 %     C o m p a r e I m a g e L a y e r s                                     %
723 %                                                                             %
724 %                                                                             %
725 %                                                                             %
726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727 %
728 %  CompareImagesLayers() compares each image with the next in a sequence and
729 %  returns the minimum bounding region of all the pixel differences (of the
730 %  LayerMethod specified) it discovers.
731 %
732 %  Images do NOT have to be the same size, though it is best that all the
733 %  images are 'coalesced' (images are all the same size, on a flattened
734 %  canvas, so as to represent exactly how an specific frame should look).
735 %
736 %  No GIF dispose methods are applied, so GIF animations must be coalesced
737 %  before applying this image operator to find differences to them.
738 %
739 %  The format of the CompareImagesLayers method is:
740 %
741 %      Image *CompareImagesLayers(const Image *images,
742 %        const LayerMethod method,ExceptionInfo *exception)
743 %
744 %  A description of each parameter follows:
745 %
746 %    o image: the image.
747 %
748 %    o method: the layers type to compare images with. Must be one of...
749 %              CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
750 %
751 %    o exception: return any errors or warnings in this structure.
752 %
753 */
754 
CompareImagesLayers(const Image * image,const LayerMethod method,ExceptionInfo * exception)755 MagickExport Image *CompareImagesLayers(const Image *image,
756   const LayerMethod method,ExceptionInfo *exception)
757 {
758   Image
759     *image_a,
760     *image_b,
761     *layers;
762 
763   RectangleInfo
764     *bounds;
765 
766   register const Image
767     *next;
768 
769   register ssize_t
770     i;
771 
772   assert(image != (const Image *) NULL);
773   assert(image->signature == MagickCoreSignature);
774   if (image->debug != MagickFalse)
775     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
776   assert(exception != (ExceptionInfo *) NULL);
777   assert(exception->signature == MagickCoreSignature);
778   assert((method == CompareAnyLayer) ||
779          (method == CompareClearLayer) ||
780          (method == CompareOverlayLayer));
781   /*
782     Allocate bounds memory.
783   */
784   next=GetFirstImageInList(image);
785   bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
786     GetImageListLength(next),sizeof(*bounds));
787   if (bounds == (RectangleInfo *) NULL)
788     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
789   /*
790     Set up first comparision images.
791   */
792   image_a=CloneImage(next,next->page.width,next->page.height,
793     MagickTrue,exception);
794   if (image_a == (Image *) NULL)
795     {
796       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
797       return((Image *) NULL);
798     }
799   image_a->background_color.alpha_trait=BlendPixelTrait;
800   image_a->background_color.alpha=(MagickRealType) TransparentAlpha;
801   (void) SetImageBackgroundColor(image_a,exception);
802   image_a->page=next->page;
803   image_a->page.x=0;
804   image_a->page.y=0;
805   (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
806     next->page.y,exception);
807   /*
808     Compute the bounding box of changes for the later images
809   */
810   i=0;
811   next=GetNextImageInList(next);
812   for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
813   {
814     image_b=CloneImage(image_a,0,0,MagickTrue,exception);
815     if (image_b == (Image *) NULL)
816       {
817         image_a=DestroyImage(image_a);
818         bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
819         return((Image *) NULL);
820       }
821     image_b->background_color.alpha_trait=BlendPixelTrait;
822     (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
823       next->page.y,exception);
824     bounds[i]=CompareImagesBounds(image_b,image_a,method,exception);
825     image_b=DestroyImage(image_b);
826     i++;
827   }
828   image_a=DestroyImage(image_a);
829   /*
830     Clone first image in sequence.
831   */
832   next=GetFirstImageInList(image);
833   layers=CloneImage(next,0,0,MagickTrue,exception);
834   if (layers == (Image *) NULL)
835     {
836       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
837       return((Image *) NULL);
838     }
839   layers->background_color.alpha_trait=BlendPixelTrait;
840   /*
841     Deconstruct the image sequence.
842   */
843   i=0;
844   next=GetNextImageInList(next);
845   for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
846   {
847     if ((bounds[i].x == -1) && (bounds[i].y == -1) &&
848         (bounds[i].width == 1) && (bounds[i].height == 1))
849       {
850         /*
851           An empty frame is returned from CompareImageBounds(), which means the
852           current frame is identical to the previous frame.
853         */
854         i++;
855         continue;
856       }
857     image_a=CloneImage(next,0,0,MagickTrue,exception);
858     if (image_a == (Image *) NULL)
859       break;
860     image_a->background_color.alpha_trait=BlendPixelTrait;
861     image_b=CropImage(image_a,&bounds[i],exception);
862     image_a=DestroyImage(image_a);
863     if (image_b == (Image *) NULL)
864       break;
865     AppendImageToList(&layers,image_b);
866     i++;
867   }
868   bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
869   if (next != (Image *) NULL)
870     {
871       layers=DestroyImageList(layers);
872       return((Image *) NULL);
873     }
874   return(GetFirstImageInList(layers));
875 }
876 
877 /*
878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
879 %                                                                             %
880 %                                                                             %
881 %                                                                             %
882 +     O p t i m i z e L a y e r F r a m e s                                   %
883 %                                                                             %
884 %                                                                             %
885 %                                                                             %
886 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
887 %
888 %  OptimizeLayerFrames() takes a coalesced GIF animation, and compares each
889 %  frame against the three different 'disposal' forms of the previous frame.
890 %  From this it then attempts to select the smallest cropped image and
891 %  disposal method needed to reproduce the resulting image.
892 %
893 %  Note that this not easy, and may require the expansion of the bounds
894 %  of previous frame, simply clear pixels for the next animation frame to
895 %  transparency according to the selected dispose method.
896 %
897 %  The format of the OptimizeLayerFrames method is:
898 %
899 %      Image *OptimizeLayerFrames(const Image *image,
900 %        const LayerMethod method,ExceptionInfo *exception)
901 %
902 %  A description of each parameter follows:
903 %
904 %    o image: the image.
905 %
906 %    o method: the layers technique to optimize with. Must be one of...
907 %      OptimizeImageLayer, or  OptimizePlusLayer.  The Plus form allows
908 %      the addition of extra 'zero delay' frames to clear pixels from
909 %      the previous frame, and the removal of frames that done change,
910 %      merging the delay times together.
911 %
912 %    o exception: return any errors or warnings in this structure.
913 %
914 */
915 /*
916   Define a 'fake' dispose method where the frame is duplicated, (for
917   OptimizePlusLayer) with a extra zero time delay frame which does a
918   BackgroundDisposal to clear the pixels that need to be cleared.
919 */
920 #define DupDispose  ((DisposeType)9)
921 /*
922   Another 'fake' dispose method used to removed frames that don't change.
923 */
924 #define DelDispose  ((DisposeType)8)
925 
926 #define DEBUG_OPT_FRAME 0
927 
OptimizeLayerFrames(const Image * image,const LayerMethod method,ExceptionInfo * exception)928 static Image *OptimizeLayerFrames(const Image *image,const LayerMethod method,
929   ExceptionInfo *exception)
930 {
931   ExceptionInfo
932     *sans_exception;
933 
934   Image
935     *prev_image,
936     *dup_image,
937     *bgnd_image,
938     *optimized_image;
939 
940   RectangleInfo
941     try_bounds,
942     bgnd_bounds,
943     dup_bounds,
944     *bounds;
945 
946   MagickBooleanType
947     add_frames,
948     try_cleared,
949     cleared;
950 
951   DisposeType
952     *disposals;
953 
954   register const Image
955     *curr;
956 
957   register ssize_t
958     i;
959 
960   assert(image != (const Image *) NULL);
961   assert(image->signature == MagickCoreSignature);
962   if (image->debug != MagickFalse)
963     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
964   assert(exception != (ExceptionInfo *) NULL);
965   assert(exception->signature == MagickCoreSignature);
966   assert(method == OptimizeLayer ||
967          method == OptimizeImageLayer ||
968          method == OptimizePlusLayer);
969   /*
970     Are we allowed to add/remove frames from animation?
971   */
972   add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
973   /*
974     Ensure  all the images are the same size.
975   */
976   curr=GetFirstImageInList(image);
977   for (; curr != (Image *) NULL; curr=GetNextImageInList(curr))
978   {
979     if ((curr->columns != image->columns) || (curr->rows != image->rows))
980       ThrowImageException(OptionError,"ImagesAreNotTheSameSize");
981 
982     if ((curr->page.x != 0) || (curr->page.y != 0) ||
983         (curr->page.width != image->page.width) ||
984         (curr->page.height != image->page.height))
985       ThrowImageException(OptionError,"ImagePagesAreNotCoalesced");
986   }
987   /*
988     Allocate memory (times 2 if we allow the use of frame duplications)
989   */
990   curr=GetFirstImageInList(image);
991   bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
992     GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)*
993     sizeof(*bounds));
994   if (bounds == (RectangleInfo *) NULL)
995     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
996   disposals=(DisposeType *) AcquireQuantumMemory((size_t)
997     GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
998     sizeof(*disposals));
999   if (disposals == (DisposeType *) NULL)
1000     {
1001       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1002       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1003     }
1004   /*
1005     Initialise Previous Image as fully transparent
1006   */
1007   prev_image=CloneImage(curr,curr->columns,curr->rows,MagickTrue,exception);
1008   if (prev_image == (Image *) NULL)
1009     {
1010       bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1011       disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1012       return((Image *) NULL);
1013     }
1014   prev_image->page=curr->page;  /* ERROR: <-- should not be need, but is! */
1015   prev_image->page.x=0;
1016   prev_image->page.y=0;
1017   prev_image->dispose=NoneDispose;
1018   prev_image->background_color.alpha_trait=BlendPixelTrait;
1019   prev_image->background_color.alpha=(MagickRealType) TransparentAlpha;
1020   (void) SetImageBackgroundColor(prev_image,exception);
1021   /*
1022     Figure out the area of overlay of the first frame
1023     No pixel could be cleared as all pixels are already cleared.
1024   */
1025 #if DEBUG_OPT_FRAME
1026   i=0;
1027   (void) FormatLocaleFile(stderr,"frame %.20g :-\n",(double) i);
1028 #endif
1029   disposals[0]=NoneDispose;
1030   bounds[0]=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
1031 #if DEBUG_OPT_FRAME
1032   (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g\n\n",
1033     (double) bounds[i].width,(double) bounds[i].height,
1034     (double) bounds[i].x,(double) bounds[i].y );
1035 #endif
1036   /*
1037     Compute the bounding box of changes for each pair of images.
1038   */
1039   i=1;
1040   bgnd_image=(Image *) NULL;
1041   dup_image=(Image *) NULL;
1042   dup_bounds.width=0;
1043   dup_bounds.height=0;
1044   dup_bounds.x=0;
1045   dup_bounds.y=0;
1046   curr=GetNextImageInList(curr);
1047   for ( ; curr != (const Image *) NULL; curr=GetNextImageInList(curr))
1048   {
1049 #if DEBUG_OPT_FRAME
1050     (void) FormatLocaleFile(stderr,"frame %.20g :-\n",(double) i);
1051 #endif
1052     /*
1053       Assume none disposal is the best
1054     */
1055     bounds[i]=CompareImagesBounds(curr->previous,curr,CompareAnyLayer,exception);
1056     cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception);
1057     disposals[i-1]=NoneDispose;
1058 #if DEBUG_OPT_FRAME
1059     (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g%s%s\n",
1060          (double) bounds[i].width,(double) bounds[i].height,
1061          (double) bounds[i].x,(double) bounds[i].y,
1062          bounds[i].x < 0?"  (unchanged)":"",
1063          cleared?"  (pixels cleared)":"");
1064 #endif
1065     if ( bounds[i].x < 0 ) {
1066       /*
1067         Image frame is exactly the same as the previous frame!
1068         If not adding frames leave it to be cropped down to a null image.
1069         Otherwise mark previous image for deleted, transfering its crop bounds
1070         to the current image.
1071       */
1072       if ( add_frames && i>=2 ) {
1073         disposals[i-1]=DelDispose;
1074         disposals[i]=NoneDispose;
1075         bounds[i]=bounds[i-1];
1076         i++;
1077         continue;
1078       }
1079     }
1080     else
1081       {
1082         /*
1083           Compare a none disposal against a previous disposal
1084         */
1085         try_bounds=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
1086         try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception);
1087 #if DEBUG_OPT_FRAME
1088     (void) FormatLocaleFile(stderr, "test_prev: %.20gx%.20g%+.20g%+.20g%s\n",
1089          (double) try_bounds.width,(double) try_bounds.height,
1090          (double) try_bounds.x,(double) try_bounds.y,
1091          try_cleared?"  (pixels were cleared)":"");
1092 #endif
1093         if ( (!try_cleared && cleared ) ||
1094                 try_bounds.width * try_bounds.height
1095                     <  bounds[i].width * bounds[i].height )
1096           {
1097             cleared=try_cleared;
1098             bounds[i]=try_bounds;
1099             disposals[i-1]=PreviousDispose;
1100 #if DEBUG_OPT_FRAME
1101             (void) FormatLocaleFile(stderr,"previous: accepted\n");
1102           } else {
1103             (void) FormatLocaleFile(stderr,"previous: rejected\n");
1104 #endif
1105           }
1106 
1107         /*
1108           If we are allowed lets try a complex frame duplication.
1109           It is useless if the previous image already clears pixels correctly.
1110           This method will always clear all the pixels that need to be cleared.
1111         */
1112         dup_bounds.width=dup_bounds.height=0; /* no dup, no pixel added */
1113         if ( add_frames )
1114           {
1115             dup_image=CloneImage(curr->previous,0,0,MagickTrue,exception);
1116             if (dup_image == (Image *) NULL)
1117               {
1118                 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1119                 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1120                 prev_image=DestroyImage(prev_image);
1121                 return((Image *) NULL);
1122               }
1123             dup_image->background_color.alpha_trait=BlendPixelTrait;
1124             dup_bounds=CompareImagesBounds(dup_image,curr,CompareClearLayer,exception);
1125             ClearBounds(dup_image,&dup_bounds,exception);
1126             try_bounds=CompareImagesBounds(dup_image,curr,CompareAnyLayer,exception);
1127             if ( cleared ||
1128                    dup_bounds.width*dup_bounds.height
1129                       +try_bounds.width*try_bounds.height
1130                    < bounds[i].width * bounds[i].height )
1131               {
1132                 cleared=MagickFalse;
1133                 bounds[i]=try_bounds;
1134                 disposals[i-1]=DupDispose;
1135                 /* to be finalised later, if found to be optimial */
1136               }
1137             else
1138               dup_bounds.width=dup_bounds.height=0;
1139           }
1140         /*
1141           Now compare against a simple background disposal
1142         */
1143         bgnd_image=CloneImage(curr->previous,0,0,MagickTrue,exception);
1144         if (bgnd_image == (Image *) NULL)
1145           {
1146             bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1147             disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1148             prev_image=DestroyImage(prev_image);
1149             if ( dup_image != (Image *) NULL)
1150               dup_image=DestroyImage(dup_image);
1151             return((Image *) NULL);
1152           }
1153         bgnd_image->background_color.alpha_trait=BlendPixelTrait;
1154         bgnd_bounds=bounds[i-1]; /* interum bounds of the previous image */
1155         ClearBounds(bgnd_image,&bgnd_bounds,exception);
1156         try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
1157         try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1158 #if DEBUG_OPT_FRAME
1159     (void) FormatLocaleFile(stderr, "background: %s\n",
1160          try_cleared?"(pixels cleared)":"");
1161 #endif
1162         if ( try_cleared )
1163           {
1164             /*
1165               Straight background disposal failed to clear pixels needed!
1166               Lets try expanding the disposal area of the previous frame, to
1167               include the pixels that are cleared.  This guaranteed
1168               to work, though may not be the most optimized solution.
1169             */
1170             try_bounds=CompareImagesBounds(curr->previous,curr,CompareClearLayer,exception);
1171 #if DEBUG_OPT_FRAME
1172             (void) FormatLocaleFile(stderr, "expand_clear: %.20gx%.20g%+.20g%+.20g%s\n",
1173                 (double) try_bounds.width,(double) try_bounds.height,
1174                 (double) try_bounds.x,(double) try_bounds.y,
1175                 try_bounds.x<0?"  (no expand nessary)":"");
1176 #endif
1177             if ( bgnd_bounds.x < 0 )
1178               bgnd_bounds = try_bounds;
1179             else
1180               {
1181 #if DEBUG_OPT_FRAME
1182                 (void) FormatLocaleFile(stderr, "expand_bgnd: %.20gx%.20g%+.20g%+.20g\n",
1183                     (double) bgnd_bounds.width,(double) bgnd_bounds.height,
1184                     (double) bgnd_bounds.x,(double) bgnd_bounds.y );
1185 #endif
1186                 if ( try_bounds.x < bgnd_bounds.x )
1187                   {
1188                      bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x;
1189                      if ( bgnd_bounds.width < try_bounds.width )
1190                        bgnd_bounds.width = try_bounds.width;
1191                      bgnd_bounds.x = try_bounds.x;
1192                   }
1193                 else
1194                   {
1195                      try_bounds.width += try_bounds.x - bgnd_bounds.x;
1196                      if ( bgnd_bounds.width < try_bounds.width )
1197                        bgnd_bounds.width = try_bounds.width;
1198                   }
1199                 if ( try_bounds.y < bgnd_bounds.y )
1200                   {
1201                      bgnd_bounds.height += bgnd_bounds.y - try_bounds.y;
1202                      if ( bgnd_bounds.height < try_bounds.height )
1203                        bgnd_bounds.height = try_bounds.height;
1204                      bgnd_bounds.y = try_bounds.y;
1205                   }
1206                 else
1207                   {
1208                     try_bounds.height += try_bounds.y - bgnd_bounds.y;
1209                      if ( bgnd_bounds.height < try_bounds.height )
1210                        bgnd_bounds.height = try_bounds.height;
1211                   }
1212 #if DEBUG_OPT_FRAME
1213                 (void) FormatLocaleFile(stderr, "        to : %.20gx%.20g%+.20g%+.20g\n",
1214                     (double) bgnd_bounds.width,(double) bgnd_bounds.height,
1215                     (double) bgnd_bounds.x,(double) bgnd_bounds.y );
1216 #endif
1217               }
1218             ClearBounds(bgnd_image,&bgnd_bounds,exception);
1219 #if DEBUG_OPT_FRAME
1220 /* Something strange is happening with a specific animation
1221  * CompareAnyLayers (normal method) and CompareClearLayers returns the whole
1222  * image, which is not posibly correct!  As verified by previous tests.
1223  * Something changed beyond the bgnd_bounds clearing.  But without being able
1224  * to see, or writet he image at this point it is hard to tell what is wrong!
1225  * Only CompareOverlay seemed to return something sensible.
1226  */
1227             try_bounds=CompareImagesBounds(bgnd_image,curr,CompareClearLayer,exception);
1228             (void) FormatLocaleFile(stderr, "expand_ctst: %.20gx%.20g%+.20g%+.20g\n",
1229                 (double) try_bounds.width,(double) try_bounds.height,
1230                 (double) try_bounds.x,(double) try_bounds.y );
1231             try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
1232             try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1233             (void) FormatLocaleFile(stderr, "expand_any : %.20gx%.20g%+.20g%+.20g%s\n",
1234                 (double) try_bounds.width,(double) try_bounds.height,
1235                 (double) try_bounds.x,(double) try_bounds.y,
1236                 try_cleared?"   (pixels cleared)":"");
1237 #endif
1238             try_bounds=CompareImagesBounds(bgnd_image,curr,CompareOverlayLayer,exception);
1239 #if DEBUG_OPT_FRAME
1240             try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1241             (void) FormatLocaleFile(stderr, "expand_test: %.20gx%.20g%+.20g%+.20g%s\n",
1242                 (double) try_bounds.width,(double) try_bounds.height,
1243                 (double) try_bounds.x,(double) try_bounds.y,
1244                 try_cleared?"   (pixels cleared)":"");
1245 #endif
1246           }
1247         /*
1248           Test if this background dispose is smaller than any of the
1249           other methods we tryed before this (including duplicated frame)
1250         */
1251         if ( cleared ||
1252               bgnd_bounds.width*bgnd_bounds.height
1253                 +try_bounds.width*try_bounds.height
1254               < bounds[i-1].width*bounds[i-1].height
1255                   +dup_bounds.width*dup_bounds.height
1256                   +bounds[i].width*bounds[i].height )
1257           {
1258             cleared=MagickFalse;
1259             bounds[i-1]=bgnd_bounds;
1260             bounds[i]=try_bounds;
1261             if ( disposals[i-1] == DupDispose )
1262               dup_image=DestroyImage(dup_image);
1263             disposals[i-1]=BackgroundDispose;
1264 #if DEBUG_OPT_FRAME
1265     (void) FormatLocaleFile(stderr,"expand_bgnd: accepted\n");
1266           } else {
1267     (void) FormatLocaleFile(stderr,"expand_bgnd: reject\n");
1268 #endif
1269           }
1270       }
1271     /*
1272        Finalise choice of dispose, set new prev_image,
1273        and junk any extra images as appropriate,
1274     */
1275     if ( disposals[i-1] == DupDispose )
1276       {
1277          if (bgnd_image != (Image *) NULL)
1278            bgnd_image=DestroyImage(bgnd_image);
1279          prev_image=DestroyImage(prev_image);
1280          prev_image=dup_image, dup_image=(Image *) NULL;
1281          bounds[i+1]=bounds[i];
1282          bounds[i]=dup_bounds;
1283          disposals[i-1]=DupDispose;
1284          disposals[i]=BackgroundDispose;
1285          i++;
1286       }
1287     else
1288       {
1289         if ( dup_image != (Image *) NULL)
1290           dup_image=DestroyImage(dup_image);
1291         if ( disposals[i-1] != PreviousDispose )
1292           prev_image=DestroyImage(prev_image);
1293         if ( disposals[i-1] == BackgroundDispose )
1294           prev_image=bgnd_image, bgnd_image=(Image *) NULL;
1295         if (bgnd_image != (Image *) NULL)
1296           bgnd_image=DestroyImage(bgnd_image);
1297         if ( disposals[i-1] == NoneDispose )
1298           {
1299             prev_image=ReferenceImage(curr->previous);
1300             if (prev_image == (Image *) NULL)
1301               {
1302                 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1303                 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1304                 return((Image *) NULL);
1305               }
1306           }
1307 
1308       }
1309     assert(prev_image != (Image *) NULL);
1310     disposals[i]=disposals[i-1];
1311 #if DEBUG_OPT_FRAME
1312     (void) FormatLocaleFile(stderr, "final   %.20g : %s  %.20gx%.20g%+.20g%+.20g\n",
1313          (double) i-1,
1314          CommandOptionToMnemonic(MagickDisposeOptions,disposals[i-1]),
1315          (double) bounds[i-1].width,(double) bounds[i-1].height,
1316          (double) bounds[i-1].x,(double) bounds[i-1].y );
1317 #endif
1318 #if DEBUG_OPT_FRAME
1319     (void) FormatLocaleFile(stderr, "interum %.20g : %s  %.20gx%.20g%+.20g%+.20g\n",
1320          (double) i,
1321          CommandOptionToMnemonic(MagickDisposeOptions,disposals[i]),
1322          (double) bounds[i].width,(double) bounds[i].height,
1323          (double) bounds[i].x,(double) bounds[i].y );
1324     (void) FormatLocaleFile(stderr,"\n");
1325 #endif
1326     i++;
1327   }
1328   prev_image=DestroyImage(prev_image);
1329   /*
1330     Optimize all images in sequence.
1331   */
1332   sans_exception=AcquireExceptionInfo();
1333   i=0;
1334   curr=GetFirstImageInList(image);
1335   optimized_image=NewImageList();
1336   while ( curr != (const Image *) NULL )
1337   {
1338     prev_image=CloneImage(curr,0,0,MagickTrue,exception);
1339     if (prev_image == (Image *) NULL)
1340       break;
1341     prev_image->background_color.alpha_trait=BlendPixelTrait;
1342     if ( disposals[i] == DelDispose ) {
1343       size_t time = 0;
1344       while ( disposals[i] == DelDispose ) {
1345         time += curr->delay*1000/curr->ticks_per_second;
1346         curr=GetNextImageInList(curr);
1347         i++;
1348       }
1349       time += curr->delay*1000/curr->ticks_per_second;
1350       prev_image->ticks_per_second = 100L;
1351       prev_image->delay = time*prev_image->ticks_per_second/1000;
1352     }
1353     bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
1354     prev_image=DestroyImage(prev_image);
1355     if (bgnd_image == (Image *) NULL)
1356       break;
1357     bgnd_image->dispose=disposals[i];
1358     if ( disposals[i] == DupDispose ) {
1359       bgnd_image->delay=0;
1360       bgnd_image->dispose=NoneDispose;
1361     }
1362     else
1363       curr=GetNextImageInList(curr);
1364     AppendImageToList(&optimized_image,bgnd_image);
1365     i++;
1366   }
1367   sans_exception=DestroyExceptionInfo(sans_exception);
1368   bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1369   disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1370   if (curr != (Image *) NULL)
1371     {
1372       optimized_image=DestroyImageList(optimized_image);
1373       return((Image *) NULL);
1374     }
1375   return(GetFirstImageInList(optimized_image));
1376 }
1377 
1378 /*
1379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1380 %                                                                             %
1381 %                                                                             %
1382 %                                                                             %
1383 %     O p t i m i z e I m a g e L a y e r s                                   %
1384 %                                                                             %
1385 %                                                                             %
1386 %                                                                             %
1387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1388 %
1389 %  OptimizeImageLayers() compares each image the GIF disposed forms of the
1390 %  previous image in the sequence.  From this it attempts to select the
1391 %  smallest cropped image to replace each frame, while preserving the results
1392 %  of the GIF animation.
1393 %
1394 %  The format of the OptimizeImageLayers method is:
1395 %
1396 %      Image *OptimizeImageLayers(const Image *image,
1397 %               ExceptionInfo *exception)
1398 %
1399 %  A description of each parameter follows:
1400 %
1401 %    o image: the image.
1402 %
1403 %    o exception: return any errors or warnings in this structure.
1404 %
1405 */
OptimizeImageLayers(const Image * image,ExceptionInfo * exception)1406 MagickExport Image *OptimizeImageLayers(const Image *image,
1407   ExceptionInfo *exception)
1408 {
1409   return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
1410 }
1411 
1412 /*
1413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1414 %                                                                             %
1415 %                                                                             %
1416 %                                                                             %
1417 %     O p t i m i z e P l u s I m a g e L a y e r s                           %
1418 %                                                                             %
1419 %                                                                             %
1420 %                                                                             %
1421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422 %
1423 %  OptimizeImagePlusLayers() is exactly as OptimizeImageLayers(), but may
1424 %  also add or even remove extra frames in the animation, if it improves
1425 %  the total number of pixels in the resulting GIF animation.
1426 %
1427 %  The format of the OptimizePlusImageLayers method is:
1428 %
1429 %      Image *OptimizePlusImageLayers(const Image *image,
1430 %               ExceptionInfo *exception)
1431 %
1432 %  A description of each parameter follows:
1433 %
1434 %    o image: the image.
1435 %
1436 %    o exception: return any errors or warnings in this structure.
1437 %
1438 */
OptimizePlusImageLayers(const Image * image,ExceptionInfo * exception)1439 MagickExport Image *OptimizePlusImageLayers(const Image *image,
1440   ExceptionInfo *exception)
1441 {
1442   return OptimizeLayerFrames(image,OptimizePlusLayer,exception);
1443 }
1444 
1445 /*
1446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1447 %                                                                             %
1448 %                                                                             %
1449 %                                                                             %
1450 %     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                       %
1451 %                                                                             %
1452 %                                                                             %
1453 %                                                                             %
1454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1455 %
1456 %  OptimizeImageTransparency() takes a frame optimized GIF animation, and
1457 %  compares the overlayed pixels against the disposal image resulting from all
1458 %  the previous frames in the animation.  Any pixel that does not change the
1459 %  disposal image (and thus does not effect the outcome of an overlay) is made
1460 %  transparent.
1461 %
1462 %  WARNING: This modifies the current images directly, rather than generate
1463 %  a new image sequence.
1464 %
1465 %  The format of the OptimizeImageTransperency method is:
1466 %
1467 %      void OptimizeImageTransperency(Image *image,ExceptionInfo *exception)
1468 %
1469 %  A description of each parameter follows:
1470 %
1471 %    o image: the image sequence
1472 %
1473 %    o exception: return any errors or warnings in this structure.
1474 %
1475 */
OptimizeImageTransparency(const Image * image,ExceptionInfo * exception)1476 MagickExport void OptimizeImageTransparency(const Image *image,
1477      ExceptionInfo *exception)
1478 {
1479   Image
1480     *dispose_image;
1481 
1482   register Image
1483     *next;
1484 
1485   /*
1486     Run the image through the animation sequence
1487   */
1488   assert(image != (Image *) NULL);
1489   assert(image->signature == MagickCoreSignature);
1490   if (image->debug != MagickFalse)
1491     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1492   assert(exception != (ExceptionInfo *) NULL);
1493   assert(exception->signature == MagickCoreSignature);
1494   next=GetFirstImageInList(image);
1495   dispose_image=CloneImage(next,next->page.width,next->page.height,
1496     MagickTrue,exception);
1497   if (dispose_image == (Image *) NULL)
1498     return;
1499   dispose_image->page=next->page;
1500   dispose_image->page.x=0;
1501   dispose_image->page.y=0;
1502   dispose_image->dispose=NoneDispose;
1503   dispose_image->background_color.alpha_trait=BlendPixelTrait;
1504   dispose_image->background_color.alpha=(MagickRealType) TransparentAlpha;
1505   (void) SetImageBackgroundColor(dispose_image,exception);
1506 
1507   while ( next != (Image *) NULL )
1508   {
1509     Image
1510       *current_image;
1511 
1512     /*
1513       Overlay this frame's image over the previous disposal image
1514     */
1515     current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
1516     if (current_image == (Image *) NULL)
1517       {
1518         dispose_image=DestroyImage(dispose_image);
1519         return;
1520       }
1521     current_image->background_color.alpha_trait=BlendPixelTrait;
1522     (void) CompositeImage(current_image,next,next->alpha_trait != UndefinedPixelTrait ?
1523       OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
1524       exception);
1525     /*
1526       At this point the image would be displayed, for the delay period
1527     **
1528       Work out the disposal of the previous image
1529     */
1530     if (next->dispose == BackgroundDispose)
1531       {
1532         RectangleInfo
1533           bounds=next->page;
1534 
1535         bounds.width=next->columns;
1536         bounds.height=next->rows;
1537         if (bounds.x < 0)
1538           {
1539             bounds.width+=bounds.x;
1540             bounds.x=0;
1541           }
1542         if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
1543           bounds.width=current_image->columns-bounds.x;
1544         if (bounds.y < 0)
1545           {
1546             bounds.height+=bounds.y;
1547             bounds.y=0;
1548           }
1549         if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
1550           bounds.height=current_image->rows-bounds.y;
1551         ClearBounds(current_image,&bounds,exception);
1552       }
1553     if (next->dispose != PreviousDispose)
1554       {
1555         dispose_image=DestroyImage(dispose_image);
1556         dispose_image=current_image;
1557       }
1558     else
1559       current_image=DestroyImage(current_image);
1560 
1561     /*
1562       Optimize Transparency of the next frame (if present)
1563     */
1564     next=GetNextImageInList(next);
1565     if (next != (Image *) NULL) {
1566       (void) CompositeImage(next,dispose_image,ChangeMaskCompositeOp,
1567         MagickTrue,-(next->page.x),-(next->page.y),exception);
1568     }
1569   }
1570   dispose_image=DestroyImage(dispose_image);
1571   return;
1572 }
1573 
1574 /*
1575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576 %                                                                             %
1577 %                                                                             %
1578 %                                                                             %
1579 %     R e m o v e D u p l i c a t e L a y e r s                               %
1580 %                                                                             %
1581 %                                                                             %
1582 %                                                                             %
1583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1584 %
1585 %  RemoveDuplicateLayers() removes any image that is exactly the same as the
1586 %  next image in the given image list.  Image size and virtual canvas offset
1587 %  must also match, though not the virtual canvas size itself.
1588 %
1589 %  No check is made with regards to image disposal setting, though it is the
1590 %  dispose setting of later image that is kept.  Also any time delays are also
1591 %  added together. As such coalesced image animations should still produce the
1592 %  same result, though with duplicte frames merged into a single frame.
1593 %
1594 %  The format of the RemoveDuplicateLayers method is:
1595 %
1596 %      void RemoveDuplicateLayers(Image **image,ExceptionInfo *exception)
1597 %
1598 %  A description of each parameter follows:
1599 %
1600 %    o images: the image list
1601 %
1602 %    o exception: return any errors or warnings in this structure.
1603 %
1604 */
RemoveDuplicateLayers(Image ** images,ExceptionInfo * exception)1605 MagickExport void RemoveDuplicateLayers(Image **images,ExceptionInfo *exception)
1606 {
1607   RectangleInfo
1608     bounds;
1609 
1610   register Image
1611     *image,
1612     *next;
1613 
1614   assert((*images) != (const Image *) NULL);
1615   assert((*images)->signature == MagickCoreSignature);
1616   if ((*images)->debug != MagickFalse)
1617     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1618       (*images)->filename);
1619   assert(exception != (ExceptionInfo *) NULL);
1620   assert(exception->signature == MagickCoreSignature);
1621   image=GetFirstImageInList(*images);
1622   for ( ; (next=GetNextImageInList(image)) != (Image *) NULL; image=next)
1623   {
1624     if ((image->columns != next->columns) || (image->rows != next->rows) ||
1625         (image->page.x != next->page.x) || (image->page.y != next->page.y))
1626       continue;
1627     bounds=CompareImagesBounds(image,next,CompareAnyLayer,exception);
1628     if (bounds.x < 0)
1629       {
1630         /*
1631           Two images are the same, merge time delays and delete one.
1632         */
1633         size_t
1634           time;
1635 
1636         time=1000*image->delay*PerceptibleReciprocal(image->ticks_per_second);
1637         time+=1000*next->delay*PerceptibleReciprocal(next->ticks_per_second);
1638         next->ticks_per_second=100L;
1639         next->delay=time*image->ticks_per_second/1000;
1640         next->iterations=image->iterations;
1641         *images=image;
1642         (void) DeleteImageFromList(images);
1643       }
1644   }
1645   *images=GetFirstImageInList(*images);
1646 }
1647 
1648 /*
1649 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1650 %                                                                             %
1651 %                                                                             %
1652 %                                                                             %
1653 %     R e m o v e Z e r o D e l a y L a y e r s                               %
1654 %                                                                             %
1655 %                                                                             %
1656 %                                                                             %
1657 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1658 %
1659 %  RemoveZeroDelayLayers() removes any image that as a zero delay time. Such
1660 %  images generally represent intermediate or partial updates in GIF
1661 %  animations used for file optimization.  They are not ment to be displayed
1662 %  to users of the animation.  Viewable images in an animation should have a
1663 %  time delay of 3 or more centi-seconds (hundredths of a second).
1664 %
1665 %  However if all the frames have a zero time delay, then either the animation
1666 %  is as yet incomplete, or it is not a GIF animation.  This a non-sensible
1667 %  situation, so no image will be removed and a 'Zero Time Animation' warning
1668 %  (exception) given.
1669 %
1670 %  No warning will be given if no image was removed because all images had an
1671 %  appropriate non-zero time delay set.
1672 %
1673 %  Due to the special requirements of GIF disposal handling, GIF animations
1674 %  should be coalesced first, before calling this function, though that is not
1675 %  a requirement.
1676 %
1677 %  The format of the RemoveZeroDelayLayers method is:
1678 %
1679 %      void RemoveZeroDelayLayers(Image **image,ExceptionInfo *exception)
1680 %
1681 %  A description of each parameter follows:
1682 %
1683 %    o images: the image list
1684 %
1685 %    o exception: return any errors or warnings in this structure.
1686 %
1687 */
RemoveZeroDelayLayers(Image ** images,ExceptionInfo * exception)1688 MagickExport void RemoveZeroDelayLayers(Image **images,
1689      ExceptionInfo *exception)
1690 {
1691   Image
1692     *i;
1693 
1694   assert((*images) != (const Image *) NULL);
1695   assert((*images)->signature == MagickCoreSignature);
1696   if ((*images)->debug != MagickFalse)
1697     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
1698   assert(exception != (ExceptionInfo *) NULL);
1699   assert(exception->signature == MagickCoreSignature);
1700 
1701   i=GetFirstImageInList(*images);
1702   for ( ; i != (Image *) NULL; i=GetNextImageInList(i))
1703     if ( i->delay != 0L ) break;
1704   if ( i == (Image *) NULL ) {
1705     (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1706        "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename);
1707     return;
1708   }
1709   i=GetFirstImageInList(*images);
1710   while ( i != (Image *) NULL )
1711   {
1712     if ( i->delay == 0L ) {
1713       (void) DeleteImageFromList(&i);
1714       *images=i;
1715     }
1716     else
1717       i=GetNextImageInList(i);
1718   }
1719   *images=GetFirstImageInList(*images);
1720 }
1721 
1722 /*
1723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724 %                                                                             %
1725 %                                                                             %
1726 %                                                                             %
1727 %     C o m p o s i t e L a y e r s                                           %
1728 %                                                                             %
1729 %                                                                             %
1730 %                                                                             %
1731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1732 %
1733 %  CompositeLayers() compose the source image sequence over the destination
1734 %  image sequence, starting with the current image in both lists.
1735 %
1736 %  Each layer from the two image lists are composted together until the end of
1737 %  one of the image lists is reached.  The offset of each composition is also
1738 %  adjusted to match the virtual canvas offsets of each layer. As such the
1739 %  given offset is relative to the virtual canvas, and not the actual image.
1740 %
1741 %  Composition uses given x and y offsets, as the 'origin' location of the
1742 %  source images virtual canvas (not the real image) allowing you to compose a
1743 %  list of 'layer images' into the destiantioni images.  This makes it well
1744 %  sutiable for directly composing 'Clears Frame Animations' or 'Coaleased
1745 %  Animations' onto a static or other 'Coaleased Animation' destination image
1746 %  list.  GIF disposal handling is not looked at.
1747 %
1748 %  Special case:- If one of the image sequences is the last image (just a
1749 %  single image remaining), that image is repeatally composed with all the
1750 %  images in the other image list.  Either the source or destination lists may
1751 %  be the single image, for this situation.
1752 %
1753 %  In the case of a single destination image (or last image given), that image
1754 %  will ve cloned to match the number of images remaining in the source image
1755 %  list.
1756 %
1757 %  This is equivelent to the "-layer Composite" Shell API operator.
1758 %
1759 %
1760 %  The format of the CompositeLayers method is:
1761 %
1762 %      void CompositeLayers(Image *destination, const CompositeOperator
1763 %      compose, Image *source, const ssize_t x_offset, const ssize_t y_offset,
1764 %      ExceptionInfo *exception);
1765 %
1766 %  A description of each parameter follows:
1767 %
1768 %    o destination: the destination images and results
1769 %
1770 %    o source: source image(s) for the layer composition
1771 %
1772 %    o compose, x_offset, y_offset:  arguments passed on to CompositeImages()
1773 %
1774 %    o exception: return any errors or warnings in this structure.
1775 %
1776 */
1777 
CompositeCanvas(Image * destination,const CompositeOperator compose,Image * source,ssize_t x_offset,ssize_t y_offset,ExceptionInfo * exception)1778 static inline void CompositeCanvas(Image *destination,
1779   const CompositeOperator compose,Image *source,ssize_t x_offset,
1780   ssize_t y_offset,ExceptionInfo *exception)
1781 {
1782   const char
1783     *value;
1784 
1785   x_offset+=source->page.x-destination->page.x;
1786   y_offset+=source->page.y-destination->page.y;
1787   value=GetImageArtifact(source,"compose:outside-overlay");
1788   (void) CompositeImage(destination,source,compose,
1789     (value != (const char *) NULL) && (IsStringTrue(value) != MagickFalse) ?
1790     MagickFalse : MagickTrue,x_offset,y_offset,exception);
1791 }
1792 
CompositeLayers(Image * destination,const CompositeOperator compose,Image * source,const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo * exception)1793 MagickExport void CompositeLayers(Image *destination,
1794   const CompositeOperator compose, Image *source,const ssize_t x_offset,
1795   const ssize_t y_offset,ExceptionInfo *exception)
1796 {
1797   assert(destination != (Image *) NULL);
1798   assert(destination->signature == MagickCoreSignature);
1799   assert(source != (Image *) NULL);
1800   assert(source->signature == MagickCoreSignature);
1801   assert(exception != (ExceptionInfo *) NULL);
1802   assert(exception->signature == MagickCoreSignature);
1803   if (source->debug != MagickFalse || destination->debug != MagickFalse)
1804     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
1805       source->filename,destination->filename);
1806 
1807   /*
1808     Overlay single source image over destation image/list
1809   */
1810   if ( source->next == (Image *) NULL )
1811     while ( destination != (Image *) NULL )
1812     {
1813       CompositeCanvas(destination, compose, source, x_offset, y_offset,
1814         exception);
1815       destination=GetNextImageInList(destination);
1816     }
1817 
1818   /*
1819     Overlay source image list over single destination.
1820     Multiple clones of destination image are created to match source list.
1821     Original Destination image becomes first image of generated list.
1822     As such the image list pointer does not require any change in caller.
1823     Some animation attributes however also needs coping in this case.
1824   */
1825   else if ( destination->next == (Image *) NULL )
1826   {
1827     Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
1828 
1829     if (dest != (Image *) NULL)
1830       {
1831         dest->background_color.alpha_trait=BlendPixelTrait;
1832         CompositeCanvas(destination, compose, source, x_offset, y_offset,
1833           exception);
1834         /* copy source image attributes ? */
1835         if ( source->next != (Image *) NULL )
1836           {
1837             destination->delay=source->delay;
1838             destination->iterations=source->iterations;
1839           }
1840         source=GetNextImageInList(source);
1841         while (source != (Image *) NULL)
1842         {
1843           AppendImageToList(&destination,
1844             CloneImage(dest,0,0,MagickTrue,exception));
1845           destination->background_color.alpha_trait=BlendPixelTrait;
1846           destination=GetLastImageInList(destination);
1847           CompositeCanvas(destination,compose,source,x_offset,y_offset,
1848             exception);
1849           destination->delay=source->delay;
1850           destination->iterations=source->iterations;
1851           source=GetNextImageInList(source);
1852         }
1853         dest=DestroyImage(dest);
1854       }
1855   }
1856 
1857   /*
1858     Overlay a source image list over a destination image list
1859     until either list runs out of images. (Does not repeat)
1860   */
1861   else
1862     while ( source != (Image *) NULL && destination != (Image *) NULL )
1863     {
1864       CompositeCanvas(destination, compose, source, x_offset, y_offset,
1865         exception);
1866       source=GetNextImageInList(source);
1867       destination=GetNextImageInList(destination);
1868     }
1869 }
1870 
1871 /*
1872 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1873 %                                                                             %
1874 %                                                                             %
1875 %                                                                             %
1876 %     M e r g e I m a g e L a y e r s                                         %
1877 %                                                                             %
1878 %                                                                             %
1879 %                                                                             %
1880 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1881 %
1882 %  MergeImageLayers() composes all the image layers from the current given
1883 %  image onward to produce a single image of the merged layers.
1884 %
1885 %  The inital canvas's size depends on the given LayerMethod, and is
1886 %  initialized using the first images background color.  The images
1887 %  are then compositied onto that image in sequence using the given
1888 %  composition that has been assigned to each individual image.
1889 %
1890 %  The format of the MergeImageLayers is:
1891 %
1892 %      Image *MergeImageLayers(Image *image,const LayerMethod method,
1893 %        ExceptionInfo *exception)
1894 %
1895 %  A description of each parameter follows:
1896 %
1897 %    o image: the image list to be composited together
1898 %
1899 %    o method: the method of selecting the size of the initial canvas.
1900 %
1901 %        MergeLayer: Merge all layers onto a canvas just large enough
1902 %           to hold all the actual images. The virtual canvas of the
1903 %           first image is preserved but otherwise ignored.
1904 %
1905 %        FlattenLayer: Use the virtual canvas size of first image.
1906 %           Images which fall outside this canvas is clipped.
1907 %           This can be used to 'fill out' a given virtual canvas.
1908 %
1909 %        MosaicLayer: Start with the virtual canvas of the first image,
1910 %           enlarging left and right edges to contain all images.
1911 %           Images with negative offsets will be clipped.
1912 %
1913 %        TrimBoundsLayer: Determine the overall bounds of all the image
1914 %           layers just as in "MergeLayer", then adjust the the canvas
1915 %           and offsets to be relative to those bounds, without overlaying
1916 %           the images.
1917 %
1918 %           WARNING: a new image is not returned, the original image
1919 %           sequence page data is modified instead.
1920 %
1921 %    o exception: return any errors or warnings in this structure.
1922 %
1923 */
MergeImageLayers(Image * image,const LayerMethod method,ExceptionInfo * exception)1924 MagickExport Image *MergeImageLayers(Image *image,const LayerMethod method,
1925   ExceptionInfo *exception)
1926 {
1927 #define MergeLayersTag  "Merge/Layers"
1928 
1929   Image
1930     *canvas;
1931 
1932   MagickBooleanType
1933     proceed;
1934 
1935   RectangleInfo
1936     page;
1937 
1938   register const Image
1939     *next;
1940 
1941   size_t
1942     number_images,
1943     height,
1944     width;
1945 
1946   ssize_t
1947     scene;
1948 
1949   assert(image != (Image *) NULL);
1950   assert(image->signature == MagickCoreSignature);
1951   if (image->debug != MagickFalse)
1952     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1953   assert(exception != (ExceptionInfo *) NULL);
1954   assert(exception->signature == MagickCoreSignature);
1955   /*
1956     Determine canvas image size, and its virtual canvas size and offset
1957   */
1958   page=image->page;
1959   width=image->columns;
1960   height=image->rows;
1961   switch (method)
1962   {
1963     case TrimBoundsLayer:
1964     case MergeLayer:
1965     default:
1966     {
1967       next=GetNextImageInList(image);
1968       for ( ; next != (Image *) NULL;  next=GetNextImageInList(next))
1969       {
1970         if (page.x > next->page.x)
1971           {
1972             width+=page.x-next->page.x;
1973             page.x=next->page.x;
1974           }
1975         if (page.y > next->page.y)
1976           {
1977             height+=page.y-next->page.y;
1978             page.y=next->page.y;
1979           }
1980         if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x))
1981           width=(size_t) next->page.x+(ssize_t) next->columns-page.x;
1982         if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y))
1983           height=(size_t) next->page.y+(ssize_t) next->rows-page.y;
1984       }
1985       break;
1986     }
1987     case FlattenLayer:
1988     {
1989       if (page.width > 0)
1990         width=page.width;
1991       if (page.height > 0)
1992         height=page.height;
1993       page.x=0;
1994       page.y=0;
1995       break;
1996     }
1997     case MosaicLayer:
1998     {
1999       if (page.width > 0)
2000         width=page.width;
2001       if (page.height > 0)
2002         height=page.height;
2003       for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
2004       {
2005         if (method == MosaicLayer)
2006           {
2007             page.x=next->page.x;
2008             page.y=next->page.y;
2009             if ((ssize_t) width < (next->page.x+(ssize_t) next->columns))
2010               width=(size_t) next->page.x+next->columns;
2011             if ((ssize_t) height < (next->page.y+(ssize_t) next->rows))
2012               height=(size_t) next->page.y+next->rows;
2013           }
2014       }
2015       page.width=width;
2016       page.height=height;
2017       page.x=0;
2018       page.y=0;
2019     }
2020     break;
2021   }
2022   /*
2023     Set virtual canvas size if not defined.
2024   */
2025   if (page.width == 0)
2026     page.width=page.x < 0 ? width : width+page.x;
2027   if (page.height == 0)
2028     page.height=page.y < 0 ? height : height+page.y;
2029   /*
2030     Handle "TrimBoundsLayer" method separately to normal 'layer merge'.
2031   */
2032   if (method == TrimBoundsLayer)
2033     {
2034       number_images=GetImageListLength(image);
2035       for (scene=0; scene < (ssize_t) number_images; scene++)
2036       {
2037         image->page.x-=page.x;
2038         image->page.y-=page.y;
2039         image->page.width=width;
2040         image->page.height=height;
2041         proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2042           number_images);
2043         if (proceed == MagickFalse)
2044           break;
2045         image=GetNextImageInList(image);
2046         if (image == (Image *) NULL)
2047           break;
2048       }
2049       return((Image *) NULL);
2050     }
2051   /*
2052     Create canvas size of width and height, and background color.
2053   */
2054   canvas=CloneImage(image,width,height,MagickTrue,exception);
2055   if (canvas == (Image *) NULL)
2056     return((Image *) NULL);
2057   canvas->background_color.alpha_trait=BlendPixelTrait;
2058   (void) SetImageBackgroundColor(canvas,exception);
2059   canvas->page=page;
2060   canvas->dispose=UndefinedDispose;
2061   /*
2062     Compose images onto canvas, with progress monitor
2063   */
2064   number_images=GetImageListLength(image);
2065   for (scene=0; scene < (ssize_t) number_images; scene++)
2066   {
2067     (void) CompositeImage(canvas,image,image->compose,MagickTrue,image->page.x-
2068       canvas->page.x,image->page.y-canvas->page.y,exception);
2069     proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2070       number_images);
2071     if (proceed == MagickFalse)
2072       break;
2073     image=GetNextImageInList(image);
2074     if (image == (Image *) NULL)
2075       break;
2076   }
2077   return(canvas);
2078 }
2079 
2080