40#include "MagickCore/studio.h"
41#include "MagickCore/artifact.h"
42#include "MagickCore/attribute.h"
43#include "MagickCore/cache.h"
44#include "MagickCore/channel.h"
45#include "MagickCore/color.h"
46#include "MagickCore/color-private.h"
47#include "MagickCore/composite.h"
48#include "MagickCore/effect.h"
49#include "MagickCore/exception.h"
50#include "MagickCore/exception-private.h"
51#include "MagickCore/geometry.h"
52#include "MagickCore/image.h"
53#include "MagickCore/layer.h"
54#include "MagickCore/list.h"
55#include "MagickCore/memory_.h"
56#include "MagickCore/monitor.h"
57#include "MagickCore/monitor-private.h"
58#include "MagickCore/option.h"
59#include "MagickCore/pixel-accessor.h"
60#include "MagickCore/property.h"
61#include "MagickCore/profile.h"
62#include "MagickCore/resource_.h"
63#include "MagickCore/resize.h"
64#include "MagickCore/statistic.h"
65#include "MagickCore/string_.h"
66#include "MagickCore/thread-private.h"
67#include "MagickCore/transform.h"
110 if ((image->alpha_trait & BlendPixelTrait) == 0)
111 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
112 for (y=0; y < (ssize_t) bounds->height; y++)
120 q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
121 if (q == (Quantum *) NULL)
123 for (x=0; x < (ssize_t) bounds->width; x++)
125 SetPixelAlpha(image,TransparentAlpha,q);
126 q+=GetPixelChannels(image);
128 if (SyncAuthenticPixels(image,exception) == MagickFalse)
167static MagickBooleanType IsBoundsCleared(
const Image *image1,
182 for (y=0; y < (ssize_t) bounds->height; y++)
184 p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,exception);
185 q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,exception);
186 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
188 for (x=0; x < (ssize_t) bounds->width; x++)
190 if ((GetPixelAlpha(image1,p) >= (Quantum) (QuantumRange/2)) &&
191 (GetPixelAlpha(image2,q) < (Quantum) (QuantumRange/2)))
193 p+=GetPixelChannels(image1);
194 q+=GetPixelChannels(image2);
196 if (x < (ssize_t) bounds->width)
199 return(y < (ssize_t) bounds->height ? MagickTrue : MagickFalse);
247 assert(image != (
Image *) NULL);
248 assert(image->signature == MagickCoreSignature);
250 assert(exception->signature == MagickCoreSignature);
251 if (IsEventLogging() != MagickFalse)
252 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
253 next=GetFirstImageInList(image);
255 if (bounds.width == 0)
257 bounds.width=next->columns;
259 bounds.width+=(size_t) bounds.x;
261 if (bounds.height == 0)
263 bounds.height=next->rows;
265 bounds.height+=(size_t) bounds.y;
269 coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue,
271 if (coalesce_image == (
Image *) NULL)
272 return((
Image *) NULL);
273 coalesce_image->background_color.alpha_trait=BlendPixelTrait;
274 coalesce_image->background_color.alpha=(MagickRealType) TransparentAlpha;
275 (void) SetImageBackgroundColor(coalesce_image,exception);
276 coalesce_image->alpha_trait=next->alpha_trait;
277 coalesce_image->page=bounds;
278 coalesce_image->dispose=NoneDispose;
282 dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
283 if (dispose_image == (
Image *) NULL)
285 coalesce_image=DestroyImage(coalesce_image);
286 return((
Image *) NULL);
288 dispose_image->background_color.alpha_trait=BlendPixelTrait;
289 (void) CompositeImage(coalesce_image,next,CopyCompositeOp,MagickTrue,
290 next->page.x,next->page.y,exception);
291 next=GetNextImageInList(next);
292 for ( ; next != (
Image *) NULL; next=GetNextImageInList(next))
300 previous=GetPreviousImageInList(next);
301 bounds=previous->page;
302 bounds.width=previous->columns;
303 bounds.height=previous->rows;
306 bounds.width=(size_t) ((ssize_t) bounds.width+bounds.x);
309 if ((bounds.x+(ssize_t) bounds.width) > (ssize_t) coalesce_image->columns)
310 bounds.width=(size_t) ((ssize_t) coalesce_image->columns-bounds.x);
313 bounds.height=(size_t) ((ssize_t) bounds.height+bounds.y);
316 if ((bounds.y+(ssize_t) bounds.height) > (ssize_t) coalesce_image->rows)
317 bounds.height=(size_t) ((ssize_t) coalesce_image->rows-bounds.y);
321 if (GetPreviousImageInList(next)->dispose != PreviousDispose)
323 dispose_image=DestroyImage(dispose_image);
324 dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
325 if (dispose_image == (
Image *) NULL)
327 coalesce_image=DestroyImageList(coalesce_image);
328 return((
Image *) NULL);
330 dispose_image->background_color.alpha_trait=BlendPixelTrait;
335 if (next->previous->dispose == BackgroundDispose)
336 ClearBounds(dispose_image,&bounds,exception);
340 coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception);
341 if (coalesce_image->next != (
Image *) NULL)
342 coalesce_image->next->previous=coalesce_image;
343 previous=coalesce_image;
344 coalesce_image=GetNextImageInList(coalesce_image);
345 coalesce_image->background_color.alpha_trait=BlendPixelTrait;
346 attribute=GetImageProperty(next,
"webp:mux-blend",exception);
347 if (attribute == (
const char *) NULL)
348 (void) CompositeImage(coalesce_image,next,
349 next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp :
350 CopyCompositeOp,MagickTrue,next->page.x,next->page.y,exception);
352 (
void) CompositeImage(coalesce_image,next,
353 LocaleCompare(attribute,
"AtopBackgroundAlphaBlend") == 0 ?
354 OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
356 (void) CloneImageProfiles(coalesce_image,next);
357 (void) CloneImageProperties(coalesce_image,next);
358 (void) CloneImageArtifacts(coalesce_image,next);
359 coalesce_image->page=previous->page;
363 if (IsBoundsCleared(previous,coalesce_image,&bounds,exception) != MagickFalse)
364 coalesce_image->dispose=BackgroundDispose;
366 coalesce_image->dispose=NoneDispose;
367 previous->dispose=coalesce_image->dispose;
369 dispose_image=DestroyImage(dispose_image);
370 return(GetFirstImageInList(coalesce_image));
415 assert(images != (
Image *) NULL);
416 assert(images->signature == MagickCoreSignature);
418 assert(exception->signature == MagickCoreSignature);
419 if (IsEventLogging() != MagickFalse)
420 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
421 image=GetFirstImageInList(images);
422 dispose_image=CloneImage(image,image->page.width,image->page.height,
423 MagickTrue,exception);
424 if (dispose_image == (
Image *) NULL)
425 return((
Image *) NULL);
426 dispose_image->page=image->page;
427 dispose_image->page.x=0;
428 dispose_image->page.y=0;
429 dispose_image->dispose=NoneDispose;
430 dispose_image->background_color.alpha_trait=BlendPixelTrait;
431 dispose_image->background_color.alpha=(MagickRealType) TransparentAlpha;
432 (void) SetImageBackgroundColor(dispose_image,exception);
433 dispose_images=NewImageList();
434 for (next=image; image != (
Image *) NULL; image=GetNextImageInList(image))
442 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
443 if (current_image == (
Image *) NULL)
445 dispose_images=DestroyImageList(dispose_images);
446 dispose_image=DestroyImage(dispose_image);
447 return((
Image *) NULL);
449 current_image->background_color.alpha_trait=BlendPixelTrait;
450 (void) CompositeImage(current_image,next,
451 next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp,
452 MagickTrue,next->page.x,next->page.y,exception);
456 if (next->dispose == BackgroundDispose)
459 bounds.width=next->columns;
460 bounds.height=next->rows;
463 bounds.width=(size_t) ((ssize_t) bounds.width+bounds.x);
466 if ((bounds.x+(ssize_t) bounds.width) > (ssize_t) current_image->columns)
467 bounds.width=(size_t) ((ssize_t) current_image->columns-bounds.x);
470 bounds.height=(size_t) ((ssize_t) bounds.height+bounds.y);
473 if ((bounds.y+(ssize_t) bounds.height) > (ssize_t) current_image->rows)
474 bounds.height=(size_t) ((ssize_t) current_image->rows-bounds.y);
475 ClearBounds(current_image,&bounds,exception);
480 if (next->dispose == PreviousDispose)
481 current_image=DestroyImage(current_image);
484 dispose_image=DestroyImage(dispose_image);
485 dispose_image=current_image;
486 current_image=(
Image *) NULL;
495 dispose=CloneImage(dispose_image,0,0,MagickTrue,exception);
496 if (dispose == (
Image *) NULL)
498 dispose_images=DestroyImageList(dispose_images);
499 dispose_image=DestroyImage(dispose_image);
500 return((
Image *) NULL);
502 dispose_image->background_color.alpha_trait=BlendPixelTrait;
503 (void) CloneImageProfiles(dispose,next);
504 (void) CloneImageProperties(dispose,next);
505 (void) CloneImageArtifacts(dispose,next);
508 dispose->dispose=next->dispose;
509 AppendImageToList(&dispose_images,dispose);
512 dispose_image=DestroyImage(dispose_image);
513 return(GetFirstImageInList(dispose_images));
547static MagickBooleanType ComparePixels(
const LayerMethod method,
557 if (method == CompareAnyLayer)
558 return(IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse ? MagickTrue : MagickFalse);
559 o1 = (p->alpha_trait != UndefinedPixelTrait) ? p->alpha : (double)
561 o2 = (q->alpha_trait != UndefinedPixelTrait) ? q->alpha : (double)
566 if (method == CompareClearLayer)
567 return((MagickBooleanType) ( (o1 >= ((
double) QuantumRange/2.0)) &&
568 (o2 < ((
double) QuantumRange/2.0)) ) );
572 if (method == CompareOverlayLayer)
574 if (o2 < ((
double) QuantumRange/2.0))
576 return(IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse ? MagickTrue :
639 if (IsEventLogging() != MagickFalse)
640 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",
641 alpha_image->filename);
642 GetPixelInfo(alpha_image,&alpha_pixel);
643 GetPixelInfo(beta_image,&beta_pixel);
644 for (x=0; x < (ssize_t) alpha_image->columns; x++)
646 p=GetVirtualPixels(alpha_image,x,0,1,alpha_image->rows,exception);
647 q=GetVirtualPixels(beta_image,x,0,1,beta_image->rows,exception);
648 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
650 for (y=0; y < (ssize_t) alpha_image->rows; y++)
652 GetPixelInfoPixel(alpha_image,p,&alpha_pixel);
653 GetPixelInfoPixel(beta_image,q,&beta_pixel);
654 if (ComparePixels(method,&alpha_pixel,&beta_pixel) != MagickFalse)
656 p+=GetPixelChannels(alpha_image);
657 q+=GetPixelChannels(beta_image);
659 if (y < (ssize_t) alpha_image->rows)
662 if (x >= (ssize_t) alpha_image->columns)
674 for (x=(ssize_t) alpha_image->columns-1; x >= 0; x--)
676 p=GetVirtualPixels(alpha_image,x,0,1,alpha_image->rows,exception);
677 q=GetVirtualPixels(beta_image,x,0,1,beta_image->rows,exception);
678 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
680 for (y=0; y < (ssize_t) alpha_image->rows; y++)
682 GetPixelInfoPixel(alpha_image,p,&alpha_pixel);
683 GetPixelInfoPixel(beta_image,q,&beta_pixel);
684 if (ComparePixels(method,&alpha_pixel,&beta_pixel) != MagickFalse)
686 p+=GetPixelChannels(alpha_image);
687 q+=GetPixelChannels(beta_image);
689 if (y < (ssize_t) alpha_image->rows)
692 bounds.width=(size_t) (x-bounds.x+1);
693 for (y=0; y < (ssize_t) alpha_image->rows; y++)
695 p=GetVirtualPixels(alpha_image,0,y,alpha_image->columns,1,exception);
696 q=GetVirtualPixels(beta_image,0,y,beta_image->columns,1,exception);
697 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
699 for (x=0; x < (ssize_t) alpha_image->columns; x++)
701 GetPixelInfoPixel(alpha_image,p,&alpha_pixel);
702 GetPixelInfoPixel(beta_image,q,&beta_pixel);
703 if (ComparePixels(method,&alpha_pixel,&beta_pixel) != MagickFalse)
705 p+=GetPixelChannels(alpha_image);
706 q+=GetPixelChannels(beta_image);
708 if (x < (ssize_t) alpha_image->columns)
712 for (y=(ssize_t) alpha_image->rows-1; y >= 0; y--)
714 p=GetVirtualPixels(alpha_image,0,y,alpha_image->columns,1,exception);
715 q=GetVirtualPixels(beta_image,0,y,beta_image->columns,1,exception);
716 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
718 for (x=0; x < (ssize_t) alpha_image->columns; x++)
720 GetPixelInfoPixel(alpha_image,p,&alpha_pixel);
721 GetPixelInfoPixel(beta_image,q,&beta_pixel);
722 if (ComparePixels(method,&alpha_pixel,&beta_pixel) != MagickFalse)
724 p+=GetPixelChannels(alpha_image);
725 q+=GetPixelChannels(beta_image);
727 if (x < (ssize_t) alpha_image->columns)
730 bounds.height=(size_t) (y-bounds.y+1);
772MagickExport
Image *CompareImagesLayers(
const Image *image,
789 assert(image != (
const Image *) NULL);
790 assert(image->signature == MagickCoreSignature);
792 assert(exception->signature == MagickCoreSignature);
793 assert((method == CompareAnyLayer) ||
794 (method == CompareClearLayer) ||
795 (method == CompareOverlayLayer));
796 if (IsEventLogging() != MagickFalse)
797 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
801 next=GetFirstImageInList(image);
803 GetImageListLength(next),
sizeof(*bounds));
805 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
809 image_a=CloneImage(next,next->page.width,next->page.height,
810 MagickTrue,exception);
811 if (image_a == (
Image *) NULL)
814 return((
Image *) NULL);
816 image_a->background_color.alpha_trait=BlendPixelTrait;
817 image_a->background_color.alpha=(MagickRealType) TransparentAlpha;
818 (void) SetImageBackgroundColor(image_a,exception);
819 image_a->page=next->page;
822 (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
823 next->page.y,exception);
828 next=GetNextImageInList(next);
829 for ( ; next != (
const Image *) NULL; next=GetNextImageInList(next))
831 image_b=CloneImage(image_a,0,0,MagickTrue,exception);
832 if (image_b == (
Image *) NULL)
834 image_a=DestroyImage(image_a);
836 return((
Image *) NULL);
838 image_b->background_color.alpha_trait=BlendPixelTrait;
839 (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
840 next->page.y,exception);
841 bounds[i]=CompareImagesBounds(image_b,image_a,method,exception);
842 image_b=DestroyImage(image_b);
845 image_a=DestroyImage(image_a);
849 next=GetFirstImageInList(image);
850 layers=CloneImage(next,0,0,MagickTrue,exception);
851 if (layers == (
Image *) NULL)
854 return((
Image *) NULL);
856 layers->background_color.alpha_trait=BlendPixelTrait;
861 next=GetNextImageInList(next);
862 for ( ; next != (
const Image *) NULL; next=GetNextImageInList(next))
864 if ((bounds[i].x == -1) && (bounds[i].y == -1) &&
865 (bounds[i].width == 1) && (bounds[i].height == 1))
874 image_a=CloneImage(next,0,0,MagickTrue,exception);
875 if (image_a == (
Image *) NULL)
877 image_a->background_color.alpha_trait=BlendPixelTrait;
878 image_b=CropImage(image_a,&bounds[i],exception);
879 image_a=DestroyImage(image_a);
880 if (image_b == (
Image *) NULL)
882 AppendImageToList(&layers,image_b);
886 if (next != (
Image *) NULL)
888 layers=DestroyImageList(layers);
889 return((
Image *) NULL);
891 return(GetFirstImageInList(layers));
937#define DupDispose ((DisposeType)9)
941#define DelDispose ((DisposeType)8)
943#define DEBUG_OPT_FRAME 0
945static Image *OptimizeLayerFrames(
const Image *image,
const LayerMethod method,
977 assert(image != (
const Image *) NULL);
978 assert(image->signature == MagickCoreSignature);
980 assert(exception->signature == MagickCoreSignature);
981 assert(method == OptimizeLayer ||
982 method == OptimizeImageLayer ||
983 method == OptimizePlusLayer);
984 if (IsEventLogging() != MagickFalse)
985 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
989 add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
993 curr=GetFirstImageInList(image);
994 for (; curr != (
Image *) NULL; curr=GetNextImageInList(curr))
996 if ((curr->columns != image->columns) || (curr->rows != image->rows))
997 ThrowImageException(OptionError,
"ImagesAreNotTheSameSize");
999 if ((curr->page.x != 0) || (curr->page.y != 0) ||
1000 (curr->page.width != image->page.width) ||
1001 (curr->page.height != image->page.height))
1002 ThrowImageException(OptionError,
"ImagePagesAreNotCoalesced");
1007 curr=GetFirstImageInList(image);
1009 GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)*
1012 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1013 disposals=(DisposeType *) AcquireQuantumMemory((
size_t)
1014 GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
1015 sizeof(*disposals));
1016 if (disposals == (DisposeType *) NULL)
1019 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1024 prev_image=CloneImage(curr,curr->columns,curr->rows,MagickTrue,exception);
1025 if (prev_image == (
Image *) NULL)
1028 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1029 return((
Image *) NULL);
1031 prev_image->page=curr->page;
1032 prev_image->page.x=0;
1033 prev_image->page.y=0;
1034 prev_image->dispose=NoneDispose;
1035 prev_image->background_color.alpha_trait=BlendPixelTrait;
1036 prev_image->background_color.alpha=(MagickRealType) TransparentAlpha;
1037 (void) SetImageBackgroundColor(prev_image,exception);
1044 (void) FormatLocaleFile(stderr,
"frame %.20g :-\n",(
double) i);
1046 disposals[0]=NoneDispose;
1047 bounds[0]=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
1049 (void) FormatLocaleFile(stderr,
"overlay: %.20gx%.20g%+.20g%+.20g\n\n",
1050 (
double) bounds[i].width,(
double) bounds[i].height,
1051 (
double) bounds[i].x,(
double) bounds[i].y );
1057 bgnd_image=(
Image *) NULL;
1058 dup_image=(
Image *) NULL;
1060 dup_bounds.height=0;
1063 curr=GetNextImageInList(curr);
1064 for ( ; curr != (
const Image *) NULL; curr=GetNextImageInList(curr))
1067 (void) FormatLocaleFile(stderr,
"frame %.20g :-\n",(
double) i);
1072 bounds[i]=CompareImagesBounds(curr->previous,curr,CompareAnyLayer,exception);
1073 cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception);
1074 disposals[i-1]=NoneDispose;
1076 (void) FormatLocaleFile(stderr,
"overlay: %.20gx%.20g%+.20g%+.20g%s%s\n",
1077 (
double) bounds[i].width,(
double) bounds[i].height,
1078 (
double) bounds[i].x,(
double) bounds[i].y,
1079 bounds[i].x < 0?
" (unchanged)":
"",
1080 cleared?
" (pixels cleared)":
"");
1082 if ( bounds[i].x < 0 ) {
1089 if ( add_frames && i>=2 ) {
1090 disposals[i-1]=DelDispose;
1091 disposals[i]=NoneDispose;
1092 bounds[i]=bounds[i-1];
1102 try_bounds=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
1103 try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception);
1105 (void) FormatLocaleFile(stderr,
"test_prev: %.20gx%.20g%+.20g%+.20g%s\n",
1106 (
double) try_bounds.width,(
double) try_bounds.height,
1107 (
double) try_bounds.x,(
double) try_bounds.y,
1108 try_cleared?
" (pixels were cleared)":
"");
1110 if ( (!try_cleared && cleared ) ||
1111 try_bounds.width * try_bounds.height
1112 < bounds[i].width * bounds[i].height )
1114 cleared=try_cleared;
1115 bounds[i]=try_bounds;
1116 disposals[i-1]=PreviousDispose;
1118 (void) FormatLocaleFile(stderr,
"previous: accepted\n");
1120 (void) FormatLocaleFile(stderr,
"previous: rejected\n");
1129 dup_bounds.width=dup_bounds.height=0;
1132 dup_image=CloneImage(curr->previous,0,0,MagickTrue,exception);
1133 if (dup_image == (
Image *) NULL)
1136 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1137 prev_image=DestroyImage(prev_image);
1138 return((
Image *) NULL);
1140 dup_image->background_color.alpha_trait=BlendPixelTrait;
1141 dup_bounds=CompareImagesBounds(dup_image,curr,CompareClearLayer,exception);
1142 ClearBounds(dup_image,&dup_bounds,exception);
1143 try_bounds=CompareImagesBounds(dup_image,curr,CompareAnyLayer,exception);
1145 dup_bounds.width*dup_bounds.height
1146 +try_bounds.width*try_bounds.height
1147 < bounds[i].width * bounds[i].height )
1149 cleared=MagickFalse;
1150 bounds[i]=try_bounds;
1151 disposals[i-1]=DupDispose;
1155 dup_bounds.width=dup_bounds.height=0;
1160 bgnd_image=CloneImage(curr->previous,0,0,MagickTrue,exception);
1161 if (bgnd_image == (
Image *) NULL)
1164 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1165 prev_image=DestroyImage(prev_image);
1166 if ( dup_image != (
Image *) NULL)
1167 dup_image=DestroyImage(dup_image);
1168 return((
Image *) NULL);
1170 bgnd_image->background_color.alpha_trait=BlendPixelTrait;
1171 bgnd_bounds=bounds[i-1];
1172 ClearBounds(bgnd_image,&bgnd_bounds,exception);
1173 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
1174 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1176 (void) FormatLocaleFile(stderr,
"background: %s\n",
1177 try_cleared?
"(pixels cleared)":
"");
1187 try_bounds=CompareImagesBounds(curr->previous,curr,CompareClearLayer,exception);
1189 (void) FormatLocaleFile(stderr,
"expand_clear: %.20gx%.20g%+.20g%+.20g%s\n",
1190 (
double) try_bounds.width,(
double) try_bounds.height,
1191 (
double) try_bounds.x,(
double) try_bounds.y,
1192 try_bounds.x<0?
" (no expand necessary)":
"");
1194 if ( bgnd_bounds.x < 0 )
1195 bgnd_bounds = try_bounds;
1199 (void) FormatLocaleFile(stderr,
"expand_bgnd: %.20gx%.20g%+.20g%+.20g\n",
1200 (
double) bgnd_bounds.width,(
double) bgnd_bounds.height,
1201 (
double) bgnd_bounds.x,(
double) bgnd_bounds.y );
1203 if ( try_bounds.x < bgnd_bounds.x )
1205 bgnd_bounds.width=(size_t) ((ssize_t) bgnd_bounds.width+
1206 bgnd_bounds.x-try_bounds.x);
1207 if ( bgnd_bounds.width < try_bounds.width )
1208 bgnd_bounds.width = try_bounds.width;
1209 bgnd_bounds.x = try_bounds.x;
1213 try_bounds.width=(size_t) ((ssize_t) try_bounds.width+
1214 try_bounds.x-bgnd_bounds.x);
1215 if ( bgnd_bounds.width < try_bounds.width )
1216 bgnd_bounds.width = try_bounds.width;
1218 if ( try_bounds.y < bgnd_bounds.y )
1220 bgnd_bounds.height=(size_t) ((ssize_t) bgnd_bounds.height+
1221 bgnd_bounds.y-try_bounds.y);
1222 if (bgnd_bounds.height < try_bounds.height)
1223 bgnd_bounds.height=try_bounds.height;
1224 bgnd_bounds.y=try_bounds.y;
1228 try_bounds.height=(size_t) ((ssize_t) try_bounds.height+
1229 try_bounds.y-bgnd_bounds.y);
1230 if ( bgnd_bounds.height < try_bounds.height )
1231 bgnd_bounds.height = try_bounds.height;
1234 (void) FormatLocaleFile(stderr,
" to : %.20gx%.20g%+.20g%+.20g\n",
1235 (
double) bgnd_bounds.width,(
double) bgnd_bounds.height,
1236 (
double) bgnd_bounds.x,(
double) bgnd_bounds.y );
1239 ClearBounds(bgnd_image,&bgnd_bounds,exception);
1248 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareClearLayer,exception);
1249 (void) FormatLocaleFile(stderr,
"expand_ctst: %.20gx%.20g%+.20g%+.20g\n",
1250 (
double) try_bounds.width,(
double) try_bounds.height,
1251 (
double) try_bounds.x,(
double) try_bounds.y );
1252 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
1253 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1254 (void) FormatLocaleFile(stderr,
"expand_any : %.20gx%.20g%+.20g%+.20g%s\n",
1255 (
double) try_bounds.width,(
double) try_bounds.height,
1256 (
double) try_bounds.x,(
double) try_bounds.y,
1257 try_cleared?
" (pixels cleared)":
"");
1259 try_bounds=CompareImagesBounds(bgnd_image,curr,CompareOverlayLayer,exception);
1261 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1262 (void) FormatLocaleFile(stderr,
"expand_test: %.20gx%.20g%+.20g%+.20g%s\n",
1263 (
double) try_bounds.width,(
double) try_bounds.height,
1264 (
double) try_bounds.x,(
double) try_bounds.y,
1265 try_cleared?
" (pixels cleared)":
"");
1273 bgnd_bounds.width*bgnd_bounds.height
1274 +try_bounds.width*try_bounds.height
1275 < bounds[i-1].width*bounds[i-1].height
1276 +dup_bounds.width*dup_bounds.height
1277 +bounds[i].width*bounds[i].height )
1279 cleared=MagickFalse;
1280 bounds[i-1]=bgnd_bounds;
1281 bounds[i]=try_bounds;
1282 if ( disposals[i-1] == DupDispose )
1283 dup_image=DestroyImage(dup_image);
1284 disposals[i-1]=BackgroundDispose;
1286 (void) FormatLocaleFile(stderr,
"expand_bgnd: accepted\n");
1288 (void) FormatLocaleFile(stderr,
"expand_bgnd: reject\n");
1296 if ( disposals[i-1] == DupDispose )
1298 if (bgnd_image != (
Image *) NULL)
1299 bgnd_image=DestroyImage(bgnd_image);
1300 prev_image=DestroyImage(prev_image);
1301 prev_image=dup_image, dup_image=(
Image *) NULL;
1302 bounds[i+1]=bounds[i];
1303 bounds[i]=dup_bounds;
1304 disposals[i-1]=DupDispose;
1305 disposals[i]=BackgroundDispose;
1310 if ( dup_image != (
Image *) NULL)
1311 dup_image=DestroyImage(dup_image);
1312 if ( disposals[i-1] != PreviousDispose )
1313 prev_image=DestroyImage(prev_image);
1314 if ( disposals[i-1] == BackgroundDispose )
1315 prev_image=bgnd_image, bgnd_image=(
Image *) NULL;
1316 if (bgnd_image != (
Image *) NULL)
1317 bgnd_image=DestroyImage(bgnd_image);
1318 if ( disposals[i-1] == NoneDispose )
1320 prev_image=ReferenceImage(curr->previous);
1321 if (prev_image == (
Image *) NULL)
1324 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1325 return((
Image *) NULL);
1330 assert(prev_image != (
Image *) NULL);
1331 disposals[i]=disposals[i-1];
1333 (void) FormatLocaleFile(stderr,
"final %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1335 CommandOptionToMnemonic(MagickDisposeOptions,disposals[i-1]),
1336 (
double) bounds[i-1].width,(
double) bounds[i-1].height,
1337 (
double) bounds[i-1].x,(
double) bounds[i-1].y );
1340 (void) FormatLocaleFile(stderr,
"interim %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1342 CommandOptionToMnemonic(MagickDisposeOptions,disposals[i]),
1343 (
double) bounds[i].width,(
double) bounds[i].height,
1344 (
double) bounds[i].x,(
double) bounds[i].y );
1345 (void) FormatLocaleFile(stderr,
"\n");
1349 prev_image=DestroyImage(prev_image);
1353 sans_exception=AcquireExceptionInfo();
1355 curr=GetFirstImageInList(image);
1356 optimized_image=NewImageList();
1357 while ( curr != (
const Image *) NULL )
1359 prev_image=CloneImage(curr,0,0,MagickTrue,exception);
1360 if (prev_image == (
Image *) NULL)
1362 prev_image->background_color.alpha_trait=BlendPixelTrait;
1363 if ( disposals[i] == DelDispose ) {
1365 while ( disposals[i] == DelDispose ) {
1366 time +=(size_t) (curr->delay*1000*
1367 PerceptibleReciprocal((
double) curr->ticks_per_second));
1368 curr=GetNextImageInList(curr);
1371 time += (size_t)(curr->delay*1000*
1372 PerceptibleReciprocal((
double) curr->ticks_per_second));
1373 prev_image->ticks_per_second = 100L;
1374 prev_image->delay = time*(size_t) prev_image->ticks_per_second/1000;
1376 bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
1377 prev_image=DestroyImage(prev_image);
1378 if (bgnd_image == (
Image *) NULL)
1380 bgnd_image->dispose=disposals[i];
1381 if ( disposals[i] == DupDispose ) {
1382 bgnd_image->delay=0;
1383 bgnd_image->dispose=NoneDispose;
1386 curr=GetNextImageInList(curr);
1387 AppendImageToList(&optimized_image,bgnd_image);
1390 sans_exception=DestroyExceptionInfo(sans_exception);
1392 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1393 if (curr != (
Image *) NULL)
1395 optimized_image=DestroyImageList(optimized_image);
1396 return((
Image *) NULL);
1398 return(GetFirstImageInList(optimized_image));
1429MagickExport
Image *OptimizeImageLayers(
const Image *image,
1432 return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
1462MagickExport
Image *OptimizePlusImageLayers(
const Image *image,
1465 return OptimizeLayerFrames(image,OptimizePlusLayer,exception);
1499MagickExport
void OptimizeImageTransparency(
const Image *image,
1511 assert(image != (
Image *) NULL);
1512 assert(image->signature == MagickCoreSignature);
1514 assert(exception->signature == MagickCoreSignature);
1515 if (IsEventLogging() != MagickFalse)
1516 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1517 next=GetFirstImageInList(image);
1518 dispose_image=CloneImage(next,next->page.width,next->page.height,
1519 MagickTrue,exception);
1520 if (dispose_image == (
Image *) NULL)
1522 dispose_image->page=next->page;
1523 dispose_image->page.x=0;
1524 dispose_image->page.y=0;
1525 dispose_image->dispose=NoneDispose;
1526 dispose_image->background_color.alpha_trait=BlendPixelTrait;
1527 dispose_image->background_color.alpha=(MagickRealType) TransparentAlpha;
1528 (void) SetImageBackgroundColor(dispose_image,exception);
1530 while ( next != (
Image *) NULL )
1538 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
1539 if (current_image == (
Image *) NULL)
1541 dispose_image=DestroyImage(dispose_image);
1544 current_image->background_color.alpha_trait=BlendPixelTrait;
1545 (void) CompositeImage(current_image,next,next->alpha_trait != UndefinedPixelTrait ?
1546 OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
1553 if (next->dispose == BackgroundDispose)
1558 bounds.width=next->columns;
1559 bounds.height=next->rows;
1562 bounds.width=(size_t) ((ssize_t) bounds.width+bounds.x);
1565 if ((bounds.x+(ssize_t) bounds.width) > (ssize_t) current_image->columns)
1566 bounds.width=(size_t) ((ssize_t) current_image->columns-bounds.x);
1569 bounds.height=(size_t) ((ssize_t) bounds.height+bounds.y);
1572 if ((bounds.y+(ssize_t) bounds.height) > (ssize_t) current_image->rows)
1573 bounds.height=(size_t) ((ssize_t) current_image->rows-bounds.y);
1574 ClearBounds(current_image,&bounds,exception);
1576 if (next->dispose != PreviousDispose)
1578 dispose_image=DestroyImage(dispose_image);
1579 dispose_image=current_image;
1582 current_image=DestroyImage(current_image);
1587 next=GetNextImageInList(next);
1588 if (next != (
Image *) NULL)
1589 (void) CompositeImage(next,dispose_image,ChangeMaskCompositeOp,
1590 MagickTrue,-(next->page.x),-(next->page.y),exception);
1592 dispose_image=DestroyImage(dispose_image);
1636 assert((*images) != (
const Image *) NULL);
1637 assert((*images)->signature == MagickCoreSignature);
1639 assert(exception->signature == MagickCoreSignature);
1640 if (IsEventLogging() != MagickFalse)
1641 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",
1642 (*images)->filename);
1643 image=GetFirstImageInList(*images);
1644 for ( ; (next=GetNextImageInList(image)) != (
Image *) NULL; image=next)
1646 if ((image->columns != next->columns) || (image->rows != next->rows) ||
1647 (image->page.x != next->page.x) || (image->page.y != next->page.y))
1649 bounds=CompareImagesBounds(image,next,CompareAnyLayer,exception);
1658 time=(size_t) (1000.0*image->delay*
1659 PerceptibleReciprocal((
double) image->ticks_per_second));
1660 time+=(size_t) (1000.0*next->delay*
1661 PerceptibleReciprocal((
double) next->ticks_per_second));
1662 next->ticks_per_second=100L;
1663 next->delay=time*(size_t) image->ticks_per_second/1000;
1664 next->iterations=image->iterations;
1666 (void) DeleteImageFromList(images);
1669 *images=GetFirstImageInList(*images);
1712MagickExport
void RemoveZeroDelayLayers(
Image **images,
1718 assert((*images) != (
const Image *) NULL);
1719 assert((*images)->signature == MagickCoreSignature);
1721 assert(exception->signature == MagickCoreSignature);
1722 if (IsEventLogging() != MagickFalse)
1723 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",
1724 (*images)->filename);
1725 i=GetFirstImageInList(*images);
1726 for ( ; i != (
Image *) NULL; i=GetNextImageInList(i))
1727 if ( i->delay != 0L )
break;
1728 if ( i == (
Image *) NULL ) {
1729 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1730 "ZeroTimeAnimation",
"`%s'",GetFirstImageInList(*images)->filename);
1733 i=GetFirstImageInList(*images);
1734 while ( i != (
Image *) NULL )
1736 if ( i->delay == 0L ) {
1737 (void) DeleteImageFromList(&i);
1741 i=GetNextImageInList(i);
1743 *images=GetFirstImageInList(*images);
1802static inline void CompositeCanvas(
Image *destination,
1803 const CompositeOperator compose,
Image *source,ssize_t x_offset,
1809 x_offset+=source->page.x-destination->page.x;
1810 y_offset+=source->page.y-destination->page.y;
1811 value=GetImageArtifact(source,
"compose:outside-overlay");
1812 (void) CompositeImage(destination,source,compose,
1813 (value != (
const char *) NULL) && (IsStringTrue(value) != MagickFalse) ?
1814 MagickFalse : MagickTrue,x_offset,y_offset,exception);
1817MagickExport
void CompositeLayers(
Image *destination,
1818 const CompositeOperator compose,
Image *source,
const ssize_t x_offset,
1821 assert(destination != (
Image *) NULL);
1822 assert(destination->signature == MagickCoreSignature);
1823 assert(source != (
Image *) NULL);
1824 assert(source->signature == MagickCoreSignature);
1826 assert(exception->signature == MagickCoreSignature);
1827 if (IsEventLogging() != MagickFalse)
1828 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s - %s",
1829 source->filename,destination->filename);
1833 if ( source->next == (
Image *) NULL )
1834 while ( destination != (
Image *) NULL )
1836 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1838 destination=GetNextImageInList(destination);
1848 else if ( destination->next == (
Image *) NULL )
1850 Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
1852 if (dest != (
Image *) NULL)
1854 dest->background_color.alpha_trait=BlendPixelTrait;
1855 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1858 if ( source->next != (
Image *) NULL )
1860 destination->delay=source->delay;
1861 destination->iterations=source->iterations;
1863 source=GetNextImageInList(source);
1864 while (source != (
Image *) NULL)
1866 AppendImageToList(&destination,
1867 CloneImage(dest,0,0,MagickTrue,exception));
1868 destination->background_color.alpha_trait=BlendPixelTrait;
1869 destination=GetLastImageInList(destination);
1870 CompositeCanvas(destination,compose,source,x_offset,y_offset,
1872 destination->delay=source->delay;
1873 destination->iterations=source->iterations;
1874 source=GetNextImageInList(source);
1876 dest=DestroyImage(dest);
1885 while ( source != (
Image *) NULL && destination != (
Image *) NULL )
1887 CompositeCanvas(destination, compose, source, x_offset, y_offset,
1889 source=GetNextImageInList(source);
1890 destination=GetNextImageInList(destination);
1947MagickExport
Image *MergeImageLayers(
Image *image,
const LayerMethod method,
1950#define MergeLayersTag "Merge/Layers"
1972 assert(image != (
Image *) NULL);
1973 assert(image->signature == MagickCoreSignature);
1975 assert(exception->signature == MagickCoreSignature);
1976 if (IsEventLogging() != MagickFalse)
1977 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1982 width=image->columns;
1986 case TrimBoundsLayer:
1990 next=GetNextImageInList(image);
1991 for ( ; next != (
Image *) NULL; next=GetNextImageInList(next))
1993 if (page.x > next->page.x)
1995 width=(size_t) ((ssize_t) width+page.x-next->page.x);
1996 page.x=next->page.x;
1998 if (page.y > next->page.y)
2000 height=(size_t) ((ssize_t) height+page.y-next->page.y);
2001 page.y=next->page.y;
2003 if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x))
2004 width=(size_t) (next->page.x+(ssize_t) next->columns-page.x);
2005 if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y))
2006 height=(size_t) (next->page.y+(ssize_t) next->rows-page.y);
2014 if (page.height > 0)
2024 if (page.height > 0)
2026 for (next=image; next != (
Image *) NULL; next=GetNextImageInList(next))
2028 if ((ssize_t) width < (next->page.x+(ssize_t) next->columns))
2029 width=(size_t) next->page.x+next->columns;
2030 if ((ssize_t) height < (next->page.y+(ssize_t) next->rows))
2031 height=(size_t) next->page.y+next->rows;
2043 if (page.width == 0)
2044 page.width=page.x < 0 ? width : (size_t) ((ssize_t) width+page.x);
2045 if (page.height == 0)
2046 page.height=page.y < 0 ? height : (size_t) ((ssize_t) height+page.y);
2050 if (method == TrimBoundsLayer)
2052 number_images=GetImageListLength(image);
2053 for (scene=0; scene < (ssize_t) number_images; scene++)
2055 image->page.x-=page.x;
2056 image->page.y-=page.y;
2057 image->page.width=width;
2058 image->page.height=height;
2059 proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2061 if (proceed == MagickFalse)
2063 image=GetNextImageInList(image);
2064 if (image == (
Image *) NULL)
2067 return((
Image *) NULL);
2072 canvas=CloneImage(image,width,height,MagickTrue,exception);
2073 if (canvas == (
Image *) NULL)
2074 return((
Image *) NULL);
2075 canvas->background_color.alpha_trait=BlendPixelTrait;
2076 (void) SetImageBackgroundColor(canvas,exception);
2078 canvas->dispose=UndefinedDispose;
2082 number_images=GetImageListLength(image);
2083 for (scene=0; scene < (ssize_t) number_images; scene++)
2085 (void) CompositeImage(canvas,image,image->compose,MagickTrue,image->page.x-
2086 canvas->page.x,image->page.y-canvas->page.y,exception);
2087 proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2089 if (proceed == MagickFalse)
2091 image=GetNextImageInList(image);
2092 if (image == (
Image *) NULL)