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