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)
166 static 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));
527 static MagickBooleanType ComparePixels(
const ImageLayerMethod method,
537 if (method == CompareAnyLayer)
538 return((MagickBooleanType)(IsMagickColorSimilar(p,q) == MagickFalse));
540 o1 = (p->matte != MagickFalse) ? GetPixelOpacity(p) : OpaqueOpacity;
541 o2 = (q->matte != MagickFalse) ? q->opacity : OpaqueOpacity;
546 if (method == CompareClearLayer)
547 return((MagickBooleanType) ( (o1 <= ((MagickRealType) QuantumRange/2.0)) &&
548 (o2 > ((MagickRealType) QuantumRange/2.0)) ) );
553 if (method == CompareOverlayLayer)
555 if (o2 > ((MagickRealType) QuantumRange/2.0))
557 return((MagickBooleanType) (IsMagickColorSimilar(p,q) == MagickFalse));
623 assert(image1->columns == image2->columns);
624 assert(image1->rows == image2->rows);
630 GetMagickPixelPacket(image1,&pixel1);
631 GetMagickPixelPacket(image2,&pixel2);
632 for (x=0; x < (ssize_t) image1->columns; x++)
634 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
635 q=GetVirtualPixels(image2,x,0,1,image1->rows,exception);
638 indexes1=GetVirtualIndexQueue(image1);
639 indexes2=GetVirtualIndexQueue(image2);
640 for (y=0; y < (ssize_t) image1->rows; y++)
642 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
643 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
644 if (ComparePixels(method,&pixel1,&pixel2))
649 if (y < (ssize_t) image1->rows)
652 if (x >= (ssize_t) image1->columns)
664 for (x=(ssize_t) image1->columns-1; x >= 0; x--)
666 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
667 q=GetVirtualPixels(image2,x,0,1,image1->rows,exception);
670 indexes1=GetVirtualIndexQueue(image1);
671 indexes2=GetVirtualIndexQueue(image2);
672 for (y=0; y < (ssize_t) image1->rows; y++)
674 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
675 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
676 if (ComparePixels(method,&pixel1,&pixel2))
681 if (y < (ssize_t) image1->rows)
684 bounds.width=(size_t) (x-bounds.x+1);
685 for (y=0; y < (ssize_t) image1->rows; y++)
687 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
688 q=GetVirtualPixels(image2,0,y,image1->columns,1,exception);
691 indexes1=GetVirtualIndexQueue(image1);
692 indexes2=GetVirtualIndexQueue(image2);
693 for (x=0; x < (ssize_t) image1->columns; x++)
695 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
696 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
697 if (ComparePixels(method,&pixel1,&pixel2))
702 if (x < (ssize_t) image1->columns)
706 for (y=(ssize_t) image1->rows-1; y >= 0; y--)
708 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
709 q=GetVirtualPixels(image2,0,y,image1->columns,1,exception);
712 indexes1=GetVirtualIndexQueue(image1);
713 indexes2=GetVirtualIndexQueue(image2);
714 for (x=0; x < (ssize_t) image1->columns; x++)
716 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
717 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
718 if (ComparePixels(method,&pixel1,&pixel2))
723 if (x < (ssize_t) image1->columns)
726 bounds.height=(size_t) (y-bounds.y+1);
768 MagickExport
Image *CompareImageLayers(
const Image *image,
785 assert(image != (
const Image *) NULL);
786 assert(image->signature == MagickCoreSignature);
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);
797 next=GetFirstImageInList(image);
799 GetImageListLength(next),
sizeof(*bounds));
801 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
805 image_a=CloneImage(next,next->page.width,next->page.height,
806 MagickTrue,exception);
807 if (image_a == (
Image *) NULL)
810 return((
Image *) NULL);
812 image_a->background_color.opacity=(Quantum) TransparentOpacity;
813 (void) SetImageBackgroundColor(image_a);
814 image_a->page=next->page;
817 (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,next->page.y);
822 next=GetNextImageInList(next);
823 for ( ; next != (
const Image *) NULL; next=GetNextImageInList(next))
825 image_b=CloneImage(image_a,0,0,MagickTrue,exception);
826 if (image_b == (
Image *) NULL)
828 image_a=DestroyImage(image_a);
830 return((
Image *) NULL);
832 (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,
834 bounds[i]=CompareImageBounds(image_b,image_a,method,exception);
836 image_b=DestroyImage(image_b);
839 image_a=DestroyImage(image_a);
843 next=GetFirstImageInList(image);
844 layers=CloneImage(next,0,0,MagickTrue,exception);
845 if (layers == (
Image *) NULL)
848 return((
Image *) NULL);
854 next=GetNextImageInList(next);
855 for ( ; next != (
const Image *) NULL; next=GetNextImageInList(next))
857 if ((bounds[i].x == -1) && (bounds[i].y == -1) &&
858 (bounds[i].width == 1) && (bounds[i].height == 1))
867 image_a=CloneImage(next,0,0,MagickTrue,exception);
868 if (image_a == (
Image *) NULL)
870 image_b=CropImage(image_a,&bounds[i],exception);
871 image_a=DestroyImage(image_a);
872 if (image_b == (
Image *) NULL)
874 AppendImageToList(&layers,image_b);
878 if (next != (
Image *) NULL)
880 layers=DestroyImageList(layers);
881 return((
Image *) NULL);
883 return(GetFirstImageInList(layers));
915 MagickExport
Image *DeconstructImages(
const Image *images,
918 return(CompareImageLayers(images,CompareAnyLayer,exception));
964 #define DupDispose ((DisposeType)9)
968 #define DelDispose ((DisposeType)8)
970 #define DEBUG_OPT_FRAME 0
972 static Image *OptimizeLayerFrames(
const Image *image,
1004 assert(image != (
const Image *) NULL);
1005 assert(image->signature == MagickCoreSignature);
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);
1016 add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
1020 curr=GetFirstImageInList(image);
1021 for (; curr != (
Image *) NULL; curr=GetNextImageInList(curr))
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");
1033 curr=GetFirstImageInList(image);
1035 GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)*
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)
1045 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1050 prev_image=CloneImage(curr,curr->page.width,curr->page.height,
1051 MagickTrue,exception);
1052 if (prev_image == (
Image *) NULL)
1055 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1056 return((
Image *) NULL);
1058 prev_image->page=curr->page;
1059 prev_image->page.x=0;
1060 prev_image->page.y=0;
1061 prev_image->dispose=NoneDispose;
1063 prev_image->background_color.opacity=(Quantum) TransparentOpacity;
1064 (void) SetImageBackgroundColor(prev_image);
1071 (void) FormatLocaleFile(stderr,
"frame %.20g :-\n",(
double) i);
1073 disposals[0]=NoneDispose;
1074 bounds[0]=CompareImageBounds(prev_image,curr,CompareAnyLayer,exception);
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 );
1084 bgnd_image=(
Image *) NULL;
1085 dup_image=(
Image *) NULL;
1087 dup_bounds.height=0;
1090 curr=GetNextImageInList(curr);
1091 for ( ; curr != (
const Image *) NULL; curr=GetNextImageInList(curr))
1094 (void) FormatLocaleFile(stderr,
"frame %.20g :-\n",(
double) i);
1099 bounds[i]=CompareImageBounds(curr->previous,curr,CompareAnyLayer,exception);
1100 cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception);
1101 disposals[i-1]=NoneDispose;
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)":
"");
1109 if ( bounds[i].x < 0 ) {
1116 if ( add_frames && i>=2 ) {
1117 disposals[i-1]=DelDispose;
1118 disposals[i]=NoneDispose;
1119 bounds[i]=bounds[i-1];
1129 try_bounds=CompareImageBounds(prev_image,curr,CompareAnyLayer,exception);
1130 try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception);
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)":
"");
1137 if ( (!try_cleared && cleared ) ||
1138 try_bounds.width * try_bounds.height
1139 < bounds[i].width * bounds[i].height )
1141 cleared=try_cleared;
1142 bounds[i]=try_bounds;
1143 disposals[i-1]=PreviousDispose;
1145 (void) FormatLocaleFile(stderr,
"previous: accepted\n");
1147 (void) FormatLocaleFile(stderr,
"previous: rejected\n");
1156 dup_bounds.width=dup_bounds.height=0;
1159 dup_image=CloneImage(curr->previous,0,0,MagickTrue,exception);
1160 if (dup_image == (
Image *) NULL)
1163 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1164 prev_image=DestroyImage(prev_image);
1165 return((
Image *) NULL);
1167 dup_bounds=CompareImageBounds(dup_image,curr,CompareClearLayer,exception);
1168 ClearBounds(dup_image,&dup_bounds);
1169 try_bounds=CompareImageBounds(dup_image,curr,CompareAnyLayer,exception);
1171 dup_bounds.width*dup_bounds.height
1172 +try_bounds.width*try_bounds.height
1173 < bounds[i].width * bounds[i].height )
1175 cleared=MagickFalse;
1176 bounds[i]=try_bounds;
1177 disposals[i-1]=DupDispose;
1181 dup_bounds.width=dup_bounds.height=0;
1186 bgnd_image=CloneImage(curr->previous,0,0,MagickTrue,exception);
1187 if (bgnd_image == (
Image *) NULL)
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);
1196 bgnd_bounds=bounds[i-1];
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);
1201 (void) FormatLocaleFile(stderr,
"background: %s\n",
1202 try_cleared?
"(pixels cleared)":
"");
1212 try_bounds=CompareImageBounds(curr->previous,curr,CompareClearLayer,exception);
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)":
"");
1219 if ( bgnd_bounds.x < 0 )
1220 bgnd_bounds = try_bounds;
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 );
1228 if ( try_bounds.x < bgnd_bounds.x )
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;
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;
1241 if ( try_bounds.y < bgnd_bounds.y )
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;
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;
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 );
1260 ClearBounds(bgnd_image,&bgnd_bounds);
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)":
"");
1280 try_bounds=CompareImageBounds(bgnd_image,curr,CompareOverlayLayer,exception);
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)":
"");
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 )
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;
1307 (void) FormatLocaleFile(stderr,
"expand_bgnd: accepted\n");
1309 (void) FormatLocaleFile(stderr,
"expand_bgnd: reject\n");
1317 if ( disposals[i-1] == DupDispose )
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;
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 )
1341 prev_image=ReferenceImage(curr->previous);
1342 if (prev_image == (
Image *) NULL)
1345 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1346 return((
Image *) NULL);
1351 assert(prev_image != (
Image *) NULL);
1352 disposals[i]=disposals[i-1];
1354 (void) FormatLocaleFile(stderr,
"final %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
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 );
1361 (void) FormatLocaleFile(stderr,
"interum %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
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");
1370 prev_image=DestroyImage(prev_image);
1374 sans_exception=AcquireExceptionInfo();
1376 curr=GetFirstImageInList(image);
1377 optimized_image=NewImageList();
1378 while ( curr != (
const Image *) NULL )
1380 prev_image=CloneImage(curr,0,0,MagickTrue,exception);
1381 if (prev_image == (
Image *) NULL)
1383 if ( disposals[i] == DelDispose ) {
1385 while ( disposals[i] == DelDispose ) {
1386 time += (size_t) (curr->delay*1000*
1387 PerceptibleReciprocal((
double) curr->ticks_per_second));
1388 curr=GetNextImageInList(curr);
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;
1396 bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
1397 prev_image=DestroyImage(prev_image);
1398 if (bgnd_image == (
Image *) NULL)
1400 bgnd_image->dispose=disposals[i];
1401 if ( disposals[i] == DupDispose ) {
1402 bgnd_image->delay=0;
1403 bgnd_image->dispose=NoneDispose;
1406 curr=GetNextImageInList(curr);
1407 AppendImageToList(&optimized_image,bgnd_image);
1410 sans_exception=DestroyExceptionInfo(sans_exception);
1412 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
1413 if (curr != (
Image *) NULL)
1415 optimized_image=DestroyImageList(optimized_image);
1416 return((
Image *) NULL);
1418 return(GetFirstImageInList(optimized_image));
1449 MagickExport
Image *OptimizeImageLayers(
const Image *image,
1452 return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
1482 MagickExport
Image *OptimizePlusImageLayers(
const Image *image,
1485 return OptimizeLayerFrames(image,OptimizePlusLayer,exception);
1519 MagickExport
void OptimizeImageTransparency(
const Image *image,
1531 assert(image != (
Image *) NULL);
1532 assert(image->signature == MagickCoreSignature);
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)
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);
1549 while ( next != (
Image *) NULL )
1557 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
1558 if (current_image == (
Image *) NULL)
1560 dispose_image=DestroyImage(dispose_image);
1563 (void) CompositeImage(current_image,next->matte != MagickFalse ?
1564 OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
1570 if (next->dispose == BackgroundDispose)
1575 bounds.width=next->columns;
1576 bounds.height=next->rows;
1579 bounds.width+=bounds.x;
1582 if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
1583 bounds.width=current_image->columns-bounds.x;
1586 bounds.height+=bounds.y;
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);
1593 if (next->dispose != PreviousDispose)
1595 dispose_image=DestroyImage(dispose_image);
1596 dispose_image=current_image;
1599 current_image=DestroyImage(current_image);
1604 next=GetNextImageInList(next);
1605 if (next != (
Image *) NULL)
1606 (void) CompositeImage(next,ChangeMaskCompositeOp,dispose_image,
1607 -(next->page.x),-(next->page.y));
1609 dispose_image=DestroyImage(dispose_image);
1653 assert((*images) != (
const Image *) NULL);
1654 assert((*images)->signature == MagickCoreSignature);
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)
1663 if ((image->columns != next->columns) || (image->rows != next->rows) ||
1664 (image->page.x != next->page.x) || (image->page.y != next->page.y))
1666 bounds=CompareImageBounds(image,next,CompareAnyLayer,exception);
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;
1681 (void) DeleteImageFromList(images);
1684 *images=GetFirstImageInList(*images);
1727 MagickExport
void RemoveZeroDelayLayers(
Image **images,
1733 assert((*images) != (
const Image *) NULL);
1734 assert((*images)->signature == MagickCoreSignature);
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);
1748 i=GetFirstImageInList(*images);
1749 while ( i != (
Image *) NULL )
1751 if ( i->delay == 0L ) {
1752 (void) DeleteImageFromList(&i);
1756 i=GetNextImageInList(i);
1758 *images=GetFirstImageInList(*images);
1817 static inline void CompositeCanvas(
Image *destination,
1818 const CompositeOperator compose,
Image *source,ssize_t x_offset,
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);
1826 MagickExport
void CompositeLayers(
Image *destination,
1827 const CompositeOperator compose,
Image *source,
const ssize_t x_offset,
1830 assert(destination != (
Image *) NULL);
1831 assert(destination->signature == MagickCoreSignature);
1832 assert(source != (
Image *) NULL);
1833 assert(source->signature == MagickCoreSignature);
1835 assert(exception->signature == MagickCoreSignature);
1836 if (IsEventLogging() != MagickFalse)
1837 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s - %s",
1838 source->filename,destination->filename);
1842 if (source->next == (
Image *) NULL)
1843 while ( destination != (
Image *) NULL )
1845 CompositeCanvas(destination,compose,source,x_offset,y_offset);
1846 destination=GetNextImageInList(destination);
1856 else if ( destination->next == (
Image *) NULL )
1858 Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
1860 CompositeCanvas(destination,compose,source,x_offset,y_offset);
1862 if ( source->next != (
Image *) NULL )
1864 destination->delay = source->delay;
1865 destination->iterations = source->iterations;
1867 source=GetNextImageInList(source);
1869 while ( source != (
Image *) NULL )
1871 AppendImageToList(&destination,
1872 CloneImage(dest,0,0,MagickTrue,exception));
1873 destination=GetLastImageInList(destination);
1875 CompositeCanvas(destination,compose,source,x_offset,y_offset);
1876 destination->delay = source->delay;
1877 destination->iterations = source->iterations;
1878 source=GetNextImageInList(source);
1880 dest=DestroyImage(dest);
1888 while ( source != (
Image *) NULL && destination != (
Image *) NULL )
1890 CompositeCanvas(destination,compose,source,x_offset,y_offset);
1891 source=GetNextImageInList(source);
1892 destination=GetNextImageInList(destination);
1949 MagickExport
Image *MergeImageLayers(
Image *image,
1952 #define MergeLayersTag "Merge/Layers"
1974 assert(image != (
Image *) NULL);
1975 assert(image->signature == MagickCoreSignature);
1977 assert(exception->signature == MagickCoreSignature);
1978 if (IsEventLogging() != MagickFalse)
1979 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1984 width=image->columns;
1988 case TrimBoundsLayer:
1992 next=GetNextImageInList(image);
1993 for ( ; next != (
Image *) NULL; next=GetNextImageInList(next))
1995 if (page.x > next->page.x)
1997 width+=page.x-next->page.x;
1998 page.x=next->page.x;
2000 if (page.y > next->page.y)
2002 height+=page.y-next->page.y;
2003 page.y=next->page.y;
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;
2016 if (page.height > 0)
2026 if (page.height > 0)
2028 for (next=image; next != (
Image *) NULL; next=GetNextImageInList(next))
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;
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;
2052 if (method == TrimBoundsLayer)
2054 number_images=GetImageListLength(image);
2055 for (scene=0; scene < (ssize_t) number_images; scene++)
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,
2063 if (proceed == MagickFalse)
2065 image=GetNextImageInList(image);
2066 if (image == (
Image *) NULL)
2069 return((
Image *) NULL);
2074 canvas=CloneImage(image,width,height,MagickTrue,exception);
2075 if (canvas == (
Image *) NULL)
2076 return((
Image *) NULL);
2077 (void) SetImageBackgroundColor(canvas);
2079 canvas->dispose=UndefinedDispose;
2083 number_images=GetImageListLength(image);
2084 for (scene=0; scene < (ssize_t) number_images; scene++)
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,
2090 if (proceed == MagickFalse)
2092 image=GetNextImageInList(image);
2093 if (image == (
Image *) NULL)