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