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