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