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