1 /*
2 % Copyright (C) 2003 - 2020 GraphicsMagick Group
3 % Copyright (C) 2002 ImageMagick Studio
4 % Copyright 1991-1999 E. I. du Pont de Nemours and Company
5 %
6 % This program is covered by multiple licenses, which are described in
7 % Copyright.txt. You should have received a copy of Copyright.txt with this
8 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9 %
10 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11 %                                                                             %
12 %                                                                             %
13 %                                                                             %
14 %       TTTTT  RRRR    AAA   N   N  SSSSS  FFFFF   OOO   RRRR   M   M         %
15 %         T    R   R  A   A  NN  N  SS     F      O   O  R   R  MM MM         %
16 %         T    RRRR   AAAAA  N N N   SSS   FFF    O   O  RRRR   M M M         %
17 %         T    R R    A   A  N  NN     SS  F      O   O  R R    M   M         %
18 %         T    R  R   A   A  N   N  SSSSS  F       OOO   R  R   M   M         %
19 %                                                                             %
20 %                                                                             %
21 %                   GraphicsMagick Image Transform Methods                    %
22 %                                                                             %
23 %                                                                             %
24 %                              Software Design                                %
25 %                                John Cristy                                  %
26 %                                 July 1992                                   %
27 %                                                                             %
28 %                                                                             %
29 %                                                                             %
30 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31 %
32 %
33 %
34 */
35 
36 /*
37   Include declarations.
38 */
39 #include "magick/studio.h"
40 #include "magick/analyze.h"
41 #include "magick/color.h"
42 #include "magick/composite.h"
43 #include "magick/monitor.h"
44 #include "magick/pixel_cache.h"
45 #include "magick/resize.h"
46 #include "magick/texture.h"
47 #include "magick/transform.h"
48 #include "magick/utility.h"
49 #include "magick/log.h"
50 
51 /*
52 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53 %                                                                             %
54 %                                                                             %
55 %                                                                             %
56 %   C h o p I m a g e                                                         %
57 %                                                                             %
58 %                                                                             %
59 %                                                                             %
60 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
61 %
62 %  Chop() removes a region of an image and collapses the image to occupy the
63 %  removed portion.
64 %
65 %  The format of the ChopImage method is:
66 %
67 %      Image *ChopImage(const Image *image,const RectangleInfo *chop_info
68 %        ExceptionInfo *exception)
69 %
70 %  A description of each parameter follows:
71 %
72 %    o image: The image.
73 %
74 %    o chop_info: Define the region of the image to chop.
75 %
76 %    o exception: Return any errors or warnings in this structure.
77 %
78 %
79 */
ChopImage(const Image * image,const RectangleInfo * chop_info,ExceptionInfo * exception)80 MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
81                               ExceptionInfo *exception)
82 {
83 #define ChopImageText "[%s] Chop..."
84 
85   Image
86     *chop_image;
87 
88   unsigned long
89     row_count=0;
90 
91   MagickBool
92     monitor_active;
93 
94   long
95     y;
96 
97   RectangleInfo
98     clone_info;
99 
100   MagickPassFail
101     status=MagickPass;
102 
103   /*
104     Check chop geometry.
105   */
106   assert(image != (const Image *) NULL);
107   assert(image->signature == MagickSignature);
108   assert(exception != (ExceptionInfo *) NULL);
109   assert(exception->signature == MagickSignature);
110   assert(chop_info != (RectangleInfo *) NULL);
111   if (((chop_info->x+(long) chop_info->width) < 0) ||
112       ((chop_info->y+(long) chop_info->height) < 0) ||
113       (chop_info->x > (long) image->columns) ||
114       (chop_info->y > (long) image->rows))
115     ThrowImageException3(OptionError,GeometryDoesNotContainImage,
116                          UnableToChopImage);
117   clone_info=(*chop_info);
118   if ((clone_info.x+(long) clone_info.width) > (long) image->columns)
119     clone_info.width=(unsigned long) ((long) image->columns-clone_info.x);
120   if ((clone_info.y+(long) clone_info.height) > (long) image->rows)
121     clone_info.height=(unsigned long) ((long) image->rows-clone_info.y);
122   if (clone_info.x < 0)
123     {
124       clone_info.width-=(unsigned long) (-clone_info.x);
125       clone_info.x=0;
126     }
127   if (clone_info.y < 0)
128     {
129       clone_info.height-=(unsigned long) (-clone_info.y);
130       clone_info.y=0;
131     }
132   /*
133     Initialize chop image attributes.
134   */
135   chop_image=CloneImage(image,image->columns-clone_info.width,
136                         image->rows-clone_info.height,False,exception);
137   if (chop_image == (Image *) NULL)
138     return((Image *) NULL);
139 
140   /*
141     Extract chop image.
142   */
143   monitor_active=MagickMonitorActive();
144 
145 #if defined(HAVE_OPENMP)  && !defined(DisableSlowOpenMP)
146 #  if defined(TUNE_OPENMP)
147 #    pragma omp parallel for schedule(runtime) shared(row_count, status)
148 #  else
149 #    pragma omp parallel for schedule(static,4) shared(row_count, status)
150 #  endif
151 #endif
152   for (y=0; y < (long) clone_info.y; y++)
153     {
154       register const PixelPacket
155         *p;
156 
157       register const IndexPacket
158         *indexes;
159 
160       register IndexPacket
161         *chop_indexes;
162 
163       register long
164         x;
165 
166       register PixelPacket
167         *q;
168 
169       MagickBool
170         thread_status;
171 
172       thread_status=status;
173       if (thread_status == MagickFail)
174         continue;
175 
176       p=AcquireImagePixels(image,0,y,image->columns,1,exception);
177       q=SetImagePixelsEx(chop_image,0,y,chop_image->columns,1,exception);
178       if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
179         thread_status=MagickFail;
180 
181       if (thread_status != MagickFail)
182         {
183           indexes=AccessImmutableIndexes(image);
184           chop_indexes=AccessMutableIndexes(chop_image);
185           for (x=0; x < (long) image->columns; x++)
186             {
187               if ((x < clone_info.x) || (x >= (long) (clone_info.x+clone_info.width)))
188                 {
189                   if ((indexes != (const IndexPacket *) NULL) &&
190                       (chop_indexes != (IndexPacket *) NULL))
191                     *chop_indexes++=indexes[x];
192                   *q=(*p);
193                   q++;
194                 }
195               p++;
196             }
197           if (!SyncImagePixelsEx(chop_image,exception))
198             thread_status=MagickFail;
199         }
200 
201 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
202 #  pragma omp atomic
203 #endif
204       row_count++;
205 
206       if (monitor_active)
207         {
208           unsigned long
209             thread_row_count;
210 
211 #if defined(HAVE_OPENMP)
212 #  pragma omp flush (row_count)
213 #endif
214           thread_row_count=row_count;
215           if (QuantumTick(thread_row_count,chop_image->rows))
216             if (!MagickMonitorFormatted(thread_row_count,chop_image->rows,exception,
217                                         ChopImageText,image->filename))
218               thread_status=MagickFail;
219         }
220 
221       if (thread_status == MagickFail)
222         {
223           status=MagickFail;
224 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
225 #  pragma omp flush (status)
226 #endif
227         }
228     }
229   /*
230     Extract chop image.
231   */
232 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
233 #  if defined(TUNE_OPENMP)
234 #    pragma omp parallel for schedule(runtime) shared(row_count, status)
235 #  else
236 #    pragma omp parallel for schedule(static,4) shared(row_count, status)
237 #  endif
238 #endif
239   for (y=0; y < (long) (image->rows-(clone_info.y+clone_info.height)); y++)
240     {
241       register const PixelPacket
242         *p;
243 
244       register const IndexPacket
245         *indexes;
246 
247       register IndexPacket
248         *chop_indexes;
249 
250       register long
251         x;
252 
253       register PixelPacket
254         *q;
255 
256       MagickBool
257         thread_status;
258 
259       thread_status=status;
260       if (thread_status == MagickFail)
261         continue;
262 
263       p=AcquireImagePixels(image,0,clone_info.y+clone_info.height+y,image->columns,1,exception);
264       q=SetImagePixelsEx(chop_image,0,clone_info.y+y,chop_image->columns,1,exception);
265       if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
266         thread_status=MagickFail;
267 
268       if (thread_status != MagickFail)
269         {
270           indexes=AccessImmutableIndexes(image);
271           chop_indexes=AccessMutableIndexes(chop_image);
272           for (x=0; x < (long) image->columns; x++)
273             {
274               if ((x < clone_info.x) || (x >= (long) (clone_info.x+clone_info.width)))
275                 {
276                   if ((indexes != (const IndexPacket *) NULL) &&
277                       (chop_indexes != (IndexPacket *) NULL))
278                     *chop_indexes++=indexes[x];
279                   *q=(*p);
280                   q++;
281                 }
282               p++;
283             }
284           if (!SyncImagePixelsEx(chop_image,exception))
285             thread_status=MagickFail;
286         }
287 
288 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
289 #  pragma omp atomic
290 #endif
291       row_count++;
292 
293       if (monitor_active)
294         {
295           unsigned long
296             thread_row_count;
297 
298 #if defined(HAVE_OPENMP)
299 #  pragma omp flush (row_count)
300 #endif
301           thread_row_count=row_count;
302           if (QuantumTick(thread_row_count,chop_image->rows))
303             if (!MagickMonitorFormatted(thread_row_count,chop_image->rows,exception,
304                                         ChopImageText,image->filename))
305               thread_status=MagickFail;
306         }
307 
308       if (thread_status == MagickFail)
309         {
310           status=MagickFail;
311 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
312 #  pragma omp flush (status)
313 #endif
314         }
315     }
316   if (row_count < chop_image->rows)
317     {
318       DestroyImage(chop_image);
319       return((Image *) NULL);
320     }
321   chop_image->is_grayscale=image->is_grayscale;
322   return(chop_image);
323 }
324 
325 /*
326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327 %                                                                             %
328 %                                                                             %
329 %                                                                             %
330 %     C o a l e s c e I m a g e s                                             %
331 %                                                                             %
332 %                                                                             %
333 %                                                                             %
334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335 %
336 %  CoalesceImages() composites a set of images while respecting any page
337 %  offsets and disposal methods.  GIF, MIFF, and MNG animation sequences
338 %  typically start with an image background and each subsequent image
339 %  varies in size and offset.  CoalesceImages() returns a new sequence
340 %  where each image in the sequence is the same size as the first and
341 %  composited with the next image in the sequence.
342 %
343 %  The format of the CoalesceImages method is:
344 %
345 %      Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
346 %
347 %  A description of each parameter follows:
348 %
349 %    o image: The image sequence.
350 %
351 %    o exception: Return any errors or warnings in this structure.
352 %
353 */
CoalesceImages(const Image * image,ExceptionInfo * exception)354 MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
355 {
356   Image
357     *coalesce_image,
358     *previous_image;
359 
360   register const Image
361     *next;
362 
363   register long
364     i;
365 
366   MagickBool
367     found_transparency=False;
368 
369   /*
370     Coalesce the image sequence.
371   */
372   assert(image != (Image *) NULL);
373   assert(image->signature == MagickSignature);
374   assert(exception != (ExceptionInfo *) NULL);
375   assert(exception->signature == MagickSignature);
376   if (image->next == (Image *) NULL)
377     ThrowImageException3(ImageError,ImageSequenceIsRequired,
378       UnableToCoalesceImage);
379   /*
380     Clone first image in sequence.
381   */
382   coalesce_image=CloneImage(image,0,0,True,exception);
383   if (coalesce_image == (Image *) NULL)
384     return((Image *) NULL);
385   (void) memset(&coalesce_image->page,0,sizeof(RectangleInfo));
386   previous_image=coalesce_image;
387   /*
388     Coalesce image.
389   */
390   for (next=image->next; next != (Image *) NULL; next=next->next)
391   {
392     switch (next->dispose)
393     {
394       case UndefinedDispose:
395       case NoneDispose:
396       {
397         coalesce_image->next=CloneImage(coalesce_image,0,0,True,exception);
398         if (coalesce_image->next != (Image *) NULL)
399           previous_image=coalesce_image->next;
400         break;
401       }
402       case BackgroundDispose:
403       {
404         /*
405           Fill image with transparent color, if one exists.
406         */
407         coalesce_image->next=CloneImage(coalesce_image,0,0,True,exception);
408         if (coalesce_image->next != (Image *) NULL) {
409           for (i = 0; i < (long) coalesce_image->colors; i++) {
410             if (coalesce_image->colormap[i].opacity == TransparentOpacity) {
411               found_transparency = True;
412               (void) SetImageColor(coalesce_image->next,&coalesce_image->colormap[i]);
413               break;
414             }
415           }
416           if (!found_transparency)
417             (void) SetImage(coalesce_image->next,OpaqueOpacity);
418         }
419         break;
420       }
421       case PreviousDispose:
422       default:
423       {
424         coalesce_image->next=CloneImage(previous_image,0,0,True,exception);
425         break;
426       }
427     }
428     if (coalesce_image->next == (Image *) NULL)
429       {
430         DestroyImageList(coalesce_image);
431         return((Image *) NULL);
432       }
433     coalesce_image->next->previous=coalesce_image;
434     coalesce_image=coalesce_image->next;
435     coalesce_image->delay=next->delay;
436     coalesce_image->start_loop=next->start_loop;
437     (void) CompositeImage(coalesce_image,next->matte ? OverCompositeOp :
438       CopyCompositeOp,next,next->page.x,next->page.y);
439   }
440 
441   while (coalesce_image->previous != (Image *) NULL)
442     coalesce_image=coalesce_image->previous;
443   return(coalesce_image);
444 }
445 
446 /*
447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448 %                                                                             %
449 %                                                                             %
450 %                                                                             %
451 %   C r o p I m a g e                                                         %
452 %                                                                             %
453 %                                                                             %
454 %                                                                             %
455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
456 %
457 %  Use CropImage() to extract a region of the image starting at the offset
458 %  defined by geometry.  As a special feature, if the geometry "0x0" is
459 %  is passed, GetImageBoundingBox() is used to locate the edges of the
460 %  image and the image is cropped ("trimmed") to that boundary.
461 %
462 %  The format of the CropImage method is:
463 %
464 %      Image *CropImage(const Image *image,const RectangleInfo *geometry,
465 %        ExceptionInfo *exception)
466 %
467 %  A description of each parameter follows:
468 %
469 %    o image: The image.
470 %
471 %    o geometry: Define the region of the image to crop with members
472 %      x, y, width, and height.
473 %
474 %    o exception: Return any errors or warnings in this structure.
475 %
476 %
477 */
CropImage(const Image * image,const RectangleInfo * geometry,ExceptionInfo * exception)478 MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
479                               ExceptionInfo *exception)
480 {
481   Image
482     *crop_image;
483 
484   unsigned long
485     row_count=0;
486 
487   MagickBool
488     monitor_active;
489 
490   long
491     y;
492 
493   RectangleInfo
494     page;
495 
496   MagickPassFail
497     status=MagickPass;
498 
499   /*
500     Check crop geometry.
501   */
502   assert(image != (const Image *) NULL);
503   assert(image->signature == MagickSignature);
504   assert(geometry != (const RectangleInfo *) NULL);
505   assert(exception != (ExceptionInfo *) NULL);
506   assert(exception->signature == MagickSignature);
507   if ((geometry->width != 0) || (geometry->height != 0))
508     {
509       if (((geometry->x+(long) geometry->width) < 0) ||
510           ((geometry->y+(long) geometry->height) < 0) ||
511           (geometry->x >= (long) image->columns) ||
512           (geometry->y >= (long) image->rows))
513         ThrowImageException(OptionError,GeometryDoesNotContainImage,
514                             MagickMsg(ResourceLimitError,UnableToCropImage));
515     }
516   page=(*geometry);
517   if ((page.width != 0) || (page.height != 0))
518     {
519       if ((page.x+(long) page.width) > (long) image->columns)
520         page.width=image->columns-page.x;
521       if ((page.y+(long) page.height) > (long) image->rows)
522         page.height=image->rows-page.y;
523       if (page.x < 0)
524         {
525           page.width+=page.x;
526           page.x=0;
527         }
528       if (page.y < 0)
529         {
530           page.height+=page.y;
531           page.y=0;
532         }
533     }
534   else
535     {
536       /*
537         Set bounding box to the image dimensions.
538       */
539       page=GetImageBoundingBox(image,exception);
540       page.width+=geometry->x*2;
541       page.height+=geometry->y*2;
542       page.x-=geometry->x;
543       if (page.x < 0)
544         page.x=0;
545       page.y-=geometry->y;
546       if (page.y < 0)
547         page.y=0;
548       if ((((long) page.width+page.x) > (long) image->columns) ||
549           (((long) page.height+page.y) > (long) image->rows))
550         ThrowImageException(OptionError,GeometryDoesNotContainImage,
551                             MagickMsg(ResourceLimitError,UnableToCropImage));
552     }
553   if ((page.width == 0) || (page.height == 0))
554     ThrowImageException(OptionError,GeometryDimensionsAreZero,
555                         MagickMsg(ResourceLimitError,UnableToCropImage));
556   if ((page.width == image->columns) && (page.height == image->rows) &&
557       (page.x == 0) && (page.y == 0))
558     return(CloneImage(image,0,0,True,exception));
559   /*
560     Initialize crop image attributes.
561   */
562   crop_image=CloneImage(image,page.width,page.height,True,exception);
563   if (crop_image == (Image *) NULL)
564     return((Image *) NULL);
565   /*
566     Extract crop image.
567   */
568   crop_image->page=page;
569   if ((geometry->width == 0) || (geometry->height == 0))
570     (void) memset(&crop_image->page,0,sizeof(RectangleInfo));
571 
572   monitor_active=MagickMonitorActive();
573 
574 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
575 #  if defined(TUNE_OPENMP)
576 #    pragma omp parallel for schedule(runtime) shared(row_count, status)
577 #  else
578 #    pragma omp parallel for schedule(static,4) shared(row_count, status)
579 #  endif
580 #endif
581   for (y=0; y < (long) crop_image->rows; y++)
582     {
583       const PixelPacket
584         *p;
585 
586       const IndexPacket
587         *indexes;
588 
589       IndexPacket
590         *crop_indexes;
591 
592       PixelPacket
593         *q;
594 
595       MagickBool
596         thread_status;
597 
598       thread_status=status;
599       if (thread_status == MagickFail)
600         continue;
601 
602       p=AcquireImagePixels(image,page.x,page.y+y,crop_image->columns,1,exception);
603       q=SetImagePixelsEx(crop_image,0,y,crop_image->columns,1,exception);
604       if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
605         thread_status=MagickFail;
606 
607       if (thread_status != MagickFail)
608         {
609           (void) memcpy(q,p,crop_image->columns*sizeof(PixelPacket));
610           indexes=AccessImmutableIndexes(image);
611           crop_indexes=AccessMutableIndexes(crop_image);
612           if ((indexes != (const IndexPacket *) NULL) &&
613               (crop_indexes != (IndexPacket *) NULL))
614             (void) memcpy(crop_indexes,indexes,crop_image->columns*
615                           sizeof(IndexPacket));
616           if (!SyncImagePixelsEx(crop_image,exception))
617             thread_status=MagickFail;
618         }
619 
620 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
621 #  pragma omp atomic
622 #endif
623       row_count++;
624 
625       if (monitor_active)
626         {
627           unsigned long
628             thread_row_count;
629 
630 #if defined(HAVE_OPENMP)
631 #  pragma omp flush (row_count)
632 #endif
633           thread_row_count=row_count;
634           if (QuantumTick(thread_row_count,crop_image->rows))
635             if (!MagickMonitorFormatted(thread_row_count,crop_image->rows,exception,
636                                         "[%s] Crop: %lux%lu+%ld+%ld...",
637                                         crop_image->filename,
638                                         crop_image->columns,crop_image->rows,
639                                         page.x,page.y))
640               thread_status=MagickFail;
641         }
642 
643       if (thread_status == MagickFail)
644         {
645           status=MagickFail;
646 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
647 #  pragma omp flush (status)
648 #endif
649         }
650     }
651   if (row_count < crop_image->rows)
652     {
653       DestroyImage(crop_image);
654       return((Image *) NULL);
655     }
656   crop_image->is_grayscale=image->is_grayscale;
657   return(crop_image);
658 }
659 
660 /*
661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662 %                                                                             %
663 %                                                                             %
664 %                                                                             %
665 %     D e c o n s t r u c t I m a g e s                                       %
666 %                                                                             %
667 %                                                                             %
668 %                                                                             %
669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670 %
671 %  DeconstructImages() compares each image with the next in a sequence and
672 %  returns the maximum bounding region of any pixel differences it discovers.
673 %
674 %  The format of the DeconstructImages method is:
675 %
676 %      Image *DeconstructImages(const Image *image,ExceptionInfo *exception)
677 %
678 %  A description of each parameter follows:
679 %
680 %    o image: The image.
681 %
682 %    o exception: Return any errors or warnings in this structure.
683 %
684 %
685 */
DeconstructImages(const Image * image,ExceptionInfo * exception)686 MagickExport Image *DeconstructImages(const Image *image,
687   ExceptionInfo *exception)
688 {
689   Image
690     *crop_image,
691     *crop_next,
692     *deconstruct_image;
693 
694   long
695     y;
696 
697   RectangleInfo
698     *bounds;
699 
700   register const Image
701     *next;
702 
703   register const PixelPacket
704     *p;
705 
706   register long
707     i,
708     x;
709 
710   register PixelPacket
711     *q;
712 
713   assert(image != (const Image *) NULL);
714   assert(image->signature == MagickSignature);
715   assert(exception != (ExceptionInfo *) NULL);
716   assert(exception->signature == MagickSignature);
717   if (image->next == (Image *) NULL)
718     ThrowImageException3(ImageError,ImageSequenceIsRequired,
719       UnableToDeconstructImageSequence);
720   /*
721     Ensure the image are the same size.
722   */
723   for (next=image; next != (Image *) NULL; next=next->next)
724   {
725     if ((next->columns != image->columns) || (next->rows != image->rows))
726       ThrowImageException(OptionError,ImagesAreNotTheSameSize,
727         MagickMsg(ImageError,UnableToDeconstructImageSequence));
728   }
729   /*
730     Allocate memory.
731   */
732   bounds=MagickAllocateMemory(RectangleInfo *,
733     GetImageListLength(image)*sizeof(RectangleInfo));
734   if (bounds == (RectangleInfo *) NULL)
735     ThrowImageException(ResourceLimitError,MemoryAllocationFailed,
736       MagickMsg(ImageError,UnableToDeconstructImageSequence));
737   /*
738     Compute the bounding box for each next in the sequence.
739   */
740   i=0;
741   for (next=image->next; next != (const Image *) NULL; next=next->next)
742   {
743     /*
744       Set bounding box to the next dimensions.
745     */
746     for (x=0; x < (long) next->columns; x++)
747     {
748       p=AcquireImagePixels(next,x,0,1,next->rows,exception);
749       q=GetImagePixels(next->previous,x,0,1,next->previous->rows);
750       if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
751         break;
752       for (y=0; y < (long) next->rows; y++)
753       {
754         if (!FuzzyColorMatch(p,q,next->fuzz))
755           break;
756         p++;
757         q++;
758       }
759       if (y < (long) next->rows)
760         break;
761     }
762     bounds[i].x=x;
763     for (y=0; y < (long) next->rows; y++)
764     {
765       p=AcquireImagePixels(next,0,y,next->columns,1,exception);
766       q=GetImagePixels(next->previous,0,y,next->previous->columns,1);
767       if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
768         break;
769       for (x=0; x < (long) next->columns; x++)
770       {
771         if (!FuzzyColorMatch(p,q,next->fuzz))
772           break;
773         p++;
774         q++;
775       }
776       if (x < (long) next->columns)
777         break;
778     }
779     bounds[i].y=y;
780     for (x=(long) next->columns-1; x >= 0; x--)
781     {
782       p=AcquireImagePixels(next,x,0,1,next->rows,exception);
783       q=GetImagePixels(next->previous,x,0,1,next->previous->rows);
784       if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
785         break;
786       for (y=0; y < (long) next->rows; y++)
787       {
788         if (!FuzzyColorMatch(p,q,next->fuzz))
789           break;
790         p++;
791         q++;
792       }
793       if (y < (long) next->rows)
794         break;
795     }
796     bounds[i].width=x-bounds[i].x+1;
797     for (y=(long) next->rows-1; y >= 0; y--)
798     {
799       p=AcquireImagePixels(next,0,y,next->columns,1,exception);
800       q=GetImagePixels(next->previous,0,y,next->previous->columns,1);
801       if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
802         break;
803       for (x=0; x < (long) next->columns; x++)
804       {
805         if (!FuzzyColorMatch(p,q,next->fuzz))
806           break;
807         p++;
808         q++;
809       }
810       if (x < (long) next->columns)
811         break;
812     }
813     bounds[i].height=y-bounds[i].y+1;
814     i++;
815   }
816   /*
817     Clone first image in sequence.
818   */
819   deconstruct_image=CloneImage(image,0,0,True,exception);
820   if (deconstruct_image == (Image *) NULL)
821     {
822       MagickFreeMemory(bounds);
823       return((Image *) NULL);
824     }
825   /*
826     Deconstruct the image sequence.
827   */
828   i=0;
829   for (next=image->next; next != (Image *) NULL; next=next->next)
830   {
831     crop_image=CloneImage(next,0,0,True,exception);
832     if (crop_image == (Image *) NULL)
833       break;
834     crop_next=CropImage(crop_image,&bounds[i++],exception);
835     DestroyImage(crop_image);
836     if (crop_next == (Image *) NULL)
837       break;
838     deconstruct_image->next=crop_next;
839     crop_next->previous=deconstruct_image;
840     deconstruct_image=deconstruct_image->next;
841   }
842   MagickFreeMemory(bounds);
843   while (deconstruct_image->previous != (Image *) NULL)
844     deconstruct_image=deconstruct_image->previous;
845   if (next != (Image *) NULL)
846     {
847       DestroyImageList(deconstruct_image);
848       return((Image *) NULL);
849     }
850   return(deconstruct_image);
851 }
852 
853 /*
854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855 %                                                                             %
856 %                                                                             %
857 %                                                                             %
858 %   E x t e n t I m a g e                                                     %
859 %                                                                             %
860 %                                                                             %
861 %                                                                             %
862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
863 %
864 %  Use ExtentImage() to change the image dimensions as specified by geometry
865 %  width and height.  The existing image content is composited at the position
866 %  specified by geometry x and y using the image compose method.  Existing
867 %  image content which falls outside the bounds of the new image dimensions
868 %  is discarded.
869 %
870 %  The format of the ExtentImage method is:
871 %
872 %      Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
873 %        ExceptionInfo *exception)
874 %
875 %  A description of each parameter follows:
876 %
877 %    o image: The image.
878 %
879 %    o geometry: Define the new image dimension with width and height, and
880 %        the top left coordinate to place the existing image content with
881 %        x and y.
882 %
883 %    o exception: Return any errors or warnings in this structure.
884 %
885 %
886 */
ExtentImage(const Image * image,const RectangleInfo * geometry,ExceptionInfo * exception)887 MagickExport Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
888                                 ExceptionInfo *exception)
889 {
890   Image
891     *extent_image;
892 
893   assert(image != (const Image *) NULL);
894   assert(image->signature == MagickSignature);
895   assert(geometry != (const RectangleInfo *) NULL);
896   assert(exception != (ExceptionInfo *) NULL);
897   assert(exception->signature == MagickSignature);
898 
899   /*
900     Allocate canvas image
901   */
902   if ((extent_image=CloneImage(image,geometry->width,geometry->height,
903                                MagickTrue,exception)) == (Image *) NULL)
904     return((Image *) NULL);
905 
906   /*
907     Set canvas image color to background color
908   */
909   if ((SetImage(extent_image,image->background_color.opacity)) == MagickFail)
910     {
911       CopyException(exception,&extent_image->exception);
912       DestroyImage(extent_image);
913       return((Image *) NULL);
914     }
915 
916   /*
917     Composite existing image at position using requested composition
918     operator.
919   */
920   if ((CompositeImage(extent_image,image->compose,image,geometry->x,
921                       geometry->y)) == MagickFail)
922     {
923       CopyException(exception,&extent_image->exception);
924       DestroyImage(extent_image);
925       return((Image *) NULL);
926     }
927 
928   return(extent_image);
929 }
930 
931 /*
932 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
933 %                                                                             %
934 %                                                                             %
935 %                                                                             %
936 %     F l a t t e n I m a g e                                                 %
937 %                                                                             %
938 %                                                                             %
939 %                                                                             %
940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
941 %
942 %  Method FlattenImage merges a sequence of images.  This is useful for
943 %  combining Photoshop layers into a single image.
944 %
945 %  The format of the FlattenImage method is:
946 %
947 %      Image *FlattenImage(const Image *image,ExceptionInfo *exception)
948 %
949 %  A description of each parameter follows:
950 %
951 %    o image: The image sequence.
952 %
953 %    o exception: Return any errors or warnings in this structure.
954 %
955 */
FlattenImages(const Image * image,ExceptionInfo * exception)956 MagickExport Image *FlattenImages(const Image *image,ExceptionInfo *exception)
957 {
958   Image
959     *flatten_image;
960 
961   register const Image
962     *next;
963 
964   /*
965     Flatten the image sequence.
966   */
967   assert(image != (Image *) NULL);
968   assert(image->signature == MagickSignature);
969   assert(exception != (ExceptionInfo *) NULL);
970   assert(exception->signature == MagickSignature);
971 
972   /*
973     Clone first image in sequence to serve as canvas image
974   */
975   flatten_image=CloneImage(image,0,0,True,exception);
976 
977   /*
978     Apply background color under image if it has a matte channel.
979   */
980   if ((flatten_image != (Image *) NULL) && (flatten_image->matte))
981     (void) MagickCompositeImageUnderColor(flatten_image,
982                                           &flatten_image->background_color,
983                                           exception);
984 
985   if ((flatten_image != (Image *) NULL) &&
986       (image->next != (Image *) NULL))
987     {
988       /*
989         Flatten remaining images onto canvas
990       */
991       for (next=image->next; next != (Image *) NULL; next=next->next)
992         (void) CompositeImage(flatten_image,next->compose,next,next->page.x,
993                               next->page.y);
994     }
995   return(flatten_image);
996 }
997 
998 /*
999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1000 %                                                                             %
1001 %                                                                             %
1002 %                                                                             %
1003 %   F l i p I m a g e                                                         %
1004 %                                                                             %
1005 %                                                                             %
1006 %                                                                             %
1007 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1008 %
1009 %  FlipImage() creates a vertical mirror image by reflecting the pixels
1010 %  around the central x-axis.
1011 %
1012 %  The format of the FlipImage method is:
1013 %
1014 %      Image *FlipImage(const Image *image,ExceptionInfo *exception)
1015 %
1016 %  A description of each parameter follows:
1017 %
1018 %    o image: The image.
1019 %
1020 %    o exception: Return any errors or warnings in this structure.
1021 %
1022 %
1023 */
FlipImage(const Image * image,ExceptionInfo * exception)1024 MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1025 {
1026 #define FlipImageText "[%s] Flip..."
1027 
1028   Image
1029     *flip_image;
1030 
1031   unsigned long
1032     row_count=0;
1033 
1034   MagickBool
1035     monitor_active;
1036 
1037   long
1038     y;
1039 
1040   MagickPassFail
1041     status=MagickPass;
1042 
1043   /*
1044     Initialize flip image attributes.
1045   */
1046   assert(image != (const Image *) NULL);
1047   assert(image->signature == MagickSignature);
1048   assert(exception != (ExceptionInfo *) NULL);
1049   assert(exception->signature == MagickSignature);
1050 
1051   if ((image->columns == 0UL) || (image->rows == 0UL))
1052     ThrowImageException(ImageError,UnableToResizeImage,
1053                         MagickMsg(OptionError,NonzeroWidthAndHeightRequired));
1054 
1055   if (((((size_t) Max(sizeof(PixelPacket),sizeof(IndexPacket)))*image->columns)/
1056        image->columns) != Max(sizeof(PixelPacket),sizeof(IndexPacket)))
1057     ThrowImageException(ImageError,WidthOrHeightExceedsLimit,image->filename);
1058 
1059   flip_image=CloneImage(image,image->columns,image->rows,True,exception);
1060   if (flip_image == (Image *) NULL)
1061     return((Image *) NULL);
1062   /*
1063     Flip each row.
1064   */
1065 
1066   monitor_active=MagickMonitorActive();
1067 
1068 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1069 #  if defined(TUNE_OPENMP)
1070 #    pragma omp parallel for schedule(runtime) shared(row_count, status)
1071 #  else
1072 #    pragma omp parallel for schedule(static,4) shared(row_count, status)
1073 #  endif
1074 #endif
1075   for (y=0; y < (long) flip_image->rows; y++)
1076     {
1077       const PixelPacket
1078         *p;
1079 
1080       const IndexPacket
1081         *indexes;
1082 
1083       IndexPacket
1084         *flip_indexes;
1085 
1086       PixelPacket
1087         *q;
1088 
1089       MagickBool
1090         thread_status;
1091 
1092       thread_status=status;
1093       if (thread_status == MagickFail)
1094         continue;
1095 
1096       p=AcquireImagePixels(image,0,y,image->columns,1,exception);
1097       q=SetImagePixelsEx(flip_image,0,(long) (flip_image->rows-y-1),
1098                          flip_image->columns,1,exception);
1099       if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1100         thread_status=MagickFail;
1101 
1102       if (thread_status != MagickFail)
1103         {
1104           (void) memcpy(q,p,flip_image->columns*sizeof(PixelPacket));
1105           indexes=AccessImmutableIndexes(image);
1106           flip_indexes=AccessMutableIndexes(flip_image);
1107           if ((indexes != (IndexPacket *) NULL) &&
1108               (flip_indexes != (IndexPacket *) NULL))
1109             (void) memcpy(flip_indexes,indexes,image->columns*sizeof(IndexPacket));
1110           if (!SyncImagePixelsEx(flip_image,exception))
1111             thread_status=MagickFail;
1112         }
1113 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1114 #  pragma omp atomic
1115 #endif
1116       row_count++;
1117 
1118       if (monitor_active)
1119         {
1120           unsigned long
1121             thread_row_count;
1122 
1123 #if defined(HAVE_OPENMP)
1124 #  pragma omp flush (row_count)
1125 #endif
1126           thread_row_count=row_count;
1127           if (QuantumTick(thread_row_count,flip_image->rows))
1128             if (!MagickMonitorFormatted(thread_row_count,flip_image->rows,exception,
1129                                         FlipImageText,image->filename))
1130               thread_status=MagickFail;
1131         }
1132 
1133       if (thread_status == MagickFail)
1134         {
1135           status=MagickFail;
1136 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1137 #  pragma omp flush (status)
1138 #endif
1139         }
1140     }
1141   if (row_count < flip_image->rows)
1142     {
1143       DestroyImage(flip_image);
1144       return((Image *) NULL);
1145     }
1146   flip_image->is_grayscale=image->is_grayscale;
1147   return(flip_image);
1148 }
1149 
1150 /*
1151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1152 %                                                                             %
1153 %                                                                             %
1154 %                                                                             %
1155 %   F l o p I m a g e                                                         %
1156 %                                                                             %
1157 %                                                                             %
1158 %                                                                             %
1159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1160 %
1161 %  FlopImage() creates a horizontal mirror image by reflecting the pixels
1162 %  around the central y-axis.
1163 %
1164 %  The format of the FlopImage method is:
1165 %
1166 %      Image *FlopImage(const Image *image,ExceptionInfo *exception)
1167 %
1168 %  A description of each parameter follows:
1169 %
1170 %    o image: The image.
1171 %
1172 %    o exception: Return any errors or warnings in this structure.
1173 %
1174 %
1175 */
FlopImage(const Image * image,ExceptionInfo * exception)1176 MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1177 {
1178 #define FlopImageText "[%s] Flop..."
1179 
1180   Image
1181     *flop_image;
1182 
1183   unsigned long
1184     row_count=0;
1185 
1186   MagickBool
1187     monitor_active;
1188 
1189   long
1190     y;
1191 
1192   MagickPassFail
1193     status=MagickPass;
1194 
1195   /*
1196     Initialize flop image attributes.
1197   */
1198   assert(image != (const Image *) NULL);
1199   assert(image->signature == MagickSignature);
1200   assert(exception != (ExceptionInfo *) NULL);
1201   assert(exception->signature == MagickSignature);
1202   flop_image=CloneImage(image,image->columns,image->rows,True,exception);
1203   if (flop_image == (Image *) NULL)
1204     return((Image *) NULL);
1205   /*
1206     Flop each row.
1207   */
1208 
1209   monitor_active=MagickMonitorActive();
1210 
1211 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1212 #  if defined(TUNE_OPENMP)
1213 #    pragma omp parallel for schedule(runtime) shared(row_count, status)
1214 #  else
1215 #    pragma omp parallel for schedule(static,4) shared(row_count, status)
1216 #  endif
1217 #endif
1218   for (y=0; y < (long) flop_image->rows; y++)
1219     {
1220       register const IndexPacket
1221         *indexes;
1222 
1223       register IndexPacket
1224         *flop_indexes;
1225 
1226       register const PixelPacket
1227         *p;
1228 
1229       register long
1230         x;
1231 
1232       register PixelPacket
1233         *q;
1234 
1235       MagickBool
1236         thread_status;
1237 
1238       thread_status=status;
1239       if (thread_status == MagickFail)
1240         continue;
1241 
1242       p=AcquireImagePixels(image,0,y,image->columns,1,exception);
1243       q=SetImagePixelsEx(flop_image,0,y,flop_image->columns,1,exception);
1244       if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1245         thread_status=MagickFail;
1246 
1247       if (thread_status != MagickFail)
1248         {
1249           indexes=AccessImmutableIndexes(image);
1250           flop_indexes=AccessMutableIndexes(flop_image);
1251           q+=flop_image->columns;
1252           for (x=0; x < (long) flop_image->columns; x++)
1253             {
1254               if ((indexes != (const IndexPacket *) NULL) &&
1255                   (flop_indexes != (IndexPacket *) NULL))
1256                 flop_indexes[flop_image->columns-x-1]=indexes[x];
1257               q--;
1258               *q=(*p);
1259               p++;
1260             }
1261           if (!SyncImagePixelsEx(flop_image,exception))
1262             thread_status=MagickFail;
1263         }
1264 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1265 #  pragma omp atomic
1266 #endif
1267       row_count++;
1268 
1269       if (monitor_active)
1270         {
1271           unsigned long
1272             thread_row_count;
1273 
1274 #if defined(HAVE_OPENMP)
1275 #  pragma omp flush (row_count)
1276 #endif
1277           thread_row_count=row_count;
1278           if (QuantumTick(thread_row_count,flop_image->rows))
1279             if (!MagickMonitorFormatted(thread_row_count,flop_image->rows,exception,
1280                                         FlopImageText,image->filename))
1281               thread_status=MagickFail;
1282         }
1283 
1284       if (thread_status == MagickFail)
1285         {
1286           status=MagickFail;
1287 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1288 #  pragma omp flush (status)
1289 #endif
1290         }
1291     }
1292   if (row_count < flop_image->rows)
1293     {
1294       DestroyImage(flop_image);
1295       return((Image *) NULL);
1296     }
1297   flop_image->is_grayscale=image->is_grayscale;
1298   return(flop_image);
1299 }
1300 
1301 /*
1302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1303 %                                                                             %
1304 %                                                                             %
1305 %                                                                             %
1306 +   G e t I m a g e M o s a i c D i m e n s i o n s                           %
1307 %                                                                             %
1308 %                                                                             %
1309 %                                                                             %
1310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1311 %
1312 %  Method GetImageMosaicDimensions returns the bounding region of the canvas
1313 %  which supports the images in the list as would be returned by
1314 %  MosaicImages().  The bounding region is computed based on the image size
1315 %  and page offsets of each image in the image list.
1316 %
1317 %  The format of the GetImageMosaicDimensions method is:
1318 %
1319 %      RectangleInfo GetImageMosaicDimensions(const Image *image)
1320 %
1321 %  A description of each parameter follows:
1322 %
1323 %    o bounds: Method GetImageMosaicDimensions returns the bounding box of
1324 %      the image canvas.
1325 %
1326 %    o image: The image.
1327 %
1328 %    o exception: Return any errors or warnings in this structure.
1329 %
1330 %
1331 */
1332 
1333 static RectangleInfo GetImageMosaicDimensions(const Image *image) MAGICK_FUNC_PURE;
1334 
GetImageMosaicDimensions(const Image * image)1335 static RectangleInfo GetImageMosaicDimensions(const Image *image)
1336 {
1337   RectangleInfo
1338     page;
1339 
1340   register const Image
1341     *next;
1342 
1343   page.width=image->columns;
1344   page.height=image->rows;
1345   page.x=0;
1346   page.y=0;
1347   for (next=image; next != (Image *) NULL; next=next->next)
1348   {
1349     page.x=next->page.x;
1350     page.y=next->page.y;
1351     /*
1352       Without casts, unsigned underflow can occur here if page offset
1353       is negative and has greater magnitude than image size.
1354     */
1355     if (((long) next->columns+page.x) > (long) page.width)
1356       page.width=(long) next->columns+page.x;
1357     if (next->page.width > page.width)
1358       page.width=next->page.width;
1359     if (((long) next->rows+page.y) > (long) page.height)
1360       page.height=(long) next->rows+page.y;
1361     if (next->page.height > page.height)
1362       page.height=next->page.height;
1363   }
1364 
1365   return page;
1366 }
1367 
1368 /*
1369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1370 %                                                                             %
1371 %                                                                             %
1372 %                                                                             %
1373 %     M o s a i c I m a g e s                                                 %
1374 %                                                                             %
1375 %                                                                             %
1376 %                                                                             %
1377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1378 %
1379 %  MosaicImages() inlays an image sequence to form a single coherent picture.
1380 %  It returns a single image with each image in the sequence composited at
1381 %  the location defined by the page member of the image structure.
1382 %
1383 %  The format of the MosaicImage method is:
1384 %
1385 %      Image *MosaicImages(const Image *image,ExceptionInfo *exception)
1386 %
1387 %  A description of each parameter follows:
1388 %
1389 %    o image: The image.
1390 %
1391 %    o exception: Return any errors or warnings in this structure.
1392 %
1393 %
1394 */
MosaicImages(const Image * image,ExceptionInfo * exception)1395 MagickExport Image *MosaicImages(const Image *image,ExceptionInfo *exception)
1396 {
1397 #define MosaicImageText "[%s] Create mosaic..."
1398 
1399   Image
1400     *mosaic_image;
1401 
1402   RectangleInfo
1403     page;
1404 
1405   register const Image
1406     *next;
1407 
1408   unsigned int
1409     scene;
1410 
1411   MagickBool
1412     matte;
1413 
1414   MagickPassFail
1415     status;
1416 
1417   size_t
1418     image_list_length;
1419 
1420   assert(image != (Image *) NULL);
1421   assert(image->signature == MagickSignature);
1422   assert(exception != (ExceptionInfo *) NULL);
1423   assert(exception->signature == MagickSignature);
1424 
1425   image_list_length=GetImageListLength(image);
1426 
1427   /*
1428     Determine mosaic bounding box.
1429   */
1430   page=GetImageMosaicDimensions(image);
1431 
1432   /*
1433     Allocate canvas image.
1434   */
1435   mosaic_image=AllocateImage((ImageInfo *) NULL);
1436   if (mosaic_image == (Image *) NULL)
1437     return((Image *) NULL);
1438   mosaic_image->columns=page.width;
1439   mosaic_image->rows=page.height;
1440 
1441   /*
1442     Canvas image supports transparency if any subordinate image uses
1443     transparency.
1444   */
1445   matte=MagickTrue;
1446   for (next=image; next != (Image *) NULL; next=next->next)
1447     matte &= next->matte;
1448   mosaic_image->matte=matte;
1449 
1450   /*
1451     Canvas color is copied from background color of first image in
1452     list.  Default canvas color is 'white' but opaque 'black' or
1453     'transparent' is often best for composition.
1454   */
1455   mosaic_image->background_color=image->background_color;
1456   (void) SetImage(mosaic_image,OpaqueOpacity);
1457 
1458   /*
1459     Composite mosaic.
1460   */
1461   scene=0;
1462   for (next=image; next != (Image *) NULL; next=next->next)
1463   {
1464     (void) CompositeImage(mosaic_image,next->compose,next,next->page.x,
1465       next->page.y);
1466     status=MagickMonitorFormatted(scene++,image_list_length,
1467                                   exception,MosaicImageText,image->filename);
1468     if (status == MagickFail)
1469       break;
1470   }
1471   return(mosaic_image);
1472 }
1473 
1474 /*
1475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1476 %                                                                             %
1477 %                                                                             %
1478 %                                                                             %
1479 %   R o l l I m a g e                                                         %
1480 %                                                                             %
1481 %                                                                             %
1482 %                                                                             %
1483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1484 %
1485 %  RollImage() offsets an image as defined by x_offset and y_offset.
1486 %
1487 %  The format of the RollImage method is:
1488 %
1489 %      Image *RollImage(const Image *image,const long x_offset,
1490 %        const long y_offset,ExceptionInfo *exception)
1491 %
1492 %  A description of each parameter follows:
1493 %
1494 %    o image: The image.
1495 %
1496 %    o x_offset: The number of columns to roll in the horizontal direction.
1497 %
1498 %    o y_offset: The number of rows to roll in the vertical direction.
1499 %
1500 %    o exception: Return any errors or warnings in this structure.
1501 %
1502 %
1503 */
RollImage(const Image * image,const long x_offset,const long y_offset,ExceptionInfo * exception)1504 MagickExport Image *RollImage(const Image *image,const long x_offset,
1505                               const long y_offset,ExceptionInfo *exception)
1506 {
1507   Image
1508     *roll_image;
1509 
1510   RectangleInfo
1511     offset;
1512 
1513   /*
1514     Initialize roll image attributes.
1515   */
1516   assert(image != (const Image *) NULL);
1517   assert(image->signature == MagickSignature);
1518   assert(exception != (ExceptionInfo *) NULL);
1519   assert(exception->signature == MagickSignature);
1520   roll_image=CloneImage(image,image->columns,image->rows,True,exception);
1521   if (roll_image == (Image *) NULL)
1522     return((Image *) NULL);
1523   /*
1524     Roll image.
1525   */
1526   offset.x=x_offset;
1527   offset.y=y_offset;
1528   while (offset.x < 0)
1529     offset.x+=image->columns;
1530   while (offset.x >= (long) image->columns)
1531     offset.x-=image->columns;
1532   while (offset.y < 0)
1533     offset.y+=image->rows;
1534   while (offset.y >= (long) image->rows)
1535     offset.y-=image->rows;
1536 
1537   /* Top left quadrant */
1538   (void) CompositeImageRegion(CopyCompositeOp,0,offset.x,offset.y,image,
1539                               image->columns-offset.x,image->rows-offset.y,
1540                               roll_image,0,0,exception);
1541 
1542   /* Top right quadrant */
1543   (void) CompositeImageRegion(CopyCompositeOp,0,image->columns-offset.x,offset.y,image,
1544                               0,image->rows-offset.y,
1545                               roll_image,offset.x,0,exception);
1546 
1547   /* Bottom left quadrant */
1548   (void) CompositeImageRegion(CopyCompositeOp,0,offset.x,image->rows-offset.y,image,
1549                               image->columns-offset.x,0,
1550                               roll_image,0,offset.y,exception);
1551 
1552   /* Bottom right quadrant */
1553   (void) CompositeImageRegion(CopyCompositeOp,0,image->columns-offset.x,image->rows-offset.y,image,
1554                               0,0,
1555                               roll_image,offset.x,offset.y,exception);
1556 
1557   roll_image->is_grayscale=image->is_grayscale;
1558   return(roll_image);
1559 }
1560 
1561 /*
1562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1563 %                                                                             %
1564 %                                                                             %
1565 %                                                                             %
1566 %   S h a v e I m a g e                                                       %
1567 %                                                                             %
1568 %                                                                             %
1569 %                                                                             %
1570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1571 %
1572 %  Method ShaveImage shaves pixels from the image edges.  It allocates the
1573 %  memory necessary for the new Image structure and returns a pointer to the
1574 %  new image.
1575 %
1576 %  The format of the ShaveImage method is:
1577 %
1578 %      Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1579 %        ExceptionInfo *exception)
1580 %
1581 %  A description of each parameter follows:
1582 %
1583 %    o shave_image: Method ShaveImage returns a pointer to the shaved
1584 %      image.  A null image is returned if there is a memory shortage or
1585 %      if the image width or height is zero.
1586 %
1587 %    o image: The image.
1588 %
1589 %    o shave_info: Specifies a pointer to a RectangleInfo which defines the
1590 %      region of the image to shave.
1591 %
1592 %    o exception: Return any errors or warnings in this structure.
1593 %
1594 %
1595 */
ShaveImage(const Image * image,const RectangleInfo * shave_info,ExceptionInfo * exception)1596 MagickExport Image *ShaveImage(const Image *image,
1597   const RectangleInfo *shave_info,ExceptionInfo *exception)
1598 {
1599   RectangleInfo
1600     geometry;
1601 
1602   if (((2*shave_info->width) >= image->columns) ||
1603       ((2*shave_info->height) >= image->rows))
1604     ThrowImageException(OptionError,GeometryDoesNotContainImage,
1605       MagickMsg(ResourceLimitError,UnableToShaveImage));
1606   SetGeometry(image,&geometry);
1607   geometry.width-=2*shave_info->width;
1608   geometry.height-=2*shave_info->height;
1609   geometry.x=(long) shave_info->width;
1610   geometry.y=(long) shave_info->height;
1611   return(CropImage(image,&geometry,exception));
1612 }
1613 
1614 /*
1615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616 %                                                                             %
1617 %                                                                             %
1618 %                                                                             %
1619 %   T r a n s f o r m I m a g e                                               %
1620 %                                                                             %
1621 %                                                                             %
1622 %                                                                             %
1623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624 %
1625 %  TransformImage() is a convenience method that behaves like ResizeImage() or
1626 %  CropImage() but accepts scaling and/or cropping information as a region
1627 %  geometry specification.  If the operation fails, the original image handle
1628 %  is returned.
1629 %
1630 %  The format of the TransformImage method is:
1631 %
1632 %      MagickPassFail TransformImage(Image **image,const char *crop_geometry,
1633 %        const char *image_geometry)
1634 %
1635 %  A description of each parameter follows:
1636 %
1637 %    o image: The image to transform. The resulting transformed image is
1638 %      returned via this parameter.
1639 %
1640 %    o crop_geometry: A crop geometry string.  This geometry defines a
1641 %      subregion of the image to crop.
1642 %
1643 %    o image_geometry: An image geometry string.  This geometry defines the
1644 %      final size of the image.
1645 %
1646 %
1647 */
TransformImage(Image ** image,const char * crop_geometry,const char * image_geometry)1648 MagickExport MagickPassFail TransformImage(Image **image,const char *crop_geometry,
1649                                            const char *image_geometry)
1650 {
1651   Image
1652     *previous,
1653     *resize_image,
1654     *transform_image;
1655 
1656   RectangleInfo
1657     geometry;
1658 
1659   int
1660     flags;
1661 
1662   MagickPassFail
1663     status = MagickPass;
1664 
1665   assert(image != (Image **) NULL);
1666   assert((*image)->signature == MagickSignature);
1667   transform_image=(*image);
1668   if (crop_geometry != (const char *) NULL)
1669     {
1670       Image
1671         *crop_image;
1672 
1673       /*
1674         Crop image to a user specified size.
1675       */
1676       crop_image=(Image *) NULL;
1677       flags=GetImageGeometry(transform_image,crop_geometry,False,&geometry);
1678       if ((geometry.width == 0) || (geometry.height == 0) ||
1679           ((flags & XValue) != 0) || ((flags & YValue) != 0) ||
1680           (flags & PercentValue))
1681         {
1682           crop_image=CropImage(transform_image,&geometry,&(*image)->exception);
1683           if (crop_image == (Image *) NULL)
1684             status = MagickFail;
1685         }
1686       else
1687         if ((transform_image->columns > geometry.width) ||
1688             (transform_image->rows > geometry.height))
1689           {
1690             Image
1691               *next;
1692 
1693             long
1694               x,
1695               y;
1696 
1697             unsigned long
1698               height,
1699               width;
1700 
1701             /*
1702               Crop repeatedly to create uniform subimages.
1703             */
1704             width=geometry.width;
1705             height=geometry.height;
1706             next=(Image *) NULL;
1707             for (y=0; y < (long) transform_image->rows; y+=height)
1708               {
1709                 for (x=0; x < (long) transform_image->columns; x+=width)
1710                   {
1711                     geometry.width=width;
1712                     geometry.height=height;
1713                     geometry.x=x;
1714                     geometry.y=y;
1715                     next=CropImage(transform_image,&geometry,&(*image)->exception);
1716                     if (next == (Image *) NULL)
1717                       break;
1718                     if (crop_image == (Image *) NULL)
1719                       crop_image=next;
1720                     else
1721                       {
1722                         next->previous=crop_image;
1723                         crop_image->next=next;
1724                         crop_image=crop_image->next;
1725                       }
1726                   }
1727                 if (next == (Image *) NULL)
1728                   {
1729                     status=MagickFail;
1730                     break;
1731                   }
1732               }
1733           }
1734       if (crop_image != (Image *) NULL)
1735         {
1736           previous=transform_image->previous;
1737           crop_image->next=transform_image->next;
1738           DestroyImage(transform_image);
1739           transform_image=(Image *) NULL;
1740           while (crop_image->previous != (Image *) NULL)
1741             crop_image=crop_image->previous;
1742           crop_image->previous=previous;
1743           transform_image=crop_image;
1744         }
1745       *image=transform_image;
1746     }
1747   if (image_geometry == (const char *) NULL)
1748     return status;
1749 
1750   /*
1751     Scale image to a user specified size.
1752   */
1753   SetGeometry(transform_image,&geometry);
1754   flags=GetMagickGeometry(image_geometry,&geometry.x,&geometry.y,
1755                           &geometry.width,&geometry.height);
1756   if ((transform_image->columns == geometry.width) &&
1757       (transform_image->rows == geometry.height))
1758     return status;
1759 
1760   /*
1761     Resize image.
1762   */
1763   resize_image=ZoomImage(transform_image,geometry.width,geometry.height,
1764                          &(*image)->exception);
1765   if (resize_image == (Image *) NULL)
1766     {
1767       status=MagickFail;
1768       return status;
1769     }
1770 
1771   ReplaceImageInList(&transform_image,resize_image);
1772   *image=transform_image;
1773   return status;
1774 }
1775