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