MagickCore  7.0.7
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  FUTURE: also check that image is also fully coalesced (full page)
967  Though as long as they are the same size it should not matter.
968  */
969  }
970  /*
971  Allocate memory (times 2 if we allow the use of frame duplications)
972  */
973  curr=GetFirstImageInList(image);
974  bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
975  GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)*
976  sizeof(*bounds));
977  if (bounds == (RectangleInfo *) NULL)
978  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
979  disposals=(DisposeType *) AcquireQuantumMemory((size_t)
980  GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
981  sizeof(*disposals));
982  if (disposals == (DisposeType *) NULL)
983  {
984  bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
985  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
986  }
987  /*
988  Initialise Previous Image as fully transparent
989  */
990  prev_image=CloneImage(curr,curr->page.width,curr->page.height,
991  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,curr->previous->page.width,
1100  curr->previous->page.height,MagickTrue,exception);
1101  if (dup_image == (Image *) NULL)
1102  {
1103  bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1104  disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1105  prev_image=DestroyImage(prev_image);
1106  return((Image *) NULL);
1107  }
1108  dup_bounds=CompareImagesBounds(dup_image,curr,CompareClearLayer,exception);
1109  ClearBounds(dup_image,&dup_bounds,exception);
1110  try_bounds=CompareImagesBounds(dup_image,curr,CompareAnyLayer,exception);
1111  if ( cleared ||
1112  dup_bounds.width*dup_bounds.height
1113  +try_bounds.width*try_bounds.height
1114  < bounds[i].width * bounds[i].height )
1115  {
1116  cleared=MagickFalse;
1117  bounds[i]=try_bounds;
1118  disposals[i-1]=DupDispose;
1119  /* to be finalised later, if found to be optimial */
1120  }
1121  else
1122  dup_bounds.width=dup_bounds.height=0;
1123  }
1124  /*
1125  Now compare against a simple background disposal
1126  */
1127  bgnd_image=CloneImage(curr->previous,curr->previous->page.width,
1128  curr->previous->page.height,MagickTrue,exception);
1129  if (bgnd_image == (Image *) NULL)
1130  {
1131  bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1132  disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1133  prev_image=DestroyImage(prev_image);
1134  if ( dup_image != (Image *) NULL)
1135  dup_image=DestroyImage(dup_image);
1136  return((Image *) NULL);
1137  }
1138  bgnd_bounds=bounds[i-1]; /* interum bounds of the previous image */
1139  ClearBounds(bgnd_image,&bgnd_bounds,exception);
1140  try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
1141  try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1142 #if DEBUG_OPT_FRAME
1143  (void) FormatLocaleFile(stderr, "background: %s\n",
1144  try_cleared?"(pixels cleared)":"");
1145 #endif
1146  if ( try_cleared )
1147  {
1148  /*
1149  Straight background disposal failed to clear pixels needed!
1150  Lets try expanding the disposal area of the previous frame, to
1151  include the pixels that are cleared. This guaranteed
1152  to work, though may not be the most optimized solution.
1153  */
1154  try_bounds=CompareImagesBounds(curr->previous,curr,CompareClearLayer,exception);
1155 #if DEBUG_OPT_FRAME
1156  (void) FormatLocaleFile(stderr, "expand_clear: %.20gx%.20g%+.20g%+.20g%s\n",
1157  (double) try_bounds.width,(double) try_bounds.height,
1158  (double) try_bounds.x,(double) try_bounds.y,
1159  try_bounds.x<0?" (no expand nessary)":"");
1160 #endif
1161  if ( bgnd_bounds.x < 0 )
1162  bgnd_bounds = try_bounds;
1163  else
1164  {
1165 #if DEBUG_OPT_FRAME
1166  (void) FormatLocaleFile(stderr, "expand_bgnd: %.20gx%.20g%+.20g%+.20g\n",
1167  (double) bgnd_bounds.width,(double) bgnd_bounds.height,
1168  (double) bgnd_bounds.x,(double) bgnd_bounds.y );
1169 #endif
1170  if ( try_bounds.x < bgnd_bounds.x )
1171  {
1172  bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x;
1173  if ( bgnd_bounds.width < try_bounds.width )
1174  bgnd_bounds.width = try_bounds.width;
1175  bgnd_bounds.x = try_bounds.x;
1176  }
1177  else
1178  {
1179  try_bounds.width += try_bounds.x - bgnd_bounds.x;
1180  if ( bgnd_bounds.width < try_bounds.width )
1181  bgnd_bounds.width = try_bounds.width;
1182  }
1183  if ( try_bounds.y < bgnd_bounds.y )
1184  {
1185  bgnd_bounds.height += bgnd_bounds.y - try_bounds.y;
1186  if ( bgnd_bounds.height < try_bounds.height )
1187  bgnd_bounds.height = try_bounds.height;
1188  bgnd_bounds.y = try_bounds.y;
1189  }
1190  else
1191  {
1192  try_bounds.height += try_bounds.y - bgnd_bounds.y;
1193  if ( bgnd_bounds.height < try_bounds.height )
1194  bgnd_bounds.height = try_bounds.height;
1195  }
1196 #if DEBUG_OPT_FRAME
1197  (void) FormatLocaleFile(stderr, " to : %.20gx%.20g%+.20g%+.20g\n",
1198  (double) bgnd_bounds.width,(double) bgnd_bounds.height,
1199  (double) bgnd_bounds.x,(double) bgnd_bounds.y );
1200 #endif
1201  }
1202  ClearBounds(bgnd_image,&bgnd_bounds,exception);
1203 #if DEBUG_OPT_FRAME
1204 /* Something strange is happening with a specific animation
1205  * CompareAnyLayers (normal method) and CompareClearLayers returns the whole
1206  * image, which is not posibly correct! As verified by previous tests.
1207  * Something changed beyond the bgnd_bounds clearing. But without being able
1208  * to see, or writet he image at this point it is hard to tell what is wrong!
1209  * Only CompareOverlay seemed to return something sensible.
1210  */
1211  try_bounds=CompareImagesBounds(bgnd_image,curr,CompareClearLayer,exception);
1212  (void) FormatLocaleFile(stderr, "expand_ctst: %.20gx%.20g%+.20g%+.20g\n",
1213  (double) try_bounds.width,(double) try_bounds.height,
1214  (double) try_bounds.x,(double) try_bounds.y );
1215  try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
1216  try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1217  (void) FormatLocaleFile(stderr, "expand_any : %.20gx%.20g%+.20g%+.20g%s\n",
1218  (double) try_bounds.width,(double) try_bounds.height,
1219  (double) try_bounds.x,(double) try_bounds.y,
1220  try_cleared?" (pixels cleared)":"");
1221 #endif
1222  try_bounds=CompareImagesBounds(bgnd_image,curr,CompareOverlayLayer,exception);
1223 #if DEBUG_OPT_FRAME
1224  try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1225  (void) FormatLocaleFile(stderr, "expand_test: %.20gx%.20g%+.20g%+.20g%s\n",
1226  (double) try_bounds.width,(double) try_bounds.height,
1227  (double) try_bounds.x,(double) try_bounds.y,
1228  try_cleared?" (pixels cleared)":"");
1229 #endif
1230  }
1231  /*
1232  Test if this background dispose is smaller than any of the
1233  other methods we tryed before this (including duplicated frame)
1234  */
1235  if ( cleared ||
1236  bgnd_bounds.width*bgnd_bounds.height
1237  +try_bounds.width*try_bounds.height
1238  < bounds[i-1].width*bounds[i-1].height
1239  +dup_bounds.width*dup_bounds.height
1240  +bounds[i].width*bounds[i].height )
1241  {
1242  cleared=MagickFalse;
1243  bounds[i-1]=bgnd_bounds;
1244  bounds[i]=try_bounds;
1245  if ( disposals[i-1] == DupDispose )
1246  dup_image=DestroyImage(dup_image);
1247  disposals[i-1]=BackgroundDispose;
1248 #if DEBUG_OPT_FRAME
1249  (void) FormatLocaleFile(stderr, "expand_bgnd: accepted\n");
1250  } else {
1251  (void) FormatLocaleFile(stderr, "expand_bgnd: reject\n");
1252 #endif
1253  }
1254  }
1255  /*
1256  Finalise choice of dispose, set new prev_image,
1257  and junk any extra images as appropriate,
1258  */
1259  if ( disposals[i-1] == DupDispose )
1260  {
1261  if (bgnd_image != (Image *) NULL)
1262  bgnd_image=DestroyImage(bgnd_image);
1263  prev_image=DestroyImage(prev_image);
1264  prev_image=dup_image, dup_image=(Image *) NULL;
1265  bounds[i+1]=bounds[i];
1266  bounds[i]=dup_bounds;
1267  disposals[i-1]=DupDispose;
1268  disposals[i]=BackgroundDispose;
1269  i++;
1270  }
1271  else
1272  {
1273  if ( dup_image != (Image *) NULL)
1274  dup_image=DestroyImage(dup_image);
1275  if ( disposals[i-1] != PreviousDispose )
1276  prev_image=DestroyImage(prev_image);
1277  if ( disposals[i-1] == BackgroundDispose )
1278  prev_image=bgnd_image, bgnd_image=(Image *) NULL;
1279  if (bgnd_image != (Image *) NULL)
1280  bgnd_image=DestroyImage(bgnd_image);
1281  if ( disposals[i-1] == NoneDispose )
1282  {
1283  prev_image=CloneImage(curr->previous,curr->previous->page.width,
1284  curr->previous->page.height,MagickTrue,exception);
1285  if (prev_image == (Image *) NULL)
1286  {
1287  bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1288  disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1289  return((Image *) NULL);
1290  }
1291  }
1292 
1293  }
1294  assert(prev_image != (Image *) NULL);
1295  disposals[i]=disposals[i-1];
1296 #if DEBUG_OPT_FRAME
1297  (void) FormatLocaleFile(stderr, "final %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1298  (double) i-1,
1300  (double) bounds[i-1].width, (double) bounds[i-1].height,
1301  (double) bounds[i-1].x, (double) bounds[i-1].y );
1302 #endif
1303 #if DEBUG_OPT_FRAME
1304  (void) FormatLocaleFile(stderr, "interum %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1305  (double) i,
1307  (double) bounds[i].width, (double) bounds[i].height,
1308  (double) bounds[i].x, (double) bounds[i].y );
1309  (void) FormatLocaleFile(stderr, "\n");
1310 #endif
1311  i++;
1312  }
1313  prev_image=DestroyImage(prev_image);
1314  /*
1315  Optimize all images in sequence.
1316  */
1317  sans_exception=AcquireExceptionInfo();
1318  i=0;
1319  curr=GetFirstImageInList(image);
1320  optimized_image=NewImageList();
1321  while ( curr != (const Image *) NULL )
1322  {
1323  prev_image=CloneImage(curr,0,0,MagickTrue,exception);
1324  if (prev_image == (Image *) NULL)
1325  break;
1326  if (prev_image->alpha_trait == UndefinedPixelTrait)
1327  (void) SetImageAlphaChannel(prev_image,OpaqueAlphaChannel,exception);
1328  if ( disposals[i] == DelDispose ) {
1329  size_t time = 0;
1330  while ( disposals[i] == DelDispose ) {
1331  time += curr->delay*1000/curr->ticks_per_second;
1332  curr=GetNextImageInList(curr);
1333  i++;
1334  }
1335  time += curr->delay*1000/curr->ticks_per_second;
1336  prev_image->ticks_per_second = 100L;
1337  prev_image->delay = time*prev_image->ticks_per_second/1000;
1338  }
1339  bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
1340  prev_image=DestroyImage(prev_image);
1341  if (bgnd_image == (Image *) NULL)
1342  break;
1343  bgnd_image->dispose=disposals[i];
1344  if ( disposals[i] == DupDispose ) {
1345  bgnd_image->delay=0;
1346  bgnd_image->dispose=NoneDispose;
1347  }
1348  else
1349  curr=GetNextImageInList(curr);
1350  AppendImageToList(&optimized_image,bgnd_image);
1351  i++;
1352  }
1353  sans_exception=DestroyExceptionInfo(sans_exception);
1354  bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
1355  disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1356  if (curr != (Image *) NULL)
1357  {
1358  optimized_image=DestroyImageList(optimized_image);
1359  return((Image *) NULL);
1360  }
1361  return(GetFirstImageInList(optimized_image));
1362 }
1363 
1364 /*
1365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1366 % %
1367 % %
1368 % %
1369 % O p t i m i z e I m a g e L a y e r s %
1370 % %
1371 % %
1372 % %
1373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1374 %
1375 % OptimizeImageLayers() compares each image the GIF disposed forms of the
1376 % previous image in the sequence. From this it attempts to select the
1377 % smallest cropped image to replace each frame, while preserving the results
1378 % of the GIF animation.
1379 %
1380 % The format of the OptimizeImageLayers method is:
1381 %
1382 % Image *OptimizeImageLayers(const Image *image,
1383 % ExceptionInfo *exception)
1384 %
1385 % A description of each parameter follows:
1386 %
1387 % o image: the image.
1388 %
1389 % o exception: return any errors or warnings in this structure.
1390 %
1391 */
1393  ExceptionInfo *exception)
1394 {
1395  return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
1396 }
1397 
1398 /*
1399 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1400 % %
1401 % %
1402 % %
1403 % O p t i m i z e P l u s I m a g e L a y e r s %
1404 % %
1405 % %
1406 % %
1407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1408 %
1409 % OptimizeImagePlusLayers() is exactly as OptimizeImageLayers(), but may
1410 % also add or even remove extra frames in the animation, if it improves
1411 % the total number of pixels in the resulting GIF animation.
1412 %
1413 % The format of the OptimizePlusImageLayers method is:
1414 %
1415 % Image *OptimizePlusImageLayers(const Image *image,
1416 % ExceptionInfo *exception)
1417 %
1418 % A description of each parameter follows:
1419 %
1420 % o image: the image.
1421 %
1422 % o exception: return any errors or warnings in this structure.
1423 %
1424 */
1426  ExceptionInfo *exception)
1427 {
1428  return OptimizeLayerFrames(image, OptimizePlusLayer, exception);
1429 }
1430 
1431 /*
1432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1433 % %
1434 % %
1435 % %
1436 % 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 %
1437 % %
1438 % %
1439 % %
1440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1441 %
1442 % OptimizeImageTransparency() takes a frame optimized GIF animation, and
1443 % compares the overlayed pixels against the disposal image resulting from all
1444 % the previous frames in the animation. Any pixel that does not change the
1445 % disposal image (and thus does not effect the outcome of an overlay) is made
1446 % transparent.
1447 %
1448 % WARNING: This modifies the current images directly, rather than generate
1449 % a new image sequence.
1450 %
1451 % The format of the OptimizeImageTransperency method is:
1452 %
1453 % void OptimizeImageTransperency(Image *image,ExceptionInfo *exception)
1454 %
1455 % A description of each parameter follows:
1456 %
1457 % o image: the image sequence
1458 %
1459 % o exception: return any errors or warnings in this structure.
1460 %
1461 */
1463  ExceptionInfo *exception)
1464 {
1465  Image
1466  *dispose_image;
1467 
1468  register Image
1469  *next;
1470 
1471  /*
1472  Run the image through the animation sequence
1473  */
1474  assert(image != (Image *) NULL);
1475  assert(image->signature == MagickCoreSignature);
1476  if (image->debug != MagickFalse)
1477  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1478  assert(exception != (ExceptionInfo *) NULL);
1479  assert(exception->signature == MagickCoreSignature);
1480  next=GetFirstImageInList(image);
1481  dispose_image=CloneImage(next,next->page.width,next->page.height,
1482  MagickTrue,exception);
1483  if (dispose_image == (Image *) NULL)
1484  return;
1485  dispose_image->page=next->page;
1486  dispose_image->page.x=0;
1487  dispose_image->page.y=0;
1488  dispose_image->dispose=NoneDispose;
1491  (void) SetImageBackgroundColor(dispose_image,exception);
1492 
1493  while ( next != (Image *) NULL )
1494  {
1495  Image
1496  *current_image;
1497 
1498  /*
1499  Overlay this frame's image over the previous disposal image
1500  */
1501  current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
1502  if (current_image == (Image *) NULL)
1503  {
1504  dispose_image=DestroyImage(dispose_image);
1505  return;
1506  }
1507  (void) CompositeImage(current_image,next,next->alpha_trait != UndefinedPixelTrait ?
1509  exception);
1510  /*
1511  At this point the image would be displayed, for the delay period
1512  **
1513  Work out the disposal of the previous image
1514  */
1515  if (next->dispose == BackgroundDispose)
1516  {
1518  bounds=next->page;
1519 
1520  bounds.width=next->columns;
1521  bounds.height=next->rows;
1522  if (bounds.x < 0)
1523  {
1524  bounds.width+=bounds.x;
1525  bounds.x=0;
1526  }
1527  if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
1528  bounds.width=current_image->columns-bounds.x;
1529  if (bounds.y < 0)
1530  {
1531  bounds.height+=bounds.y;
1532  bounds.y=0;
1533  }
1534  if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
1535  bounds.height=current_image->rows-bounds.y;
1536  ClearBounds(current_image, &bounds,exception);
1537  }
1538  if (next->dispose != PreviousDispose)
1539  {
1540  dispose_image=DestroyImage(dispose_image);
1541  dispose_image=current_image;
1542  }
1543  else
1544  current_image=DestroyImage(current_image);
1545 
1546  /*
1547  Optimize Transparency of the next frame (if present)
1548  */
1549  next=GetNextImageInList(next);
1550  if (next != (Image *) NULL) {
1551  (void) CompositeImage(next,dispose_image,ChangeMaskCompositeOp,
1552  MagickTrue,-(next->page.x),-(next->page.y),exception);
1553  }
1554  }
1555  dispose_image=DestroyImage(dispose_image);
1556  return;
1557 }
1558 
1559 /*
1560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1561 % %
1562 % %
1563 % %
1564 % R e m o v e D u p l i c a t e L a y e r s %
1565 % %
1566 % %
1567 % %
1568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1569 %
1570 % RemoveDuplicateLayers() removes any image that is exactly the same as the
1571 % next image in the given image list. Image size and virtual canvas offset
1572 % must also match, though not the virtual canvas size itself.
1573 %
1574 % No check is made with regards to image disposal setting, though it is the
1575 % dispose setting of later image that is kept. Also any time delays are also
1576 % added together. As such coalesced image animations should still produce the
1577 % same result, though with duplicte frames merged into a single frame.
1578 %
1579 % The format of the RemoveDuplicateLayers method is:
1580 %
1581 % void RemoveDuplicateLayers(Image **image, ExceptionInfo *exception)
1582 %
1583 % A description of each parameter follows:
1584 %
1585 % o images: the image list
1586 %
1587 % o exception: return any errors or warnings in this structure.
1588 %
1589 */
1591  ExceptionInfo *exception)
1592 {
1593  register Image
1594  *curr,
1595  *next;
1596 
1598  bounds;
1599 
1600  assert((*images) != (const Image *) NULL);
1601  assert((*images)->signature == MagickCoreSignature);
1602  if ((*images)->debug != MagickFalse)
1603  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
1604  assert(exception != (ExceptionInfo *) NULL);
1605  assert(exception->signature == MagickCoreSignature);
1606 
1607  curr=GetFirstImageInList(*images);
1608  for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next)
1609  {
1610  if ( curr->columns != next->columns || curr->rows != next->rows
1611  || curr->page.x != next->page.x || curr->page.y != next->page.y )
1612  continue;
1613  bounds=CompareImagesBounds(curr,next,CompareAnyLayer,exception);
1614  if ( bounds.x < 0 ) {
1615  /*
1616  the two images are the same, merge time delays and delete one.
1617  */
1618  size_t time;
1619  time = curr->delay*1000/curr->ticks_per_second;
1620  time += next->delay*1000/next->ticks_per_second;
1621  next->ticks_per_second = 100L;
1622  next->delay = time*curr->ticks_per_second/1000;
1623  next->iterations = curr->iterations;
1624  *images = curr;
1625  (void) DeleteImageFromList(images);
1626  }
1627  }
1628  *images = GetFirstImageInList(*images);
1629 }
1630 
1631 /*
1632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633 % %
1634 % %
1635 % %
1636 % R e m o v e Z e r o D e l a y L a y e r s %
1637 % %
1638 % %
1639 % %
1640 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1641 %
1642 % RemoveZeroDelayLayers() removes any image that as a zero delay time. Such
1643 % images generally represent intermediate or partial updates in GIF
1644 % animations used for file optimization. They are not ment to be displayed
1645 % to users of the animation. Viewable images in an animation should have a
1646 % time delay of 3 or more centi-seconds (hundredths of a second).
1647 %
1648 % However if all the frames have a zero time delay, then either the animation
1649 % is as yet incomplete, or it is not a GIF animation. This a non-sensible
1650 % situation, so no image will be removed and a 'Zero Time Animation' warning
1651 % (exception) given.
1652 %
1653 % No warning will be given if no image was removed because all images had an
1654 % appropriate non-zero time delay set.
1655 %
1656 % Due to the special requirements of GIF disposal handling, GIF animations
1657 % should be coalesced first, before calling this function, though that is not
1658 % a requirement.
1659 %
1660 % The format of the RemoveZeroDelayLayers method is:
1661 %
1662 % void RemoveZeroDelayLayers(Image **image, ExceptionInfo *exception)
1663 %
1664 % A description of each parameter follows:
1665 %
1666 % o images: the image list
1667 %
1668 % o exception: return any errors or warnings in this structure.
1669 %
1670 */
1672  ExceptionInfo *exception)
1673 {
1674  Image
1675  *i;
1676 
1677  assert((*images) != (const Image *) NULL);
1678  assert((*images)->signature == MagickCoreSignature);
1679  if ((*images)->debug != MagickFalse)
1680  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
1681  assert(exception != (ExceptionInfo *) NULL);
1682  assert(exception->signature == MagickCoreSignature);
1683 
1684  i=GetFirstImageInList(*images);
1685  for ( ; i != (Image *) NULL; i=GetNextImageInList(i))
1686  if ( i->delay != 0L ) break;
1687  if ( i == (Image *) NULL ) {
1689  "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename);
1690  return;
1691  }
1692  i=GetFirstImageInList(*images);
1693  while ( i != (Image *) NULL )
1694  {
1695  if ( i->delay == 0L ) {
1696  (void) DeleteImageFromList(&i);
1697  *images=i;
1698  }
1699  else
1700  i=GetNextImageInList(i);
1701  }
1702  *images=GetFirstImageInList(*images);
1703 }
1704 
1705 /*
1706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1707 % %
1708 % %
1709 % %
1710 % C o m p o s i t e L a y e r s %
1711 % %
1712 % %
1713 % %
1714 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1715 %
1716 % CompositeLayers() compose the source image sequence over the destination
1717 % image sequence, starting with the current image in both lists.
1718 %
1719 % Each layer from the two image lists are composted together until the end of
1720 % one of the image lists is reached. The offset of each composition is also
1721 % adjusted to match the virtual canvas offsets of each layer. As such the
1722 % given offset is relative to the virtual canvas, and not the actual image.
1723 %
1724 % Composition uses given x and y offsets, as the 'origin' location of the
1725 % source images virtual canvas (not the real image) allowing you to compose a
1726 % list of 'layer images' into the destiantioni images. This makes it well
1727 % sutiable for directly composing 'Clears Frame Animations' or 'Coaleased
1728 % Animations' onto a static or other 'Coaleased Animation' destination image
1729 % list. GIF disposal handling is not looked at.
1730 %
1731 % Special case:- If one of the image sequences is the last image (just a
1732 % single image remaining), that image is repeatally composed with all the
1733 % images in the other image list. Either the source or destination lists may
1734 % be the single image, for this situation.
1735 %
1736 % In the case of a single destination image (or last image given), that image
1737 % will ve cloned to match the number of images remaining in the source image
1738 % list.
1739 %
1740 % This is equivelent to the "-layer Composite" Shell API operator.
1741 %
1742 %
1743 % The format of the CompositeLayers method is:
1744 %
1745 % void CompositeLayers(Image *destination, const CompositeOperator
1746 % compose, Image *source, const ssize_t x_offset, const ssize_t y_offset,
1747 % ExceptionInfo *exception);
1748 %
1749 % A description of each parameter follows:
1750 %
1751 % o destination: the destination images and results
1752 %
1753 % o source: source image(s) for the layer composition
1754 %
1755 % o compose, x_offset, y_offset: arguments passed on to CompositeImages()
1756 %
1757 % o exception: return any errors or warnings in this structure.
1758 %
1759 */
1760 
1761 static inline void CompositeCanvas(Image *destination,
1762  const CompositeOperator compose,Image *source,ssize_t x_offset,
1763  ssize_t y_offset,ExceptionInfo *exception)
1764 {
1765  x_offset+=source->page.x-destination->page.x;
1766  y_offset+=source->page.y-destination->page.y;
1767  (void) CompositeImage(destination,source,compose,MagickTrue,x_offset,
1768  y_offset,exception);
1769 }
1770 
1772  const CompositeOperator compose, Image *source,const ssize_t x_offset,
1773  const ssize_t y_offset,ExceptionInfo *exception)
1774 {
1775  assert(destination != (Image *) NULL);
1776  assert(destination->signature == MagickCoreSignature);
1777  assert(source != (Image *) NULL);
1778  assert(source->signature == MagickCoreSignature);
1779  assert(exception != (ExceptionInfo *) NULL);
1780  assert(exception->signature == MagickCoreSignature);
1781  if (source->debug != MagickFalse || destination->debug != MagickFalse)
1782  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
1783  source->filename, destination->filename);
1784 
1785  /*
1786  Overlay single source image over destation image/list
1787  */
1788  if ( source->next == (Image *) NULL )
1789  while ( destination != (Image *) NULL )
1790  {
1791  CompositeCanvas(destination, compose, source, x_offset, y_offset,
1792  exception);
1793  destination=GetNextImageInList(destination);
1794  }
1795 
1796  /*
1797  Overlay source image list over single destination.
1798  Multiple clones of destination image are created to match source list.
1799  Original Destination image becomes first image of generated list.
1800  As such the image list pointer does not require any change in caller.
1801  Some animation attributes however also needs coping in this case.
1802  */
1803  else if ( destination->next == (Image *) NULL )
1804  {
1805  Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
1806 
1807  CompositeCanvas(destination, compose, source, x_offset, y_offset,
1808  exception);
1809  /* copy source image attributes ? */
1810  if ( source->next != (Image *) NULL )
1811  {
1812  destination->delay = source->delay;
1813  destination->iterations = source->iterations;
1814  }
1815  source=GetNextImageInList(source);
1816 
1817  while ( source != (Image *) NULL )
1818  {
1819  AppendImageToList(&destination,
1820  CloneImage(dest,0,0,MagickTrue,exception));
1821  destination=GetLastImageInList(destination);
1822 
1823  CompositeCanvas(destination, compose, source, x_offset, y_offset,
1824  exception);
1825  destination->delay = source->delay;
1826  destination->iterations = source->iterations;
1827  source=GetNextImageInList(source);
1828  }
1829  dest=DestroyImage(dest);
1830  }
1831 
1832  /*
1833  Overlay a source image list over a destination image list
1834  until either list runs out of images. (Does not repeat)
1835  */
1836  else
1837  while ( source != (Image *) NULL && destination != (Image *) NULL )
1838  {
1839  CompositeCanvas(destination, compose, source, x_offset, y_offset,
1840  exception);
1841  source=GetNextImageInList(source);
1842  destination=GetNextImageInList(destination);
1843  }
1844 }
1845 
1846 /*
1847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1848 % %
1849 % %
1850 % %
1851 % M e r g e I m a g e L a y e r s %
1852 % %
1853 % %
1854 % %
1855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1856 %
1857 % MergeImageLayers() composes all the image layers from the current given
1858 % image onward to produce a single image of the merged layers.
1859 %
1860 % The inital canvas's size depends on the given LayerMethod, and is
1861 % initialized using the first images background color. The images
1862 % are then compositied onto that image in sequence using the given
1863 % composition that has been assigned to each individual image.
1864 %
1865 % The format of the MergeImageLayers is:
1866 %
1867 % Image *MergeImageLayers(const Image *image,
1868 % const LayerMethod method, ExceptionInfo *exception)
1869 %
1870 % A description of each parameter follows:
1871 %
1872 % o image: the image list to be composited together
1873 %
1874 % o method: the method of selecting the size of the initial canvas.
1875 %
1876 % MergeLayer: Merge all layers onto a canvas just large enough
1877 % to hold all the actual images. The virtual canvas of the
1878 % first image is preserved but otherwise ignored.
1879 %
1880 % FlattenLayer: Use the virtual canvas size of first image.
1881 % Images which fall outside this canvas is clipped.
1882 % This can be used to 'fill out' a given virtual canvas.
1883 %
1884 % MosaicLayer: Start with the virtual canvas of the first image,
1885 % enlarging left and right edges to contain all images.
1886 % Images with negative offsets will be clipped.
1887 %
1888 % TrimBoundsLayer: Determine the overall bounds of all the image
1889 % layers just as in "MergeLayer", then adjust the the canvas
1890 % and offsets to be relative to those bounds, without overlaying
1891 % the images.
1892 %
1893 % WARNING: a new image is not returned, the original image
1894 % sequence page data is modified instead.
1895 %
1896 % o exception: return any errors or warnings in this structure.
1897 %
1898 */
1900  ExceptionInfo *exception)
1901 {
1902 #define MergeLayersTag "Merge/Layers"
1903 
1904  Image
1905  *canvas;
1906 
1908  proceed;
1909 
1911  page;
1912 
1913  register const Image
1914  *next;
1915 
1916  size_t
1917  number_images,
1918  height,
1919  width;
1920 
1921  ssize_t
1922  scene;
1923 
1924  assert(image != (Image *) NULL);
1925  assert(image->signature == MagickCoreSignature);
1926  if (image->debug != MagickFalse)
1927  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1928  assert(exception != (ExceptionInfo *) NULL);
1929  assert(exception->signature == MagickCoreSignature);
1930  /*
1931  Determine canvas image size, and its virtual canvas size and offset
1932  */
1933  page=image->page;
1934  width=image->columns;
1935  height=image->rows;
1936  switch (method)
1937  {
1938  case TrimBoundsLayer:
1939  case MergeLayer:
1940  default:
1941  {
1942  next=GetNextImageInList(image);
1943  for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
1944  {
1945  if (page.x > next->page.x)
1946  {
1947  width+=page.x-next->page.x;
1948  page.x=next->page.x;
1949  }
1950  if (page.y > next->page.y)
1951  {
1952  height+=page.y-next->page.y;
1953  page.y=next->page.y;
1954  }
1955  if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x))
1956  width=(size_t) next->page.x+(ssize_t) next->columns-page.x;
1957  if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y))
1958  height=(size_t) next->page.y+(ssize_t) next->rows-page.y;
1959  }
1960  break;
1961  }
1962  case FlattenLayer:
1963  {
1964  if (page.width > 0)
1965  width=page.width;
1966  if (page.height > 0)
1967  height=page.height;
1968  page.x=0;
1969  page.y=0;
1970  break;
1971  }
1972  case MosaicLayer:
1973  {
1974  if (page.width > 0)
1975  width=page.width;
1976  if (page.height > 0)
1977  height=page.height;
1978  for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
1979  {
1980  if (method == MosaicLayer)
1981  {
1982  page.x=next->page.x;
1983  page.y=next->page.y;
1984  if ((ssize_t) width < (next->page.x+(ssize_t) next->columns))
1985  width=(size_t) next->page.x+next->columns;
1986  if ((ssize_t) height < (next->page.y+(ssize_t) next->rows))
1987  height=(size_t) next->page.y+next->rows;
1988  }
1989  }
1990  page.width=width;
1991  page.height=height;
1992  page.x=0;
1993  page.y=0;
1994  }
1995  break;
1996  }
1997  /*
1998  Set virtual canvas size if not defined.
1999  */
2000  if (page.width == 0)
2001  page.width=page.x < 0 ? width : width+page.x;
2002  if (page.height == 0)
2003  page.height=page.y < 0 ? height : height+page.y;
2004  /*
2005  Handle "TrimBoundsLayer" method separately to normal 'layer merge'.
2006  */
2007  if (method == TrimBoundsLayer)
2008  {
2009  number_images=GetImageListLength(image);
2010  for (scene=0; scene < (ssize_t) number_images; scene++)
2011  {
2012  image->page.x-=page.x;
2013  image->page.y-=page.y;
2014  image->page.width=width;
2015  image->page.height=height;
2016  proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2017  number_images);
2018  if (proceed == MagickFalse)
2019  break;
2020  image=GetNextImageInList(image);
2021  if (image == (Image *) NULL)
2022  break;
2023  }
2024  return((Image *) NULL);
2025  }
2026  /*
2027  Create canvas size of width and height, and background color.
2028  */
2029  canvas=CloneImage(image,width,height,MagickTrue,exception);
2030  if (canvas == (Image *) NULL)
2031  return((Image *) NULL);
2032  (void) SetImageBackgroundColor(canvas,exception);
2033  canvas->page=page;
2034  canvas->dispose=UndefinedDispose;
2035  /*
2036  Compose images onto canvas, with progress monitor
2037  */
2038  number_images=GetImageListLength(image);
2039  for (scene=0; scene < (ssize_t) number_images; scene++)
2040  {
2041  (void) CompositeImage(canvas,image,image->compose,MagickTrue,image->page.x-
2042  canvas->page.x,image->page.y-canvas->page.y,exception);
2043  proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2044  number_images);
2045  if (proceed == MagickFalse)
2046  break;
2047  image=GetNextImageInList(image);
2048  if (image == (Image *) NULL)
2049  break;
2050  }
2051  return(canvas);
2052 }
2053 
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:1381
MagickExport Image * CoalesceImages(const Image *image, ExceptionInfo *exception)
Definition: layer.c:229
MagickExport Image * OptimizePlusImageLayers(const Image *image, ExceptionInfo *exception)
Definition: layer.c:1425
#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
PixelTrait alpha_trait
Definition: pixel.h:176
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:1590
#define OpaqueAlpha
Definition: image.h:25
MagickExport ExceptionInfo * AcquireExceptionInfo(void)
Definition: exception.c:108
MagickExport Image * MergeImageLayers(Image *image, const LayerMethod method, ExceptionInfo *exception)
Definition: layer.c:1899
size_t delay
Definition: image.h:240
#define DelDispose
Definition: layer.c:908
MagickRealType alpha
Definition: pixel.h:188
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:539
size_t width
Definition: geometry.h:129
Definition: log.h:52
ssize_t MagickOffsetType
Definition: magick-type.h:127
MagickExport void GetPixelInfo(const Image *image, PixelInfo *pixel)
Definition: pixel.c:2161
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:784
#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:541
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:378
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:916
MagickExport const char * CommandOptionToMnemonic(const CommandOption option, const ssize_t type)
Definition: option.c:2666
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:3154
MagickExport void * AcquireQuantumMemory(const size_t count, const size_t quantum)
Definition: memory.c:529
MagickExport MagickBooleanType CloneImageProfiles(Image *image, const Image *clone_image)
Definition: profile.c:141
RectangleInfo page
Definition: image.h:212
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:1058
MagickExport MagickBooleanType LogMagickEvent(const LogEventType type, const char *module, const char *function, const size_t line, const char *format,...)
Definition: log.c:1397
MagickExport MagickBooleanType SetImageBackgroundColor(Image *image, ExceptionInfo *exception)
Definition: image.c:2308
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:133
struct _Image * next
Definition: image.h:348
MagickExport Image * GetLastImageInList(const Image *images)
Definition: list.c:718
size_t height
Definition: geometry.h:129
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:1771
MagickExport Image * DestroyImageList(Image *images)
Definition: list.c:442
static void CompositeCanvas(Image *destination, const CompositeOperator compose, Image *source, ssize_t x_offset, ssize_t y_offset, ExceptionInfo *exception)
Definition: layer.c:1761
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:1392
#define GetMagickModule()
Definition: log.h:28
#define ThrowImageException(severity, tag)
MagickExport MagickBooleanType SyncAuthenticPixels(Image *image, ExceptionInfo *exception)
Definition: cache.c:5195
MagickExport void OptimizeImageTransparency(const Image *image, ExceptionInfo *exception)
Definition: layer.c:1462
unsigned short Quantum
Definition: magick-type.h:82
MagickExport Image * GetNextImageInList(const Image *images)
Definition: list.c:752
MagickExport MagickBooleanType IsFuzzyEquivalencePixelInfo(const PixelInfo *p, const PixelInfo *q)
Definition: pixel.c:6049
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:1038
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:1671
ssize_t y
Definition: geometry.h:133
MagickExport void DeleteImageFromList(Image **images)
Definition: list.c:291
PixelInfo background_color
Definition: image.h:179
MagickExport size_t GetImageListLength(const Image *images)
Definition: list.c:680
MagickExport Image * DestroyImage(Image *image)
Definition: image.c:1182
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:799
#define QuantumRange
Definition: magick-type.h:83
MagickBooleanType debug
Definition: image.h:334
MagickExport ExceptionInfo * DestroyExceptionInfo(ExceptionInfo *exception)
Definition: exception.c:417