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"
108 if (image->matte == MagickFalse)
109 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
110 exception=(&image->exception);
111 for (y=0; y < (ssize_t) bounds->height; y++)
119 q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
122 for (x=0; x < (ssize_t) bounds->width; x++)
124 q->opacity=(Quantum) TransparentOpacity;
127 if (SyncAuthenticPixels(image,exception) == MagickFalse)
166static MagickBooleanType IsBoundsCleared(
const Image *image1,
181 for (y=0; y < (ssize_t) bounds->height; y++)
183 p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,
185 q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,
189 for (x=0; x < (ssize_t) bounds->width; x++)
191 if ((GetPixelOpacity(p) <= (Quantum) (QuantumRange/2)) &&
192 (GetPixelOpacity(q) > (Quantum) (QuantumRange/2)))
197 if (x < (ssize_t) bounds->width)
200 return(y < (ssize_t) bounds->height ? MagickTrue : MagickFalse);
248 assert(image != (
Image *) NULL);
249 assert(image->signature == MagickCoreSignature);
251 assert(exception->signature == MagickCoreSignature);
252 if (IsEventLogging() != MagickFalse)
253 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
257 next=GetFirstImageInList(image);
259 if (bounds.width == 0)
261 bounds.width=next->columns;
263 bounds.width+=bounds.x;
265 if (bounds.height == 0)
267 bounds.height=next->rows;
269 bounds.height+=bounds.y;
273 coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue,
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;
285 dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
286 (void) CompositeImage(coalesce_image,CopyCompositeOp,next,next->page.x,
288 next=GetNextImageInList(next);
289 for ( ; next != (
Image *) NULL; next=GetNextImageInList(next))
294 previous=GetPreviousImageInList(next);
295 bounds=previous->page;
296 bounds.width=previous->columns;
297 bounds.height=previous->rows;
300 bounds.width+=bounds.x;
303 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) coalesce_image->columns)
304 bounds.width=coalesce_image->columns-bounds.x;
307 bounds.height+=bounds.y;
310 if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) coalesce_image->rows)
311 bounds.height=coalesce_image->rows-bounds.y;
315 if (GetPreviousImageInList(next)->dispose != PreviousDispose)
317 dispose_image=DestroyImage(dispose_image);
318 dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
319 if (dispose_image == (
Image *) NULL)
321 coalesce_image=DestroyImageList(coalesce_image);
322 return((
Image *) NULL);
328 if (next->previous->dispose == BackgroundDispose)
329 ClearBounds(dispose_image,&bounds);
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;
347 if (IsBoundsCleared(previous,coalesce_image,&bounds,exception) != MagickFalse)
348 coalesce_image->dispose=BackgroundDispose;
350 coalesce_image->dispose=NoneDispose;
351 previous->dispose=coalesce_image->dispose;
353 dispose_image=DestroyImage(dispose_image);
354 return(GetFirstImageInList(coalesce_image));
399 assert(images != (
Image *) NULL);
400 assert(images->signature == MagickCoreSignature);
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))
425 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
426 if (current_image == (
Image *) NULL)
428 dispose_images=DestroyImageList(dispose_images);
429 dispose_image=DestroyImage(dispose_image);
430 return((
Image *) NULL);
432 (void) CompositeImage(current_image,next->matte != MagickFalse ?
433 OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
437 if (next->dispose == BackgroundDispose)
440 bounds.width=next->columns;
441 bounds.height=next->rows;
444 bounds.width+=bounds.x;
447 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
448 bounds.width=current_image->columns-bounds.x;
451 bounds.height+=bounds.y;
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);
461 if (next->dispose == PreviousDispose)
462 current_image=DestroyImage(current_image);
465 dispose_image=DestroyImage(dispose_image);
466 dispose_image=current_image;
467 current_image=(
Image *) NULL;
476 dispose=CloneImage(dispose_image,0,0,MagickTrue,exception);
477 if (dispose == (
Image *) NULL)
479 dispose_images=DestroyImageList(dispose_images);
480 dispose_image=DestroyImage(dispose_image);
481 return((
Image *) NULL);
483 (void) CloneImageProfiles(dispose,next);
484 (void) CloneImageProperties(dispose,next);
485 (void) CloneImageArtifacts(dispose,next);
488 dispose->dispose=next->dispose;
489 AppendImageToList(&dispose_images,dispose);
492 dispose_image=DestroyImage(dispose_image);
493 return(GetFirstImageInList(dispose_images));
527static MagickBooleanType ComparePixels(
const ImageLayerMethod method,
537 if (method == CompareAnyLayer)
538 return((MagickBooleanType)(IsMagickColorSimilar(p,q) == MagickFalse));
540 o1 = (p->matte != MagickFalse) ? (MagickRealType) GetPixelOpacity(p) :
541 (MagickRealType) OpaqueOpacity;
542 o2 = (q->matte != MagickFalse) ? (MagickRealType) q->opacity :
543 (MagickRealType) OpaqueOpacity;
548 if (method == CompareClearLayer)
549 return((MagickBooleanType) ( (o1 <= ((MagickRealType) QuantumRange/2.0)) &&
550 (o2 > ((MagickRealType) QuantumRange/2.0)) ) );
555 if (method == CompareOverlayLayer)
557 if (o2 > ((MagickRealType) QuantumRange/2.0))
559 return((MagickBooleanType) (IsMagickColorSimilar(p,q) == MagickFalse));
625 assert(image1->columns == image2->columns);
626 assert(image1->rows == image2->rows);
632 GetMagickPixelPacket(image1,&pixel1);
633 GetMagickPixelPacket(image2,&pixel2);
634 for (x=0; x < (ssize_t) image1->columns; x++)
636 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
637 q=GetVirtualPixels(image2,x,0,1,image1->rows,exception);
640 indexes1=GetVirtualIndexQueue(image1);
641 indexes2=GetVirtualIndexQueue(image2);
642 for (y=0; y < (ssize_t) image1->rows; y++)
644 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
645 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
646 if (ComparePixels(method,&pixel1,&pixel2))
651 if (y < (ssize_t) image1->rows)
654 if (x >= (ssize_t) image1->columns)
666 for (x=(ssize_t) image1->columns-1; x >= 0; x--)
668 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
669 q=GetVirtualPixels(image2,x,0,1,image1->rows,exception);
672 indexes1=GetVirtualIndexQueue(image1);
673 indexes2=GetVirtualIndexQueue(image2);
674 for (y=0; y < (ssize_t) image1->rows; y++)
676 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
677 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
678 if (ComparePixels(method,&pixel1,&pixel2))
683 if (y < (ssize_t) image1->rows)
686 bounds.width=(size_t) (x-bounds.x+1);
687 for (y=0; y < (ssize_t) image1->rows; y++)
689 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
690 q=GetVirtualPixels(image2,0,y,image1->columns,1,exception);
693 indexes1=GetVirtualIndexQueue(image1);
694 indexes2=GetVirtualIndexQueue(image2);
695 for (x=0; x < (ssize_t) image1->columns; x++)
697 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
698 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
699 if (ComparePixels(method,&pixel1,&pixel2))
704 if (x < (ssize_t) image1->columns)
708 for (y=(ssize_t) image1->rows-1; y >= 0; y--)
710 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
711 q=GetVirtualPixels(image2,0,y,image1->columns,1,exception);
714 indexes1=GetVirtualIndexQueue(image1);
715 indexes2=GetVirtualIndexQueue(image2);
716 for (x=0; x < (ssize_t) image1->columns; x++)
718 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
719 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
720 if (ComparePixels(method,&pixel1,&pixel2))
725 if (x < (ssize_t) image1->columns)
728 bounds.height=(size_t) (y-bounds.y+1);
770MagickExport
Image *CompareImageLayers(
const Image *image,
787 assert(image != (
const Image *) NULL);
788 assert(image->signature == MagickCoreSignature);
790 assert(exception->signature == MagickCoreSignature);
791 assert((method == CompareAnyLayer) ||
792 (method == CompareClearLayer) ||
793 (method == CompareOverlayLayer));
794 if (IsEventLogging() != MagickFalse)
795 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
799 next=GetFirstImageInList(image);
801 GetImageListLength(next),
sizeof(*bounds));
803 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
807 image_a=CloneImage(next,next->page.width,next->page.height,
808 MagickTrue,exception);
809 if (image_a == (
Image *) NULL)
812 return((
Image *) NULL);
814 image_a->background_color.opacity=(Quantum) TransparentOpacity;
815 (void) SetImageBackgroundColor(image_a);
816 image_a->page=next->page;
819 (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,next->page.y);
824 next=GetNextImageInList(next);
825 for ( ; next != (
const Image *) NULL; next=GetNextImageInList(next))
827 image_b=CloneImage(image_a,0,0,MagickTrue,exception);
828 if (image_b == (
Image *) NULL)
830 image_a=DestroyImage(image_a);
832 return((
Image *) NULL);
834 (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,
836 bounds[i]=CompareImageBounds(image_b,image_a,method,exception);
838 image_b=DestroyImage(image_b);
841 image_a=DestroyImage(image_a);
845 next=GetFirstImageInList(image);
846 layers=CloneImage(next,0,0,MagickTrue,exception);
847 if (layers == (
Image *) NULL)
850 return((
Image *) NULL);
856 next=GetNextImageInList(next);
857 for ( ; next != (
const Image *) NULL; next=GetNextImageInList(next))
859 if ((bounds[i].x == -1) && (bounds[i].y == -1) &&
860 (bounds[i].width == 1) && (bounds[i].height == 1))
869 image_a=CloneImage(next,0,0,MagickTrue,exception);
870 if (image_a == (
Image *) NULL)
872 image_b=CropImage(image_a,&bounds[i],exception);
873 image_a=DestroyImage(image_a);
874 if (image_b == (
Image *) NULL)
876 AppendImageToList(&layers,image_b);
880 if (next != (
Image *) NULL)
882 layers=DestroyImageList(layers);
883 return((
Image *) NULL);
885 return(GetFirstImageInList(layers));
917MagickExport
Image *DeconstructImages(
const Image *images,
920 return(CompareImageLayers(images,CompareAnyLayer,exception));
966#define DupDispose ((DisposeType)9)
970#define DelDispose ((DisposeType)8)
972#define DEBUG_OPT_FRAME 0
974static Image *OptimizeLayerFrames(
const Image *image,
1006 assert(image != (
const Image *) NULL);
1007 assert(image->signature == MagickCoreSignature);
1009 assert(exception->signature == MagickCoreSignature);
1010 assert(method == OptimizeLayer ||
1011 method == OptimizeImageLayer ||
1012 method == OptimizePlusLayer);
1013 if (IsEventLogging() != MagickFalse)
1014 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1018 add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
1022 curr=GetFirstImageInList(image);
1023 for (; curr != (
Image *) NULL; curr=GetNextImageInList(curr))
1025 if ((curr->columns != image->columns) || (curr->rows != image->rows))
1026 ThrowImageException(OptionError,
"ImagesAreNotTheSameSize");
1027 if ((curr->page.x != 0) || (curr->page.y != 0) ||
1028 (curr->page.width != image->page.width) ||
1029 (curr->page.height != image->page.height))
1030 ThrowImageException(OptionError,
"ImagePagesAreNotCoalesced");
1035 curr=GetFirstImageInList(image);
1037 GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)*
1040 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1041 disposals=(DisposeType *) AcquireQuantumMemory((
size_t)
1042 GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
1043 sizeof(*disposals));
1044 if (disposals == (DisposeType *) NULL)
1047 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1052 prev_image=CloneImage(curr,curr->page.width,curr->page.height,
1053 MagickTrue,exception);
1054 if (prev_image == (
Image *) NULL)
1057 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1058 return((
Image *) NULL);
1060 prev_image->page=curr->page;
1061 prev_image->page.x=0;
1062 prev_image->page.y=0;
1063 prev_image->dispose=NoneDispose;
1065 prev_image->background_color.opacity=(Quantum) TransparentOpacity;
1066 (void) SetImageBackgroundColor(prev_image);
1073 (void) FormatLocaleFile(stderr,
"frame %.20g :-\n",(
double) i);
1075 disposals[0]=NoneDispose;
1076 bounds[0]=CompareImageBounds(prev_image,curr,CompareAnyLayer,exception);
1078 (void) FormatLocaleFile(stderr,
"overlay: %.20gx%.20g%+.20g%+.20g\n\n",
1079 (
double) bounds[i].width,(
double) bounds[i].height,
1080 (
double) bounds[i].x,(
double) bounds[i].y );
1086 bgnd_image=(
Image *) NULL;
1087 dup_image=(
Image *) NULL;
1089 dup_bounds.height=0;
1092 curr=GetNextImageInList(curr);
1093 for ( ; curr != (
const Image *) NULL; curr=GetNextImageInList(curr))
1096 (void) FormatLocaleFile(stderr,
"frame %.20g :-\n",(
double) i);
1101 bounds[i]=CompareImageBounds(curr->previous,curr,CompareAnyLayer,exception);
1102 cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception);
1103 disposals[i-1]=NoneDispose;
1105 (void) FormatLocaleFile(stderr,
"overlay: %.20gx%.20g%+.20g%+.20g%s%s\n",
1106 (
double) bounds[i].width,(
double) bounds[i].height,
1107 (
double) bounds[i].x,(
double) bounds[i].y,
1108 bounds[i].x < 0?
" (unchanged)":
"",
1109 cleared?
" (pixels cleared)":
"");
1111 if ( bounds[i].x < 0 ) {
1118 if ( add_frames && i>=2 ) {
1119 disposals[i-1]=DelDispose;
1120 disposals[i]=NoneDispose;
1121 bounds[i]=bounds[i-1];
1131 try_bounds=CompareImageBounds(prev_image,curr,CompareAnyLayer,exception);
1132 try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception);
1134 (void) FormatLocaleFile(stderr,
"test_prev: %.20gx%.20g%+.20g%+.20g%s\n",
1135 (
double) try_bounds.width,(
double) try_bounds.height,
1136 (
double) try_bounds.x,(
double) try_bounds.y,
1137 try_cleared?
" (pixels were cleared)":
"");
1139 if ( (!try_cleared && cleared ) ||
1140 try_bounds.width * try_bounds.height
1141 < bounds[i].width * bounds[i].height )
1143 cleared=try_cleared;
1144 bounds[i]=try_bounds;
1145 disposals[i-1]=PreviousDispose;
1147 (void) FormatLocaleFile(stderr,
"previous: accepted\n");
1149 (void) FormatLocaleFile(stderr,
"previous: rejected\n");
1158 dup_bounds.width=dup_bounds.height=0;
1161 dup_image=CloneImage(curr->previous,0,0,MagickTrue,exception);
1162 if (dup_image == (
Image *) NULL)
1165 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1166 prev_image=DestroyImage(prev_image);
1167 return((
Image *) NULL);
1169 dup_bounds=CompareImageBounds(dup_image,curr,CompareClearLayer,exception);
1170 ClearBounds(dup_image,&dup_bounds);
1171 try_bounds=CompareImageBounds(dup_image,curr,CompareAnyLayer,exception);
1173 dup_bounds.width*dup_bounds.height
1174 +try_bounds.width*try_bounds.height
1175 < bounds[i].width * bounds[i].height )
1177 cleared=MagickFalse;
1178 bounds[i]=try_bounds;
1179 disposals[i-1]=DupDispose;
1183 dup_bounds.width=dup_bounds.height=0;
1188 bgnd_image=CloneImage(curr->previous,0,0,MagickTrue,exception);
1189 if (bgnd_image == (
Image *) NULL)
1192 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1193 prev_image=DestroyImage(prev_image);
1194 if ( dup_image != (
Image *) NULL)
1195 dup_image=DestroyImage(dup_image);
1196 return((
Image *) NULL);
1198 bgnd_bounds=bounds[i-1];
1199 ClearBounds(bgnd_image,&bgnd_bounds);
1200 try_bounds=CompareImageBounds(bgnd_image,curr,CompareAnyLayer,exception);
1201 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1203 (void) FormatLocaleFile(stderr,
"background: %s\n",
1204 try_cleared?
"(pixels cleared)":
"");
1214 try_bounds=CompareImageBounds(curr->previous,curr,CompareClearLayer,exception);
1216 (void) FormatLocaleFile(stderr,
"expand_clear: %.20gx%.20g%+.20g%+.20g%s\n",
1217 (
double) try_bounds.width,(
double) try_bounds.height,
1218 (
double) try_bounds.x,(
double) try_bounds.y,
1219 try_bounds.x<0?
" (no expand necessary)":
"");
1221 if ( bgnd_bounds.x < 0 )
1222 bgnd_bounds = try_bounds;
1226 (void) FormatLocaleFile(stderr,
"expand_bgnd: %.20gx%.20g%+.20g%+.20g\n",
1227 (
double) bgnd_bounds.width,(
double) bgnd_bounds.height,
1228 (
double) bgnd_bounds.x,(
double) bgnd_bounds.y );
1230 if ( try_bounds.x < bgnd_bounds.x )
1232 bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x;
1233 if ( bgnd_bounds.width < try_bounds.width )
1234 bgnd_bounds.width = try_bounds.width;
1235 bgnd_bounds.x = try_bounds.x;
1239 try_bounds.width += try_bounds.x - bgnd_bounds.x;
1240 if ( bgnd_bounds.width < try_bounds.width )
1241 bgnd_bounds.width = try_bounds.width;
1243 if ( try_bounds.y < bgnd_bounds.y )
1245 bgnd_bounds.height += bgnd_bounds.y - try_bounds.y;
1246 if ( bgnd_bounds.height < try_bounds.height )
1247 bgnd_bounds.height = try_bounds.height;
1248 bgnd_bounds.y = try_bounds.y;
1252 try_bounds.height += try_bounds.y - bgnd_bounds.y;
1253 if ( bgnd_bounds.height < try_bounds.height )
1254 bgnd_bounds.height = try_bounds.height;
1257 (void) FormatLocaleFile(stderr,
" to : %.20gx%.20g%+.20g%+.20g\n",
1258 (
double) bgnd_bounds.width,(
double) bgnd_bounds.height,
1259 (
double) bgnd_bounds.x,(
double) bgnd_bounds.y );
1262 ClearBounds(bgnd_image,&bgnd_bounds);
1271 try_bounds=CompareImageBounds(bgnd_image,curr,CompareClearLayer,exception);
1272 (void) FormatLocaleFile(stderr,
"expand_ctst: %.20gx%.20g%+.20g%+.20g\n",
1273 (
double) try_bounds.width,(
double) try_bounds.height,
1274 (
double) try_bounds.x,(
double) try_bounds.y );
1275 try_bounds=CompareImageBounds(bgnd_image,curr,CompareAnyLayer,exception);
1276 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1277 (void) FormatLocaleFile(stderr,
"expand_any : %.20gx%.20g%+.20g%+.20g%s\n",
1278 (
double) try_bounds.width,(
double) try_bounds.height,
1279 (
double) try_bounds.x,(
double) try_bounds.y,
1280 try_cleared?
" (pixels cleared)":
"");
1282 try_bounds=CompareImageBounds(bgnd_image,curr,CompareOverlayLayer,exception);
1284 try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
1285 (void) FormatLocaleFile(stderr,
"expand_test: %.20gx%.20g%+.20g%+.20g%s\n",
1286 (
double) try_bounds.width,(
double) try_bounds.height,
1287 (
double) try_bounds.x,(
double) try_bounds.y,
1288 try_cleared?
" (pixels cleared)":
"");
1296 bgnd_bounds.width*bgnd_bounds.height
1297 +try_bounds.width*try_bounds.height
1298 < bounds[i-1].width*bounds[i-1].height
1299 +dup_bounds.width*dup_bounds.height
1300 +bounds[i].width*bounds[i].height )
1302 cleared=MagickFalse;
1303 bounds[i-1]=bgnd_bounds;
1304 bounds[i]=try_bounds;
1305 if ( disposals[i-1] == DupDispose )
1306 dup_image=DestroyImage(dup_image);
1307 disposals[i-1]=BackgroundDispose;
1309 (void) FormatLocaleFile(stderr,
"expand_bgnd: accepted\n");
1311 (void) FormatLocaleFile(stderr,
"expand_bgnd: reject\n");
1319 if ( disposals[i-1] == DupDispose )
1321 if (bgnd_image != (
Image *) NULL)
1322 bgnd_image=DestroyImage(bgnd_image);
1323 prev_image=DestroyImage(prev_image);
1324 prev_image=dup_image, dup_image=(
Image *) NULL;
1325 bounds[i+1]=bounds[i];
1326 bounds[i]=dup_bounds;
1327 disposals[i-1]=DupDispose;
1328 disposals[i]=BackgroundDispose;
1333 if ( dup_image != (
Image *) NULL)
1334 dup_image=DestroyImage(dup_image);
1335 if ( disposals[i-1] != PreviousDispose )
1336 prev_image=DestroyImage(prev_image);
1337 if ( disposals[i-1] == BackgroundDispose )
1338 prev_image=bgnd_image, bgnd_image=(
Image *) NULL;
1339 if (bgnd_image != (
Image *) NULL)
1340 bgnd_image=DestroyImage(bgnd_image);
1341 if ( disposals[i-1] == NoneDispose )
1343 prev_image=ReferenceImage(curr->previous);
1344 if (prev_image == (
Image *) NULL)
1347 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1348 return((
Image *) NULL);
1353 assert(prev_image != (
Image *) NULL);
1354 disposals[i]=disposals[i-1];
1356 (void) FormatLocaleFile(stderr,
"final %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1358 CommandOptionToMnemonic(MagickDisposeOptions,disposals[i-1]),
1359 (
double) bounds[i-1].width,(
double) bounds[i-1].height,
1360 (
double) bounds[i-1].x,(
double) bounds[i-1].y );
1363 (void) FormatLocaleFile(stderr,
"interim %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
1365 CommandOptionToMnemonic(MagickDisposeOptions,disposals[i]),
1366 (
double) bounds[i].width,(
double) bounds[i].height,
1367 (
double) bounds[i].x,(
double) bounds[i].y );
1368 (void) FormatLocaleFile(stderr,
"\n");
1372 prev_image=DestroyImage(prev_image);
1376 sans_exception=AcquireExceptionInfo();
1378 curr=GetFirstImageInList(image);
1379 optimized_image=NewImageList();
1380 while ( curr != (
const Image *) NULL )
1382 prev_image=CloneImage(curr,0,0,MagickTrue,exception);
1383 if (prev_image == (
Image *) NULL)
1385 if ( disposals[i] == DelDispose ) {
1387 while ( disposals[i] == DelDispose ) {
1388 time += (size_t) (curr->delay*1000*
1389 PerceptibleReciprocal((
double) curr->ticks_per_second));
1390 curr=GetNextImageInList(curr);
1393 time += (size_t) (curr->delay*1000*
1394 PerceptibleReciprocal((
double) curr->ticks_per_second));
1395 prev_image->ticks_per_second = 100L;
1396 prev_image->delay = time*prev_image->ticks_per_second/1000;
1398 bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
1399 prev_image=DestroyImage(prev_image);
1400 if (bgnd_image == (
Image *) NULL)
1402 bgnd_image->dispose=disposals[i];
1403 if ( disposals[i] == DupDispose ) {
1404 bgnd_image->delay=0;
1405 bgnd_image->dispose=NoneDispose;
1408 curr=GetNextImageInList(curr);
1409 AppendImageToList(&optimized_image,bgnd_image);
1412 sans_exception=DestroyExceptionInfo(sans_exception);
1414 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1415 if (curr != (
Image *) NULL)
1417 optimized_image=DestroyImageList(optimized_image);
1418 return((
Image *) NULL);
1420 return(GetFirstImageInList(optimized_image));
1451MagickExport
Image *OptimizeImageLayers(
const Image *image,
1454 return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
1484MagickExport
Image *OptimizePlusImageLayers(
const Image *image,
1487 return OptimizeLayerFrames(image,OptimizePlusLayer,exception);
1521MagickExport
void OptimizeImageTransparency(
const Image *image,
1533 assert(image != (
Image *) NULL);
1534 assert(image->signature == MagickCoreSignature);
1536 assert(exception->signature == MagickCoreSignature);
1537 if (IsEventLogging() != MagickFalse)
1538 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1539 next=GetFirstImageInList(image);
1540 dispose_image=CloneImage(next,next->page.width,next->page.height,
1541 MagickTrue,exception);
1542 if (dispose_image == (
Image *) NULL)
1544 dispose_image->page=next->page;
1545 dispose_image->page.x=0;
1546 dispose_image->page.y=0;
1547 dispose_image->dispose=NoneDispose;
1548 dispose_image->background_color.opacity=(Quantum) TransparentOpacity;
1549 (void) SetImageBackgroundColor(dispose_image);
1551 while ( next != (
Image *) NULL )
1559 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
1560 if (current_image == (
Image *) NULL)
1562 dispose_image=DestroyImage(dispose_image);
1565 (void) CompositeImage(current_image,next->matte != MagickFalse ?
1566 OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
1572 if (next->dispose == BackgroundDispose)
1577 bounds.width=next->columns;
1578 bounds.height=next->rows;
1581 bounds.width+=bounds.x;
1584 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
1585 bounds.width=current_image->columns-bounds.x;
1588 bounds.height+=bounds.y;
1591 if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
1592 bounds.height=current_image->rows-bounds.y;
1593 ClearBounds(current_image,&bounds);
1595 if (next->dispose != PreviousDispose)
1597 dispose_image=DestroyImage(dispose_image);
1598 dispose_image=current_image;
1601 current_image=DestroyImage(current_image);
1606 next=GetNextImageInList(next);
1607 if (next != (
Image *) NULL)
1608 (void) CompositeImage(next,ChangeMaskCompositeOp,dispose_image,
1609 -(next->page.x),-(next->page.y));
1611 dispose_image=DestroyImage(dispose_image);
1655 assert((*images) != (
const Image *) NULL);
1656 assert((*images)->signature == MagickCoreSignature);
1658 assert(exception->signature == MagickCoreSignature);
1659 if (IsEventLogging() != MagickFalse)
1660 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",
1661 (*images)->filename);
1662 image=GetFirstImageInList(*images);
1663 for ( ; (next=GetNextImageInList(image)) != (
Image *) NULL; image=next)
1665 if ((image->columns != next->columns) || (image->rows != next->rows) ||
1666 (image->page.x != next->page.x) || (image->page.y != next->page.y))
1668 bounds=CompareImageBounds(image,next,CompareAnyLayer,exception);
1677 time=1000*image->delay*PerceptibleReciprocal(image->ticks_per_second);
1678 time+=1000*next->delay*PerceptibleReciprocal(next->ticks_per_second);
1679 next->ticks_per_second=100L;
1680 next->delay=time*image->ticks_per_second/1000;
1681 next->iterations=image->iterations;
1683 (void) DeleteImageFromList(images);
1686 *images=GetFirstImageInList(*images);
1729MagickExport
void RemoveZeroDelayLayers(
Image **images,
1735 assert((*images) != (
const Image *) NULL);
1736 assert((*images)->signature == MagickCoreSignature);
1738 assert(exception->signature == MagickCoreSignature);
1739 if (IsEventLogging() != MagickFalse)
1740 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",
1741 (*images)->filename);
1742 i=GetFirstImageInList(*images);
1743 for ( ; i != (
Image *) NULL; i=GetNextImageInList(i))
1744 if ( i->delay != 0L )
break;
1745 if ( i == (
Image *) NULL ) {
1746 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1747 "ZeroTimeAnimation",
"`%s'",GetFirstImageInList(*images)->filename);
1750 i=GetFirstImageInList(*images);
1751 while ( i != (
Image *) NULL )
1753 if ( i->delay == 0L ) {
1754 (void) DeleteImageFromList(&i);
1758 i=GetNextImageInList(i);
1760 *images=GetFirstImageInList(*images);
1819static inline void CompositeCanvas(
Image *destination,
1820 const CompositeOperator compose,
Image *source,ssize_t x_offset,
1823 x_offset+=source->page.x-destination->page.x;
1824 y_offset+=source->page.y-destination->page.y;
1825 (void) CompositeImage(destination,compose,source,x_offset,y_offset);
1828MagickExport
void CompositeLayers(
Image *destination,
1829 const CompositeOperator compose,
Image *source,
const ssize_t x_offset,
1832 assert(destination != (
Image *) NULL);
1833 assert(destination->signature == MagickCoreSignature);
1834 assert(source != (
Image *) NULL);
1835 assert(source->signature == MagickCoreSignature);
1837 assert(exception->signature == MagickCoreSignature);
1838 if (IsEventLogging() != MagickFalse)
1839 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s - %s",
1840 source->filename,destination->filename);
1844 if (source->next == (
Image *) NULL)
1845 while ( destination != (
Image *) NULL )
1847 CompositeCanvas(destination,compose,source,x_offset,y_offset);
1848 destination=GetNextImageInList(destination);
1858 else if ( destination->next == (
Image *) NULL )
1860 Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
1862 CompositeCanvas(destination,compose,source,x_offset,y_offset);
1864 if ( source->next != (
Image *) NULL )
1866 destination->delay = source->delay;
1867 destination->iterations = source->iterations;
1869 source=GetNextImageInList(source);
1871 while ( source != (
Image *) NULL )
1873 AppendImageToList(&destination,
1874 CloneImage(dest,0,0,MagickTrue,exception));
1875 destination=GetLastImageInList(destination);
1877 CompositeCanvas(destination,compose,source,x_offset,y_offset);
1878 destination->delay = source->delay;
1879 destination->iterations = source->iterations;
1880 source=GetNextImageInList(source);
1882 dest=DestroyImage(dest);
1890 while ( source != (
Image *) NULL && destination != (
Image *) NULL )
1892 CompositeCanvas(destination,compose,source,x_offset,y_offset);
1893 source=GetNextImageInList(source);
1894 destination=GetNextImageInList(destination);
1951MagickExport
Image *MergeImageLayers(
Image *image,
1954#define MergeLayersTag "Merge/Layers"
1976 assert(image != (
Image *) NULL);
1977 assert(image->signature == MagickCoreSignature);
1979 assert(exception->signature == MagickCoreSignature);
1980 if (IsEventLogging() != MagickFalse)
1981 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1986 width=image->columns;
1990 case TrimBoundsLayer:
1994 next=GetNextImageInList(image);
1995 for ( ; next != (
Image *) NULL; next=GetNextImageInList(next))
1997 if (page.x > next->page.x)
1999 width+=page.x-next->page.x;
2000 page.x=next->page.x;
2002 if (page.y > next->page.y)
2004 height+=page.y-next->page.y;
2005 page.y=next->page.y;
2007 if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x))
2008 width=(size_t) next->page.x+(ssize_t) next->columns-page.x;
2009 if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y))
2010 height=(size_t) next->page.y+(ssize_t) next->rows-page.y;
2018 if (page.height > 0)
2028 if (page.height > 0)
2030 for (next=image; next != (
Image *) NULL; next=GetNextImageInList(next))
2032 if ((ssize_t) width < (next->page.x+(ssize_t) next->columns))
2033 width=(size_t) next->page.x+next->columns;
2034 if ((ssize_t) height < (next->page.y+(ssize_t) next->rows))
2035 height=(size_t) next->page.y+next->rows;
2047 if (page.width == 0)
2048 page.width=page.x < 0 ? width : width+page.x;
2049 if (page.height == 0)
2050 page.height=page.y < 0 ? height : height+page.y;
2054 if (method == TrimBoundsLayer)
2056 number_images=GetImageListLength(image);
2057 for (scene=0; scene < (ssize_t) number_images; scene++)
2059 image->page.x-=page.x;
2060 image->page.y-=page.y;
2061 image->page.width=width;
2062 image->page.height=height;
2063 proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2065 if (proceed == MagickFalse)
2067 image=GetNextImageInList(image);
2068 if (image == (
Image *) NULL)
2071 return((
Image *) NULL);
2076 canvas=CloneImage(image,width,height,MagickTrue,exception);
2077 if (canvas == (
Image *) NULL)
2078 return((
Image *) NULL);
2079 (void) SetImageBackgroundColor(canvas);
2081 canvas->dispose=UndefinedDispose;
2085 number_images=GetImageListLength(image);
2086 for (scene=0; scene < (ssize_t) number_images; scene++)
2088 (void) CompositeImage(canvas,image->compose,image,image->page.x-
2089 canvas->page.x,image->page.y-canvas->page.y);
2090 proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
2092 if (proceed == MagickFalse)
2094 image=GetNextImageInList(image);
2095 if (image == (
Image *) NULL)