00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 #include "magick/studio.h"
00041 #include "magick/artifact.h"
00042 #include "magick/cache.h"
00043 #include "magick/color.h"
00044 #include "magick/color-private.h"
00045 #include "magick/composite.h"
00046 #include "magick/effect.h"
00047 #include "magick/exception.h"
00048 #include "magick/exception-private.h"
00049 #include "magick/geometry.h"
00050 #include "magick/image.h"
00051 #include "magick/layer.h"
00052 #include "magick/list.h"
00053 #include "magick/memory_.h"
00054 #include "magick/monitor.h"
00055 #include "magick/monitor-private.h"
00056 #include "magick/pixel-private.h"
00057 #include "magick/property.h"
00058 #include "magick/profile.h"
00059 #include "magick/resource_.h"
00060 #include "magick/resize.h"
00061 #include "magick/statistic.h"
00062 #include "magick/string_.h"
00063 #include "magick/transform.h"
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095 static void ClearBounds(Image *image,RectangleInfo *bounds)
00096 {
00097 ExceptionInfo
00098 *exception;
00099
00100 long
00101 y;
00102
00103 if (bounds->x < 0)
00104 return;
00105 if (image->matte == MagickFalse)
00106 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
00107 exception=(&image->exception);
00108 for (y=0; y < (long) bounds->height; y++)
00109 {
00110 register long
00111 x;
00112
00113 register PixelPacket
00114 *__restrict q;
00115
00116 q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
00117 if (q == (PixelPacket *) NULL)
00118 break;
00119 for (x=0; x < (long) bounds->width; x++)
00120 {
00121 q->opacity=(Quantum) TransparentOpacity;
00122 q++;
00123 }
00124 if (SyncAuthenticPixels(image,exception) == MagickFalse)
00125 break;
00126 }
00127 }
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163 static MagickBooleanType IsBoundsCleared(const Image *image1,
00164 const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception)
00165 {
00166 long
00167 y;
00168
00169 register long
00170 x;
00171
00172 register const PixelPacket
00173 *p,
00174 *q;
00175
00176 if ( bounds->x< 0 ) return(MagickFalse);
00177
00178 for (y=0; y < (long) bounds->height; y++)
00179 {
00180 p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,
00181 exception);
00182 q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,
00183 exception);
00184 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00185 break;
00186 for (x=0; x < (long) bounds->width; x++)
00187 {
00188 if ((p->opacity <= (long) (QuantumRange/2)) &&
00189 (q->opacity > (long) (QuantumRange/2)))
00190 break;
00191 p++;
00192 q++;
00193 }
00194 if (x < (long) bounds->width)
00195 break;
00196 }
00197 return(y < (long) bounds->height ? MagickTrue : MagickFalse);
00198 }
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229 MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
00230 {
00231 Image
00232 *coalesce_image,
00233 *dispose_image,
00234 *previous;
00235
00236 register Image
00237 *next;
00238
00239 RectangleInfo
00240 bounds;
00241
00242
00243
00244
00245 assert(image != (Image *) NULL);
00246 assert(image->signature == MagickSignature);
00247 if (image->debug != MagickFalse)
00248 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00249 assert(exception != (ExceptionInfo *) NULL);
00250 assert(exception->signature == MagickSignature);
00251
00252
00253 next=GetFirstImageInList(image);
00254 bounds=next->page;
00255 if (bounds.width == 0)
00256 {
00257 bounds.width=next->columns;
00258 if (bounds.x > 0)
00259 bounds.width+=bounds.x;
00260 }
00261 if (bounds.height == 0)
00262 {
00263 bounds.height=next->rows;
00264 if (bounds.y > 0)
00265 bounds.height+=bounds.y;
00266 }
00267 bounds.x=0;
00268 bounds.y=0;
00269 coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue,
00270 exception);
00271 if (coalesce_image == (Image *) NULL)
00272 return((Image *) NULL);
00273 coalesce_image->page=bounds;
00274 coalesce_image->dispose=NoneDispose;
00275 coalesce_image->background_color.opacity=(Quantum) TransparentOpacity;
00276 (void) SetImageBackgroundColor(coalesce_image);
00277
00278
00279
00280 dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
00281 (void) CompositeImage(coalesce_image,CopyCompositeOp,next,next->page.x,
00282 next->page.y);
00283 next=GetNextImageInList(next);
00284 for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
00285 {
00286
00287
00288
00289 previous=GetPreviousImageInList(next);
00290 bounds=previous->page;
00291 bounds.width=previous->columns;
00292 bounds.height=previous->rows;
00293 if (bounds.x < 0)
00294 {
00295 bounds.width+=bounds.x;
00296 bounds.x=0;
00297 }
00298 if ((long) (bounds.x+bounds.width) > (long) coalesce_image->columns)
00299 bounds.width=coalesce_image->columns-bounds.x;
00300 if (bounds.y < 0)
00301 {
00302 bounds.height+=bounds.y;
00303 bounds.y=0;
00304 }
00305 if ((long) (bounds.y+bounds.height) > (long) coalesce_image->rows)
00306 bounds.height=coalesce_image->rows-bounds.y;
00307
00308
00309
00310 if (GetPreviousImageInList(next)->dispose != PreviousDispose)
00311 {
00312 dispose_image=DestroyImage(dispose_image);
00313 dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
00314 if (dispose_image == (Image *) NULL)
00315 {
00316 coalesce_image=DestroyImageList(coalesce_image);
00317 return((Image *) NULL);
00318 }
00319 }
00320
00321
00322
00323 if (next->previous->dispose == BackgroundDispose)
00324 ClearBounds(dispose_image, &bounds);
00325
00326
00327
00328 coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception);
00329 coalesce_image->next->previous=coalesce_image;
00330 previous=coalesce_image;
00331 coalesce_image=GetNextImageInList(coalesce_image);
00332 coalesce_image->matte=MagickTrue;
00333 (void) CompositeImage(coalesce_image,next->matte != MagickFalse ?
00334 OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
00335 (void) CloneImageProfiles(coalesce_image,next);
00336 (void) CloneImageProperties(coalesce_image,next);
00337 (void) CloneImageArtifacts(coalesce_image,next);
00338 coalesce_image->page=previous->page;
00339
00340
00341
00342 if (IsBoundsCleared(previous,coalesce_image,&bounds,exception))
00343 coalesce_image->dispose=BackgroundDispose;
00344 else
00345 coalesce_image->dispose=NoneDispose;
00346 previous->dispose=coalesce_image->dispose;
00347 }
00348 dispose_image=DestroyImage(dispose_image);
00349 return(GetFirstImageInList(coalesce_image));
00350 }
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378 MagickExport Image *DisposeImages(const Image *image,ExceptionInfo *exception)
00379 {
00380 Image
00381 *dispose_image,
00382 *dispose_images;
00383
00384 register Image
00385 *next;
00386
00387
00388
00389
00390 assert(image != (Image *) NULL);
00391 assert(image->signature == MagickSignature);
00392 if (image->debug != MagickFalse)
00393 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00394 assert(exception != (ExceptionInfo *) NULL);
00395 assert(exception->signature == MagickSignature);
00396 next=GetFirstImageInList(image);
00397 dispose_image=CloneImage(next,next->page.width,next->page.height,MagickTrue,
00398 exception);
00399 if (dispose_image == (Image *) NULL)
00400 return((Image *) NULL);
00401 dispose_image->page=next->page;
00402 dispose_image->page.x=0;
00403 dispose_image->page.y=0;
00404 dispose_image->dispose=NoneDispose;
00405 dispose_image->background_color.opacity=(Quantum) TransparentOpacity;
00406 (void) SetImageBackgroundColor(dispose_image);
00407 dispose_images=NewImageList();
00408 for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
00409 {
00410 Image
00411 *current_image;
00412
00413
00414
00415
00416 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
00417 if (current_image == (Image *) NULL)
00418 {
00419 dispose_images=DestroyImageList(dispose_images);
00420 dispose_image=DestroyImage(dispose_image);
00421 return((Image *) NULL);
00422 }
00423 (void) CompositeImage(current_image,next->matte != MagickFalse ?
00424 OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
00425
00426
00427
00428 if (next->dispose == BackgroundDispose)
00429 {
00430 RectangleInfo
00431 bounds;
00432
00433 bounds=next->page;
00434 bounds.width=next->columns;
00435 bounds.height=next->rows;
00436 if (bounds.x < 0)
00437 {
00438 bounds.width+=bounds.x;
00439 bounds.x=0;
00440 }
00441 if ((long) (bounds.x+bounds.width) > (long) current_image->columns)
00442 bounds.width=current_image->columns-bounds.x;
00443 if (bounds.y < 0)
00444 {
00445 bounds.height+=bounds.y;
00446 bounds.y=0;
00447 }
00448 if ((long) (bounds.y+bounds.height) > (long) current_image->rows)
00449 bounds.height=current_image->rows-bounds.y;
00450 ClearBounds(current_image,&bounds);
00451 }
00452
00453
00454
00455 if (next->dispose == PreviousDispose)
00456 current_image=DestroyImage(current_image);
00457 else
00458 {
00459 dispose_image=DestroyImage(dispose_image);
00460 dispose_image=current_image;
00461 }
00462 {
00463 Image
00464 *dispose;
00465
00466
00467
00468
00469 dispose=CloneImage(dispose_image,0,0,MagickTrue,exception);
00470 if (dispose == (Image *) NULL)
00471 {
00472 dispose_images=DestroyImageList(dispose_images);
00473 dispose_image=DestroyImage(dispose_image);
00474 return((Image *) NULL);
00475 }
00476 (void) CloneImageProfiles(dispose,next);
00477 (void) CloneImageProperties(dispose,next);
00478 (void) CloneImageArtifacts(dispose,next);
00479 dispose->page.x=0;
00480 dispose->page.y=0;
00481 dispose->dispose=next->dispose;
00482 AppendImageToList(&dispose_images,dispose);
00483 }
00484 }
00485 dispose_image=DestroyImage(dispose_image);
00486 return(GetFirstImageInList(dispose_images));
00487 }
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520 static MagickBooleanType ComparePixels(const ImageLayerMethod method,
00521 const MagickPixelPacket *p,const MagickPixelPacket *q)
00522 {
00523 MagickRealType
00524 o1,
00525 o2;
00526
00527
00528
00529
00530 if (method == CompareAnyLayer)
00531 return(IsMagickColorSimilar(p,q) == MagickFalse ? MagickTrue : MagickFalse);
00532
00533 o1 = (p->matte != MagickFalse) ? p->opacity : OpaqueOpacity;
00534 o2 = (q->matte != MagickFalse) ? q->opacity : OpaqueOpacity;
00535
00536
00537
00538
00539 if (method == CompareClearLayer)
00540 return((MagickBooleanType) ( (o1 <= ((MagickRealType) QuantumRange/2.0)) &&
00541 (o2 > ((MagickRealType) QuantumRange/2.0)) ) );
00542
00543
00544
00545
00546 if (method == CompareOverlayLayer)
00547 {
00548 if (o2 > ((MagickRealType) QuantumRange/2.0))
00549 return MagickFalse;
00550 return((MagickBooleanType) (IsMagickColorSimilar(p,q) == MagickFalse));
00551 }
00552 return(MagickFalse);
00553 }
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590 static RectangleInfo CompareImageBounds(const Image *image1,const Image *image2,
00591 const ImageLayerMethod method,ExceptionInfo *exception)
00592 {
00593 RectangleInfo
00594 bounds;
00595
00596 MagickPixelPacket
00597 pixel1,
00598 pixel2;
00599
00600 register const IndexPacket
00601 *indexes1,
00602 *indexes2;
00603
00604 register const PixelPacket
00605 *p,
00606 *q;
00607
00608 long
00609 y;
00610
00611 register long
00612 x;
00613
00614
00615
00616
00617 GetMagickPixelPacket(image1,&pixel1);
00618 GetMagickPixelPacket(image2,&pixel2);
00619 for (x=0; x < (long) image1->columns; x++)
00620 {
00621 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
00622 q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
00623 if ((p == (const PixelPacket *) NULL) ||
00624 (q == (const PixelPacket *) NULL))
00625 break;
00626 indexes1=GetVirtualIndexQueue(image1);
00627 indexes2=GetVirtualIndexQueue(image2);
00628 for (y=0; y < (long) image1->rows; y++)
00629 {
00630 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
00631 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
00632 if (ComparePixels(method,&pixel1,&pixel2))
00633 break;
00634 p++;
00635 q++;
00636 }
00637 if (y < (long) image1->rows)
00638 break;
00639 }
00640 if (x >= (long) image1->columns)
00641 {
00642
00643
00644
00645 bounds.x=-1;
00646 bounds.y=-1;
00647 bounds.width=1;
00648 bounds.height=1;
00649 return(bounds);
00650 }
00651 bounds.x=x;
00652 for (x=(long) image1->columns-1; x >= 0; x--)
00653 {
00654 p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
00655 q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
00656 if ((p == (const PixelPacket *) NULL) ||
00657 (q == (const PixelPacket *) NULL))
00658 break;
00659 indexes1=GetVirtualIndexQueue(image1);
00660 indexes2=GetVirtualIndexQueue(image2);
00661 for (y=0; y < (long) image1->rows; y++)
00662 {
00663 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
00664 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
00665 if (ComparePixels(method,&pixel1,&pixel2))
00666 break;
00667 p++;
00668 q++;
00669 }
00670 if (y < (long) image1->rows)
00671 break;
00672 }
00673 bounds.width=(unsigned long) (x-bounds.x+1);
00674 for (y=0; y < (long) image1->rows; y++)
00675 {
00676 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
00677 q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
00678 if ((p == (const PixelPacket *) NULL) ||
00679 (q == (const PixelPacket *) NULL))
00680 break;
00681 indexes1=GetVirtualIndexQueue(image1);
00682 indexes2=GetVirtualIndexQueue(image2);
00683 for (x=0; x < (long) image1->columns; x++)
00684 {
00685 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
00686 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
00687 if (ComparePixels(method,&pixel1,&pixel2))
00688 break;
00689 p++;
00690 q++;
00691 }
00692 if (x < (long) image1->columns)
00693 break;
00694 }
00695 bounds.y=y;
00696 for (y=(long) image1->rows-1; y >= 0; y--)
00697 {
00698 p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
00699 q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
00700 if ((p == (const PixelPacket *) NULL) ||
00701 (q == (const PixelPacket *) NULL))
00702 break;
00703 indexes1=GetVirtualIndexQueue(image1);
00704 indexes2=GetVirtualIndexQueue(image2);
00705 for (x=0; x < (long) image1->columns; x++)
00706 {
00707 SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
00708 SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
00709 if (ComparePixels(method,&pixel1,&pixel2))
00710 break;
00711 p++;
00712 q++;
00713 }
00714 if (x < (long) image1->columns)
00715 break;
00716 }
00717 bounds.height=(unsigned long) (y-bounds.y+1);
00718 return(bounds);
00719 }
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759 MagickExport Image *CompareImageLayers(const Image *image,
00760 const ImageLayerMethod method, ExceptionInfo *exception)
00761 {
00762 Image
00763 *image_a,
00764 *image_b,
00765 *layers;
00766
00767 RectangleInfo
00768 *bounds;
00769
00770 register const Image
00771 *next;
00772
00773 register long
00774 i;
00775
00776 assert(image != (const Image *) NULL);
00777 assert(image->signature == MagickSignature);
00778 if (image->debug != MagickFalse)
00779 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00780 assert(exception != (ExceptionInfo *) NULL);
00781 assert(exception->signature == MagickSignature);
00782 assert((method == CompareAnyLayer) ||
00783 (method == CompareClearLayer) ||
00784 (method == CompareOverlayLayer));
00785
00786
00787
00788 next=GetFirstImageInList(image);
00789 bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
00790 GetImageListLength(next),sizeof(*bounds));
00791 if (bounds == (RectangleInfo *) NULL)
00792 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00793
00794
00795
00796 image_a=CloneImage(next,next->page.width,next->page.height,
00797 MagickTrue,exception);
00798 if (image_a == (Image *) NULL)
00799 {
00800 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00801 return((Image *) NULL);
00802 }
00803 image_a->background_color.opacity=(Quantum) TransparentOpacity;
00804 (void) SetImageBackgroundColor(image_a);
00805 image_a->page=next->page;
00806 image_a->page.x=0;
00807 image_a->page.y=0;
00808 (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,next->page.y);
00809
00810
00811
00812 i=0;
00813 next=GetNextImageInList(next);
00814 for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
00815 {
00816 image_b=CloneImage(image_a,0,0,MagickTrue,exception);
00817 if (image_b == (Image *) NULL)
00818 {
00819 image_a=DestroyImage(image_a);
00820 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00821 return((Image *) NULL);
00822 }
00823 (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,
00824 next->page.y);
00825 bounds[i]=CompareImageBounds(image_b,image_a,method,exception);
00826
00827 image_b=DestroyImage(image_b);
00828 i++;
00829 }
00830 image_a=DestroyImage(image_a);
00831
00832
00833
00834 next=GetFirstImageInList(image);
00835 layers=CloneImage(next,0,0,MagickTrue,exception);
00836 if (layers == (Image *) NULL)
00837 {
00838 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00839 return((Image *) NULL);
00840 }
00841
00842
00843
00844 i=0;
00845 next=GetNextImageInList(next);
00846 for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
00847 {
00848 image_a=CloneImage(next,0,0,MagickTrue,exception);
00849 if (image_a == (Image *) NULL)
00850 break;
00851 image_b=CropImage(image_a,&bounds[i],exception);
00852 image_a=DestroyImage(image_a);
00853 if (image_b == (Image *) NULL)
00854 break;
00855 AppendImageToList(&layers,image_b);
00856 i++;
00857 }
00858 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
00859 if (next != (Image *) NULL)
00860 {
00861 layers=DestroyImageList(layers);
00862 return((Image *) NULL);
00863 }
00864 return(GetFirstImageInList(layers));
00865 }
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896 MagickExport Image *DeconstructImages(const Image *images,
00897 ExceptionInfo *exception)
00898 {
00899 return(CompareImageLayers(images,CompareAnyLayer,exception));
00900 }
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944 #define DupDispose ((DisposeType)9)
00945
00946
00947
00948 #define DelDispose ((DisposeType)8)
00949
00950 static Image *OptimizeLayerFrames(const Image *image,
00951 const ImageLayerMethod method, ExceptionInfo *exception)
00952 {
00953 ExceptionInfo
00954 *sans_exception;
00955
00956 Image
00957 *prev_image,
00958 *dup_image,
00959 *bgnd_image,
00960 *optimized_image;
00961
00962 RectangleInfo
00963 try_bounds,
00964 bgnd_bounds,
00965 dup_bounds,
00966 *bounds;
00967
00968 MagickBooleanType
00969 add_frames,
00970 try_cleared,
00971 cleared;
00972
00973 DisposeType
00974 *disposals;
00975
00976 register const Image
00977 *next;
00978
00979 register long
00980 i;
00981
00982 assert(image != (const Image *) NULL);
00983 assert(image->signature == MagickSignature);
00984 if (image->debug != MagickFalse)
00985 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00986 assert(exception != (ExceptionInfo *) NULL);
00987 assert(exception->signature == MagickSignature);
00988 assert(method == OptimizeLayer ||
00989 method == OptimizeImageLayer ||
00990 method == OptimizePlusLayer);
00991
00992
00993
00994
00995 add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
00996
00997
00998
00999 next=GetFirstImageInList(image);
01000 for (; next != (Image *) NULL; next=GetNextImageInList(next))
01001 {
01002 if ((next->columns != image->columns) || (next->rows != image->rows))
01003 ThrowImageException(OptionError,"ImagesAreNotTheSameSize");
01004
01005
01006
01007 }
01008
01009
01010
01011 next=GetFirstImageInList(image);
01012 bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
01013 GetImageListLength(next),(add_frames != MagickFalse ? 2UL : 1UL)*
01014 sizeof(*bounds));
01015 if (bounds == (RectangleInfo *) NULL)
01016 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01017 disposals=(DisposeType *) AcquireQuantumMemory((size_t)
01018 GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
01019 sizeof(*disposals));
01020 if (disposals == (DisposeType *) NULL)
01021 {
01022 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01023 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01024 }
01025
01026
01027
01028 prev_image=CloneImage(next,next->page.width,next->page.height,
01029 MagickTrue,exception);
01030 if (prev_image == (Image *) NULL)
01031 {
01032 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01033 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
01034 return((Image *) NULL);
01035 }
01036 prev_image->page=next->page;
01037 prev_image->page.x=0;
01038 prev_image->page.y=0;
01039 prev_image->dispose=NoneDispose;
01040
01041 prev_image->background_color.opacity=(Quantum) TransparentOpacity;
01042 (void) SetImageBackgroundColor(prev_image);
01043
01044
01045
01046
01047 disposals[0]=NoneDispose;
01048 bounds[0]=CompareImageBounds(prev_image,next,CompareAnyLayer,exception);
01049
01050
01051
01052 i=1;
01053 bgnd_image=(Image *)NULL;
01054 dup_image=(Image *)NULL;
01055 dup_bounds.width=0;
01056 dup_bounds.height=0;
01057 dup_bounds.x=0;
01058 dup_bounds.y=0;
01059 next=GetNextImageInList(next);
01060 for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
01061 {
01062
01063
01064
01065 bounds[i]=CompareImageBounds(next->previous,next,CompareAnyLayer,exception);
01066 cleared=IsBoundsCleared(next->previous,next,&bounds[i],exception);
01067 disposals[i-1]=NoneDispose;
01068 if ( bounds[i].x < 0 ) {
01069
01070
01071
01072
01073
01074
01075 if ( add_frames && i>=2 ) {
01076 disposals[i-1]=DelDispose;
01077 disposals[i]=NoneDispose;
01078 bounds[i]=bounds[i-1];
01079 i++;
01080 continue;
01081 }
01082 }
01083 else
01084 {
01085
01086
01087
01088 try_bounds=CompareImageBounds(prev_image,next,CompareAnyLayer,exception);
01089 try_cleared=IsBoundsCleared(prev_image,next,&try_bounds,exception);
01090 if ( (!try_cleared && cleared ) ||
01091 try_bounds.width * try_bounds.height
01092 < bounds[i].width * bounds[i].height )
01093 {
01094 cleared=try_cleared;
01095 bounds[i]=try_bounds;
01096 disposals[i-1]=PreviousDispose;
01097 }
01098
01099
01100
01101
01102
01103
01104 dup_bounds.width=dup_bounds.height=0;
01105 if ( add_frames )
01106 {
01107 dup_image=CloneImage(next->previous,next->previous->page.width,
01108 next->previous->page.height,MagickTrue,exception);
01109 if (dup_image == (Image *) NULL)
01110 {
01111 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01112 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
01113 prev_image=DestroyImage(prev_image);
01114 return((Image *) NULL);
01115 }
01116 dup_bounds=CompareImageBounds(dup_image,next,CompareClearLayer,exception);
01117 ClearBounds(dup_image,&dup_bounds);
01118 try_bounds=CompareImageBounds(dup_image,next,CompareAnyLayer,exception);
01119 if ( cleared ||
01120 dup_bounds.width*dup_bounds.height
01121 +try_bounds.width*try_bounds.height
01122 < bounds[i].width * bounds[i].height )
01123 {
01124 cleared=MagickFalse;
01125 bounds[i]=try_bounds;
01126 disposals[i-1]=DupDispose;
01127
01128 }
01129 else
01130 dup_bounds.width=dup_bounds.height=0;
01131 }
01132
01133
01134
01135
01136 bgnd_image=CloneImage(next->previous,next->previous->page.width,
01137 next->previous->page.height,MagickTrue,exception);
01138 if (bgnd_image == (Image *) NULL)
01139 {
01140 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01141 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
01142 prev_image=DestroyImage(prev_image);
01143 if ( disposals[i-1] == DupDispose )
01144 bgnd_image=DestroyImage(bgnd_image);
01145 return((Image *) NULL);
01146 }
01147 bgnd_bounds=bounds[i-1];
01148 ClearBounds(bgnd_image,&bgnd_bounds);
01149 try_bounds=CompareImageBounds(bgnd_image,next,CompareAnyLayer,exception);
01150
01151 try_cleared=IsBoundsCleared(bgnd_image,next,&try_bounds,exception);
01152 if ( try_cleared )
01153 {
01154
01155
01156
01157
01158
01159
01160 try_bounds=CompareImageBounds(prev_image,next,CompareClearLayer,exception);
01161 if ( bgnd_bounds.x < 0 )
01162 bgnd_bounds = try_bounds;
01163 else
01164 {
01165 if ( try_bounds.x < bgnd_bounds.x )
01166 {
01167 bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x;
01168 if ( bgnd_bounds.width < try_bounds.width )
01169 bgnd_bounds.width = try_bounds.width;
01170 bgnd_bounds.x = try_bounds.x;
01171 }
01172 else
01173 {
01174 try_bounds.width += try_bounds.x - bgnd_bounds.x;
01175 if ( bgnd_bounds.width < try_bounds.width )
01176 bgnd_bounds.width = try_bounds.width;
01177 }
01178 if ( try_bounds.y < bgnd_bounds.y )
01179 {
01180 bgnd_bounds.height += bgnd_bounds.y - try_bounds.y;
01181 if ( bgnd_bounds.height < try_bounds.height )
01182 bgnd_bounds.height = try_bounds.height;
01183 bgnd_bounds.y = try_bounds.y;
01184 }
01185 else
01186 {
01187 try_bounds.height += try_bounds.y - bgnd_bounds.y;
01188 if ( bgnd_bounds.height < try_bounds.height )
01189 bgnd_bounds.height = try_bounds.height;
01190 }
01191 }
01192 ClearBounds(bgnd_image,&bgnd_bounds);
01193 try_bounds=CompareImageBounds(bgnd_image,next,CompareAnyLayer,exception);
01194 }
01195
01196
01197
01198
01199 if ( cleared ||
01200 bgnd_bounds.width*bgnd_bounds.height
01201 +try_bounds.width*try_bounds.height
01202 < bounds[i-1].width*bounds[i-1].height
01203 +dup_bounds.width*dup_bounds.height
01204 +bounds[i].width*bounds[i].height )
01205 {
01206 cleared=MagickFalse;
01207 bounds[i-1]=bgnd_bounds;
01208 bounds[i]=try_bounds;
01209 if ( disposals[i-1] == DupDispose )
01210 dup_image=DestroyImage(dup_image);
01211 disposals[i-1]=BackgroundDispose;
01212 }
01213 }
01214
01215
01216
01217
01218 if ( disposals[i-1] == DupDispose )
01219 {
01220 if (bgnd_image != (Image *) NULL)
01221 bgnd_image=DestroyImage(bgnd_image);
01222 prev_image=DestroyImage(prev_image);
01223 prev_image=dup_image, dup_image=(Image *) NULL;
01224 bounds[i+1]=bounds[i];
01225 bounds[i]=dup_bounds;
01226 disposals[i-1]=DupDispose;
01227 disposals[i]=BackgroundDispose;
01228 i++;
01229 }
01230 else
01231 {
01232 if ( disposals[i-1] != PreviousDispose )
01233 prev_image=DestroyImage(prev_image);
01234 if ( disposals[i-1] == BackgroundDispose )
01235 prev_image=bgnd_image, bgnd_image=(Image *)NULL;
01236 else if (bgnd_image != (Image *) NULL)
01237 bgnd_image=DestroyImage(bgnd_image);
01238 if ( dup_image != (Image *) NULL)
01239 dup_image=DestroyImage(dup_image);
01240 if ( disposals[i-1] == NoneDispose )
01241 {
01242 prev_image=CloneImage(next->previous,next->previous->page.width,
01243 next->previous->page.height,MagickTrue,exception);
01244 if (prev_image == (Image *) NULL)
01245 {
01246 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01247 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
01248 return((Image *) NULL);
01249 }
01250 }
01251 }
01252 disposals[i]=disposals[i-1];
01253 i++;
01254 }
01255 prev_image=DestroyImage(prev_image);
01256
01257
01258
01259 sans_exception=AcquireExceptionInfo();
01260 i=0;
01261 next=GetFirstImageInList(image);
01262 optimized_image=NewImageList();
01263 while ( next != (const Image *) NULL )
01264 {
01265 #if 0
01266 printf("image %ld :- %d %ldx%ld%+ld%+ld\n", i, disposals[i],
01267 bounds[i].width, bounds[i].height, bounds[i].x, bounds[i].y );
01268 #endif
01269 prev_image=CloneImage(next,0,0,MagickTrue,exception);
01270 if (prev_image == (Image *) NULL)
01271 break;
01272 if ( disposals[i] == DelDispose ) {
01273 unsigned long time = 0;
01274 while ( disposals[i] == DelDispose ) {
01275 time += next->delay*1000/next->ticks_per_second;
01276 next=GetNextImageInList(next);
01277 i++;
01278 }
01279 time += next->delay*1000/next->ticks_per_second;
01280 prev_image->ticks_per_second = 100L;
01281 prev_image->delay = time*prev_image->ticks_per_second/1000;
01282 }
01283 bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
01284 prev_image=DestroyImage(prev_image);
01285 if (bgnd_image == (Image *) NULL)
01286 break;
01287 bgnd_image->dispose=disposals[i];
01288 if ( disposals[i] == DupDispose ) {
01289 bgnd_image->delay=0;
01290 bgnd_image->dispose=NoneDispose;
01291 }
01292 else
01293 next=GetNextImageInList(next);
01294 AppendImageToList(&optimized_image,bgnd_image);
01295 i++;
01296 }
01297 sans_exception=DestroyExceptionInfo(sans_exception);
01298 bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
01299 disposals=(DisposeType *) RelinquishMagickMemory(disposals);
01300 if (next != (Image *) NULL)
01301 {
01302 optimized_image=DestroyImageList(optimized_image);
01303 return((Image *) NULL);
01304 }
01305 return(GetFirstImageInList(optimized_image));
01306 }
01307
01308
01309
01310
01311
01312
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336 MagickExport Image *OptimizeImageLayers(const Image *image,
01337 ExceptionInfo *exception)
01338 {
01339 return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
01340 }
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354
01355
01356
01357
01358
01359
01360
01361
01362
01363
01364
01365
01366
01367
01368
01369 MagickExport Image *OptimizePlusImageLayers(const Image *image,
01370 ExceptionInfo *exception)
01371 {
01372 return OptimizeLayerFrames(image, OptimizePlusLayer, exception);
01373 }
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406 MagickExport void OptimizeImageTransparency(const Image *image,
01407 ExceptionInfo *exception)
01408 {
01409 Image
01410 *dispose_image;
01411
01412 register Image
01413 *next;
01414
01415
01416
01417
01418 assert(image != (Image *) NULL);
01419 assert(image->signature == MagickSignature);
01420 if (image->debug != MagickFalse)
01421 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01422 assert(exception != (ExceptionInfo *) NULL);
01423 assert(exception->signature == MagickSignature);
01424 next=GetFirstImageInList(image);
01425 dispose_image=CloneImage(next,next->page.width,next->page.height,
01426 MagickTrue,exception);
01427 if (dispose_image == (Image *) NULL)
01428 return;
01429 dispose_image->page=next->page;
01430 dispose_image->page.x=0;
01431 dispose_image->page.y=0;
01432 dispose_image->dispose=NoneDispose;
01433 dispose_image->background_color.opacity=(Quantum) TransparentOpacity;
01434 (void) SetImageBackgroundColor(dispose_image);
01435
01436 while ( next != (Image *) NULL )
01437 {
01438 Image
01439 *current_image;
01440
01441
01442
01443
01444 current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
01445 if (current_image == (Image *) NULL)
01446 {
01447 dispose_image=DestroyImage(dispose_image);
01448 return;
01449 }
01450 (void) CompositeImage(current_image,next->matte != MagickFalse ?
01451 OverCompositeOp : CopyCompositeOp, next,next->page.x,next->page.y);
01452
01453
01454
01455
01456
01457 if (next->dispose == BackgroundDispose)
01458 {
01459 RectangleInfo
01460 bounds=next->page;
01461
01462 bounds.width=next->columns;
01463 bounds.height=next->rows;
01464 if (bounds.x < 0)
01465 {
01466 bounds.width+=bounds.x;
01467 bounds.x=0;
01468 }
01469 if ((long) (bounds.x+bounds.width) > (long) current_image->columns)
01470 bounds.width=current_image->columns-bounds.x;
01471 if (bounds.y < 0)
01472 {
01473 bounds.height+=bounds.y;
01474 bounds.y=0;
01475 }
01476 if ((long) (bounds.y+bounds.height) > (long) current_image->rows)
01477 bounds.height=current_image->rows-bounds.y;
01478 ClearBounds(current_image, &bounds);
01479 }
01480 if (next->dispose != PreviousDispose)
01481 {
01482 dispose_image=DestroyImage(dispose_image);
01483 dispose_image=current_image;
01484 }
01485 else
01486 current_image=DestroyImage(current_image);
01487
01488
01489
01490
01491 next=GetNextImageInList(next);
01492 if ( next != (Image *) NULL ) {
01493 (void) CompositeImage(next, ChangeMaskCompositeOp,
01494 dispose_image, -(next->page.x), -(next->page.y) );
01495 }
01496 }
01497 dispose_image=DestroyImage(dispose_image);
01498 return;
01499 }
01500
01501
01502
01503
01504
01505
01506
01507
01508
01509
01510
01511
01512
01513
01514
01515
01516
01517
01518
01519
01520
01521
01522
01523
01524
01525
01526
01527
01528
01529
01530
01531
01532 MagickExport void RemoveDuplicateLayers(Image **images,
01533 ExceptionInfo *exception)
01534 {
01535 register Image
01536 *curr,
01537 *next;
01538
01539 RectangleInfo
01540 bounds;
01541
01542 assert((*images) != (const Image *) NULL);
01543 assert((*images)->signature == MagickSignature);
01544 if ((*images)->debug != MagickFalse)
01545 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
01546 assert(exception != (ExceptionInfo *) NULL);
01547 assert(exception->signature == MagickSignature);
01548
01549 curr=GetFirstImageInList(*images);
01550 for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next)
01551 {
01552 if ( curr->columns != next->columns || curr->rows != next->rows
01553 || curr->page.x != next->page.x || curr->page.y != next->page.y )
01554 continue;
01555 bounds=CompareImageBounds(curr,next,CompareAnyLayer,exception);
01556 if ( bounds.x < 0 ) {
01557
01558
01559
01560 unsigned long time;
01561 time = curr->delay*1000/curr->ticks_per_second;
01562 time += next->delay*1000/next->ticks_per_second;
01563 next->ticks_per_second = 100L;
01564 next->delay = time*curr->ticks_per_second/1000;
01565 next->iterations = curr->iterations;
01566 *images = curr;
01567 (void) DeleteImageFromList(images);
01568 }
01569 }
01570 *images = GetFirstImageInList(*images);
01571 }
01572
01573
01574
01575
01576
01577
01578
01579
01580
01581
01582
01583
01584
01585
01586
01587
01588
01589
01590
01591
01592
01593
01594
01595
01596
01597
01598
01599
01600
01601
01602
01603
01604
01605
01606
01607
01608
01609
01610
01611
01612
01613 MagickExport void RemoveZeroDelayLayers(Image **images,
01614 ExceptionInfo *exception)
01615 {
01616 Image
01617 *i;
01618
01619 assert((*images) != (const Image *) NULL);
01620 assert((*images)->signature == MagickSignature);
01621 if ((*images)->debug != MagickFalse)
01622 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
01623 assert(exception != (ExceptionInfo *) NULL);
01624 assert(exception->signature == MagickSignature);
01625
01626 i=GetFirstImageInList(*images);
01627 for ( ; i != (Image *) NULL; i=GetNextImageInList(i))
01628 if ( i->delay != 0L ) break;
01629 if ( i == (Image *) NULL ) {
01630 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
01631 "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename);
01632 return;
01633 }
01634 i=GetFirstImageInList(*images);
01635 while ( i != (Image *) NULL )
01636 {
01637 if ( i->delay == 0L ) {
01638 (void) DeleteImageFromList(&i);
01639 *images=i;
01640 }
01641 else
01642 i=GetNextImageInList(i);
01643 }
01644 *images=GetFirstImageInList(*images);
01645 }
01646
01647
01648
01649
01650
01651
01652
01653
01654
01655
01656
01657
01658
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675
01676
01677
01678
01679
01680
01681
01682
01683
01684
01685
01686
01687
01688
01689
01690
01691
01692
01693
01694
01695
01696
01697
01698 static inline void CompositeCanvas(Image *destination,
01699 const CompositeOperator compose, Image *source,
01700 long x_offset, long y_offset )
01701 {
01702 x_offset += source->page.x - destination->page.x;
01703 y_offset += source->page.y - destination->page.y;
01704 (void) CompositeImage(destination, compose, source, x_offset, y_offset);
01705 }
01706
01707 MagickExport void CompositeLayers(Image *destination,
01708 const CompositeOperator compose, Image *source,
01709 const long x_offset, const long y_offset,
01710 ExceptionInfo *exception)
01711 {
01712 assert(destination != (Image *) NULL);
01713 assert(destination->signature == MagickSignature);
01714 assert(source != (Image *) NULL);
01715 assert(source->signature == MagickSignature);
01716 assert(exception != (ExceptionInfo *) NULL);
01717 assert(exception->signature == MagickSignature);
01718 if (source->debug != MagickFalse || destination->debug != MagickFalse)
01719 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
01720 source->filename, destination->filename);
01721
01722
01723
01724
01725 if ( source->previous == (Image *) NULL && source->next == (Image *) NULL )
01726 while ( destination != (Image *) NULL )
01727 {
01728 CompositeCanvas(destination, compose, source, x_offset, y_offset);
01729 destination=GetNextImageInList(destination);
01730 }
01731
01732
01733
01734
01735
01736
01737
01738
01739 else if ( destination->previous == (Image *) NULL &&
01740 destination->next == (Image *) NULL )
01741 {
01742 Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
01743
01744 CompositeCanvas(destination, compose, source, x_offset, y_offset);
01745
01746 source=GetNextImageInList(source);
01747
01748 while ( source != (Image *) NULL )
01749 {
01750 AppendImageToList(&destination,
01751 CloneImage(dest,0,0,MagickTrue,exception));
01752 destination=GetLastImageInList(destination);
01753
01754 CompositeCanvas(destination, compose, source, x_offset, y_offset);
01755 destination->delay = source->delay;
01756 destination->iterations = source->iterations;
01757 source=GetNextImageInList(source);
01758 }
01759 dest=DestroyImage(dest);
01760 }
01761
01762
01763
01764
01765
01766 else
01767 while ( source != (Image *) NULL && destination != (Image *) NULL )
01768 {
01769 CompositeCanvas(destination, compose, source, x_offset, y_offset);
01770 source=GetNextImageInList(source);
01771 destination=GetNextImageInList(destination);
01772 }
01773 }
01774
01775
01776
01777
01778
01779
01780
01781
01782
01783
01784
01785
01786
01787
01788
01789
01790
01791
01792
01793
01794
01795
01796
01797
01798
01799
01800
01801
01802
01803
01804
01805
01806
01807
01808
01809
01810
01811
01812
01813
01814
01815
01816
01817
01818
01819
01820
01821
01822
01823
01824
01825
01826
01827
01828 MagickExport Image *MergeImageLayers(Image *image,
01829 const ImageLayerMethod method,ExceptionInfo *exception)
01830 {
01831 #define MergeLayersTag "Merge/Layers"
01832
01833 Image
01834 *canvas;
01835
01836 MagickBooleanType
01837 proceed;
01838
01839 MagickOffsetType
01840 scene;
01841
01842 RectangleInfo
01843 page;
01844
01845 unsigned long
01846 width,
01847 height;
01848
01849 register const Image
01850 *next;
01851
01852 unsigned long
01853 number_images;
01854
01855 assert(image != (Image *) NULL);
01856 assert(image->signature == MagickSignature);
01857 if (image->debug != MagickFalse)
01858 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01859 assert(exception != (ExceptionInfo *) NULL);
01860 assert(exception->signature == MagickSignature);
01861
01862
01863
01864 page=image->page;
01865 width=image->columns;
01866 height=image->rows;
01867 switch (method)
01868 {
01869 case TrimBoundsLayer:
01870 case MergeLayer:
01871 default:
01872 {
01873 next = GetNextImageInList(image);
01874 for ( ; next != (Image *) NULL; next=GetNextImageInList(next)) {
01875 if ( page.x > next->page.x ) {
01876 width += page.x-next->page.x;
01877 page.x = next->page.x;
01878 }
01879 if ( page.y > next->page.y ) {
01880 height += page.y-next->page.y;
01881 page.y = next->page.y;
01882 }
01883 if ( width < (next->page.x + next->columns - page.x) )
01884 width = (unsigned long) next->page.x + next->columns - page.x;
01885 if ( height < (next->page.y + next->rows - page.y) )
01886 height = (unsigned long) next->page.y + next->rows - page.y;
01887 }
01888 break;
01889 }
01890 case FlattenLayer:
01891 {
01892 if ( page.width > 0 )
01893 width=page.width;
01894 if ( page.height > 0 )
01895 height=page.height;
01896 page.x=0;
01897 page.y=0;
01898 break;
01899 }
01900 case MosaicLayer:
01901 {
01902 if ( page.width > 0 )
01903 width=page.width;
01904 if ( page.height > 0 )
01905 height=page.height;
01906 for (next=image; next != (Image *) NULL; next=GetNextImageInList(next)) {
01907 if (method == MosaicLayer) {
01908 page.x=next->page.x;
01909 page.y=next->page.y;
01910 if ( width < (next->page.x + next->columns) )
01911 width = (unsigned long) next->page.x + next->columns;
01912 if ( height < (next->page.y + next->rows) )
01913 height = (unsigned long) next->page.y + next->rows;
01914 }
01915 }
01916 page.width=width;
01917 page.height=height;
01918 page.x=0;
01919 page.y=0;
01920 }
01921 break;
01922 }
01923
01924 if ( page.width == 0 )
01925 page.width = (page.x < 0) ? width : width+page.x;
01926 if ( page.height == 0 )
01927 page.height = (page.y < 0) ? height : height+page.y;
01928
01929
01930
01931
01932 if ( method == TrimBoundsLayer ) {
01933 number_images=GetImageListLength(image);
01934 for (scene=0; scene < (long) number_images; scene++)
01935 {
01936 image->page.x -= page.x;
01937 image->page.y -= page.y;
01938 image->page.width = width;
01939 image->page.height = height;
01940 proceed=SetImageProgress(image,MergeLayersTag,scene,number_images);
01941 if (proceed == MagickFalse)
01942 break;
01943 image=GetNextImageInList(image);
01944 }
01945 return((Image *) NULL);
01946 }
01947
01948
01949
01950
01951 canvas=CloneImage(image,width,height,MagickTrue,exception);
01952 if (canvas == (Image *) NULL)
01953 return((Image *) NULL);
01954 (void) SetImageBackgroundColor(canvas);
01955 canvas->page=page;
01956 canvas->dispose=UndefinedDispose;
01957
01958
01959
01960
01961 number_images=GetImageListLength(image);
01962 for (scene=0; scene < (long) number_images; scene++)
01963 {
01964 (void) CompositeImage(canvas,image->compose,image,image->page.x-
01965 canvas->page.x,image->page.y-canvas->page.y);
01966 proceed=SetImageProgress(image,MergeLayersTag,scene,number_images);
01967 if (proceed == MagickFalse)
01968 break;
01969 image=GetNextImageInList(image);
01970 }
01971 return(canvas);
01972 }
01973