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