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