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