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