]> granicus.if.org Git - imagemagick/blob - MagickCore/transform.c
0546abc5aa13791dbf8dd11d682eed929f303353
[imagemagick] / MagickCore / transform.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %       TTTTT  RRRR    AAA   N   N  SSSSS  FFFFF   OOO   RRRR   M   M         %
7 %         T    R   R  A   A  NN  N  SS     F      O   O  R   R  MM MM         %
8 %         T    RRRR   AAAAA  N N N   SSS   FFF    O   O  RRRR   M M M         %
9 %         T    R R    A   A  N  NN     SS  F      O   O  R R    M   M         %
10 %         T    R  R   A   A  N   N  SSSSS  F       OOO   R  R   M   M         %
11 %                                                                             %
12 %                                                                             %
13 %                    MagickCore Image Transform Methods                       %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/attribute.h"
44 #include "MagickCore/cache.h"
45 #include "MagickCore/cache-view.h"
46 #include "MagickCore/color.h"
47 #include "MagickCore/color-private.h"
48 #include "MagickCore/colorspace-private.h"
49 #include "MagickCore/composite.h"
50 #include "MagickCore/draw.h"
51 #include "MagickCore/effect.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/geometry.h"
55 #include "MagickCore/image.h"
56 #include "MagickCore/memory_.h"
57 #include "MagickCore/layer.h"
58 #include "MagickCore/list.h"
59 #include "MagickCore/monitor.h"
60 #include "MagickCore/monitor-private.h"
61 #include "MagickCore/pixel-accessor.h"
62 #include "MagickCore/resource_.h"
63 #include "MagickCore/resize.h"
64 #include "MagickCore/statistic.h"
65 #include "MagickCore/string_.h"
66 #include "MagickCore/thread-private.h"
67 #include "MagickCore/transform.h"
68 \f
69 /*
70 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71 %                                                                             %
72 %                                                                             %
73 %                                                                             %
74 %   C h o p I m a g e                                                         %
75 %                                                                             %
76 %                                                                             %
77 %                                                                             %
78 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 %
80 %  ChopImage() removes a region of an image and collapses the image to occupy
81 %  the removed portion.
82 %
83 %  The format of the ChopImage method is:
84 %
85 %      Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
86 %        ExceptionInfo *exception)
87 %
88 %  A description of each parameter follows:
89 %
90 %    o image: the image.
91 %
92 %    o chop_info: Define the region of the image to chop.
93 %
94 %    o exception: return any errors or warnings in this structure.
95 %
96 */
97 MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
98   ExceptionInfo *exception)
99 {
100 #define ChopImageTag  "Chop/Image"
101
102   CacheView
103     *chop_view,
104     *image_view;
105
106   Image
107     *chop_image;
108
109   MagickBooleanType
110     status;
111
112   MagickOffsetType
113     progress;
114
115   RectangleInfo
116     extent;
117
118   ssize_t
119     y;
120
121   /*
122     Check chop geometry.
123   */
124   assert(image != (const Image *) NULL);
125   assert(image->signature == MagickSignature);
126   if (image->debug != MagickFalse)
127     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
128   assert(exception != (ExceptionInfo *) NULL);
129   assert(exception->signature == MagickSignature);
130   assert(chop_info != (RectangleInfo *) NULL);
131   if (((chop_info->x+(ssize_t) chop_info->width) < 0) ||
132       ((chop_info->y+(ssize_t) chop_info->height) < 0) ||
133       (chop_info->x > (ssize_t) image->columns) ||
134       (chop_info->y > (ssize_t) image->rows))
135     ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
136   extent=(*chop_info);
137   if ((extent.x+(ssize_t) extent.width) > (ssize_t) image->columns)
138     extent.width=(size_t) ((ssize_t) image->columns-extent.x);
139   if ((extent.y+(ssize_t) extent.height) > (ssize_t) image->rows)
140     extent.height=(size_t) ((ssize_t) image->rows-extent.y);
141   if (extent.x < 0)
142     {
143       extent.width-=(size_t) (-extent.x);
144       extent.x=0;
145     }
146   if (extent.y < 0)
147     {
148       extent.height-=(size_t) (-extent.y);
149       extent.y=0;
150     }
151   chop_image=CloneImage(image,image->columns-extent.width,image->rows-
152     extent.height,MagickTrue,exception);
153   if (chop_image == (Image *) NULL)
154     return((Image *) NULL);
155   /*
156     Extract chop image.
157   */
158   status=MagickTrue;
159   progress=0;
160   image_view=AcquireCacheView(image);
161   chop_view=AcquireCacheView(chop_image);
162 #if defined(MAGICKCORE_OPENMP_SUPPORT)
163   #pragma omp parallel for schedule(static) shared(progress,status)
164 #endif
165   for (y=0; y < (ssize_t) extent.y; y++)
166   {
167     register const Quantum
168       *restrict p;
169
170     register ssize_t
171       x;
172
173     register Quantum
174       *restrict q;
175
176     if (status == MagickFalse)
177       continue;
178     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
179     q=QueueCacheViewAuthenticPixels(chop_view,0,y,chop_image->columns,1,
180       exception);
181     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
182       {
183         status=MagickFalse;
184         continue;
185       }
186     for (x=0; x < (ssize_t) image->columns; x++)
187     {
188       if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
189         {
190           register ssize_t
191             i;
192
193           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
194           {
195             PixelChannel
196               channel;
197
198             PixelTrait
199               chop_traits,
200               traits;
201
202             channel=GetPixelChannelMapChannel(image,i);
203             traits=GetPixelChannelMapTraits(image,channel);
204             chop_traits=GetPixelChannelMapTraits(chop_image,channel);
205             if ((traits == UndefinedPixelTrait) ||
206                 (chop_traits == UndefinedPixelTrait))
207               continue;
208             SetPixelChannel(chop_image,channel,p[i],q);
209           }
210           q+=GetPixelChannels(chop_image);
211         }
212       p+=GetPixelChannels(image);
213     }
214     if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
215       status=MagickFalse;
216     if (image->progress_monitor != (MagickProgressMonitor) NULL)
217       {
218         MagickBooleanType
219           proceed;
220
221 #if defined(MAGICKCORE_OPENMP_SUPPORT)
222         #pragma omp critical (MagickCore_ChopImage)
223 #endif
224         proceed=SetImageProgress(image,ChopImageTag,progress++,image->rows);
225         if (proceed == MagickFalse)
226           status=MagickFalse;
227       }
228   }
229   /*
230     Extract chop image.
231   */
232 #if defined(MAGICKCORE_OPENMP_SUPPORT)
233   #pragma omp parallel for schedule(static) shared(progress,status)
234 #endif
235   for (y=0; y < (ssize_t) (image->rows-(extent.y+extent.height)); y++)
236   {
237     register const Quantum
238       *restrict p;
239
240     register ssize_t
241       x;
242
243     register Quantum
244       *restrict q;
245
246     if (status == MagickFalse)
247       continue;
248     p=GetCacheViewVirtualPixels(image_view,0,extent.y+extent.height+y,
249       image->columns,1,exception);
250     q=QueueCacheViewAuthenticPixels(chop_view,0,extent.y+y,chop_image->columns,
251       1,exception);
252     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
253       {
254         status=MagickFalse;
255         continue;
256       }
257     for (x=0; x < (ssize_t) image->columns; x++)
258     {
259       if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
260         {
261           register ssize_t
262             i;
263
264           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
265           {
266             PixelChannel
267               channel;
268
269             PixelTrait
270               chop_traits,
271               traits;
272
273             channel=GetPixelChannelMapChannel(image,i);
274             traits=GetPixelChannelMapTraits(image,channel);
275             chop_traits=GetPixelChannelMapTraits(chop_image,channel);
276             if ((traits == UndefinedPixelTrait) ||
277                 (chop_traits == UndefinedPixelTrait))
278               continue;
279             SetPixelChannel(chop_image,channel,p[i],q);
280           }
281           p+=GetPixelChannels(chop_image);
282           q+=GetPixelChannels(chop_image);
283         }
284       p+=GetPixelChannels(image);
285     }
286     if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
287       status=MagickFalse;
288     if (image->progress_monitor != (MagickProgressMonitor) NULL)
289       {
290         MagickBooleanType
291           proceed;
292
293 #if defined(MAGICKCORE_OPENMP_SUPPORT)
294         #pragma omp critical (MagickCore_ChopImage)
295 #endif
296         proceed=SetImageProgress(image,ChopImageTag,progress++,image->rows);
297         if (proceed == MagickFalse)
298           status=MagickFalse;
299       }
300   }
301   chop_view=DestroyCacheView(chop_view);
302   image_view=DestroyCacheView(image_view);
303   chop_image->type=image->type;
304   return(chop_image);
305 }
306 \f
307 /*
308 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
309 %                                                                             %
310 %                                                                             %
311 %                                                                             %
312 +     C o n s o l i d a t e C M Y K I m a g e                                 %
313 %                                                                             %
314 %                                                                             %
315 %                                                                             %
316 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
317 %
318 %  ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
319 %  single image.
320 %
321 %  The format of the ConsolidateCMYKImage method is:
322 %
323 %      Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
324 %
325 %  A description of each parameter follows:
326 %
327 %    o image: the image sequence.
328 %
329 %    o exception: return any errors or warnings in this structure.
330 %
331 */
332 MagickExport Image *ConsolidateCMYKImages(const Image *images,
333   ExceptionInfo *exception)
334 {
335   CacheView
336     *cmyk_view,
337     *image_view;
338
339   Image
340     *cmyk_image,
341     *cmyk_images;
342
343   register ssize_t
344     j;
345
346   ssize_t
347     y;
348
349   /*
350     Consolidate separate C, M, Y, and K planes into a single image.
351   */
352   assert(images != (Image *) NULL);
353   assert(images->signature == MagickSignature);
354   if (images->debug != MagickFalse)
355     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
356   assert(exception != (ExceptionInfo *) NULL);
357   assert(exception->signature == MagickSignature);
358   cmyk_images=NewImageList();
359   for (j=0; j < (ssize_t) GetImageListLength(images); j+=4)
360   {
361     register ssize_t
362       i;
363
364     cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
365       exception);
366     if (cmyk_image == (Image *) NULL)
367       break;
368     if (SetImageStorageClass(cmyk_image,DirectClass,exception) == MagickFalse)
369       break;
370     (void) SetImageColorspace(cmyk_image,CMYKColorspace,exception);
371     for (i=0; i < 4; i++)
372     {
373       image_view=AcquireCacheView(images);
374       cmyk_view=AcquireCacheView(cmyk_image);
375       for (y=0; y < (ssize_t) images->rows; y++)
376       {
377         register const Quantum
378           *restrict p;
379
380         register ssize_t
381           x;
382
383         register Quantum
384           *restrict q;
385
386         p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
387         q=QueueCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
388           exception);
389         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
390           break;
391         for (x=0; x < (ssize_t) images->columns; x++)
392         {
393           Quantum
394             pixel;
395
396           pixel=QuantumRange-GetPixelIntensity(images,p);
397           switch (i)
398           {
399             case 0: SetPixelCyan(cmyk_image,pixel,q);  break;
400             case 1: SetPixelMagenta(cmyk_image,pixel,q);  break;
401             case 2: SetPixelYellow(cmyk_image,pixel,q);  break;
402             case 3: SetPixelBlack(cmyk_image,pixel,q);  break;
403             default: break;
404           }
405           p+=GetPixelChannels(images);
406           q+=GetPixelChannels(cmyk_image);
407         }
408         if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
409           break;
410       }
411       cmyk_view=DestroyCacheView(cmyk_view);
412       image_view=DestroyCacheView(image_view);
413       images=GetNextImageInList(images);
414       if (images == (Image *) NULL)
415         break;
416     }
417     AppendImageToList(&cmyk_images,cmyk_image);
418   }
419   return(cmyk_images);
420 }
421 \f
422 /*
423 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
424 %                                                                             %
425 %                                                                             %
426 %                                                                             %
427 %   C r o p I m a g e                                                         %
428 %                                                                             %
429 %                                                                             %
430 %                                                                             %
431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
432 %
433 %  CropImage() extracts a region of the image starting at the offset defined
434 %  by geometry.  Region must be fully defined, and no special handling of
435 %  geometry flags is performed.
436 %
437 %  The format of the CropImage method is:
438 %
439 %      Image *CropImage(const Image *image,const RectangleInfo *geometry,
440 %        ExceptionInfo *exception)
441 %
442 %  A description of each parameter follows:
443 %
444 %    o image: the image.
445 %
446 %    o geometry: Define the region of the image to crop with members
447 %      x, y, width, and height.
448 %
449 %    o exception: return any errors or warnings in this structure.
450 %
451 */
452 MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
453   ExceptionInfo *exception)
454 {
455 #define CropImageTag  "Crop/Image"
456
457   CacheView
458     *crop_view,
459     *image_view;
460
461   Image
462     *crop_image;
463
464   MagickBooleanType
465     status;
466
467   MagickOffsetType
468     progress;
469
470   OffsetInfo
471     offset;
472
473   RectangleInfo
474     bounding_box,
475     page;
476
477   ssize_t
478     y;
479
480   /*
481     Check crop geometry.
482   */
483   assert(image != (const Image *) NULL);
484   assert(image->signature == MagickSignature);
485   if (image->debug != MagickFalse)
486     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
487   assert(geometry != (const RectangleInfo *) NULL);
488   assert(exception != (ExceptionInfo *) NULL);
489   assert(exception->signature == MagickSignature);
490   bounding_box=image->page;
491   if ((bounding_box.width == 0) || (bounding_box.height == 0))
492     {
493       bounding_box.width=image->columns;
494       bounding_box.height=image->rows;
495     }
496   page=(*geometry);
497   if (page.width == 0)
498     page.width=bounding_box.width;
499   if (page.height == 0)
500     page.height=bounding_box.height;
501   if (((bounding_box.x-page.x) >= (ssize_t) page.width) ||
502       ((bounding_box.y-page.y) >= (ssize_t) page.height) ||
503       ((page.x-bounding_box.x) > (ssize_t) image->columns) ||
504       ((page.y-bounding_box.y) > (ssize_t) image->rows))
505     {
506       /*
507         Crop is not within virtual canvas, return 1 pixel transparent image.
508       */
509       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
510         "GeometryDoesNotContainImage","`%s'",image->filename);
511       crop_image=CloneImage(image,1,1,MagickTrue,exception);
512       if (crop_image == (Image *) NULL)
513         return((Image *) NULL);
514       crop_image->background_color.alpha=(Quantum) TransparentAlpha;
515       (void) SetImageBackgroundColor(crop_image,exception);
516       crop_image->page=bounding_box;
517       crop_image->page.x=(-1);
518       crop_image->page.y=(-1);
519       if (crop_image->dispose == BackgroundDispose)
520         crop_image->dispose=NoneDispose;
521       return(crop_image);
522     }
523   if ((page.x < 0) && (bounding_box.x >= 0))
524     {
525       page.width+=page.x-bounding_box.x;
526       page.x=0;
527     }
528   else
529     {
530       page.width-=bounding_box.x-page.x;
531       page.x-=bounding_box.x;
532       if (page.x < 0)
533         page.x=0;
534     }
535   if ((page.y < 0) && (bounding_box.y >= 0))
536     {
537       page.height+=page.y-bounding_box.y;
538       page.y=0;
539     }
540   else
541     {
542       page.height-=bounding_box.y-page.y;
543       page.y-=bounding_box.y;
544       if (page.y < 0)
545         page.y=0;
546     }
547   if ((size_t) (page.x+page.width) > image->columns)
548     page.width=image->columns-page.x;
549   if ((geometry->width != 0) && (page.width > geometry->width))
550     page.width=geometry->width;
551   if ((size_t) (page.y+page.height) > image->rows)
552     page.height=image->rows-page.y;
553   if ((geometry->height != 0) && (page.height > geometry->height))
554     page.height=geometry->height;
555   bounding_box.x+=page.x;
556   bounding_box.y+=page.y;
557   if ((page.width == 0) || (page.height == 0))
558     {
559       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
560         "GeometryDoesNotContainImage","`%s'",image->filename);
561       return((Image *) NULL);
562     }
563   /*
564     Initialize crop image attributes.
565   */
566   crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
567   if (crop_image == (Image *) NULL)
568     return((Image *) NULL);
569   crop_image->page.width=image->page.width;
570   crop_image->page.height=image->page.height;
571   offset.x=(ssize_t) (bounding_box.x+bounding_box.width);
572   offset.y=(ssize_t) (bounding_box.y+bounding_box.height);
573   if ((offset.x > (ssize_t) image->page.width) ||
574       (offset.y > (ssize_t) image->page.height))
575     {
576       crop_image->page.width=bounding_box.width;
577       crop_image->page.height=bounding_box.height;
578     }
579   crop_image->page.x=bounding_box.x;
580   crop_image->page.y=bounding_box.y;
581   /*
582     Crop image.
583   */
584   status=MagickTrue;
585   progress=0;
586   image_view=AcquireCacheView(image);
587   crop_view=AcquireCacheView(crop_image);
588 #if defined(MAGICKCORE_OPENMP_SUPPORT)
589   #pragma omp parallel for schedule(static) shared(progress,status)
590 #endif
591   for (y=0; y < (ssize_t) crop_image->rows; y++)
592   {
593     register const Quantum
594       *restrict p;
595
596     register Quantum
597       *restrict q;
598
599     register ssize_t
600       x;
601
602     if (status == MagickFalse)
603       continue;
604     p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
605       1,exception);
606     q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
607       exception);
608     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
609       {
610         status=MagickFalse;
611         continue;
612       }
613     for (x=0; x < (ssize_t) crop_image->columns; x++)
614     {
615       register ssize_t
616         i;
617
618       if (GetPixelMask(image,p) != 0)
619         {
620           p+=GetPixelChannels(image);
621           q+=GetPixelChannels(crop_image);
622           continue;
623         }
624       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
625       {
626         PixelChannel
627           channel;
628
629         PixelTrait
630           crop_traits,
631           traits;
632
633         channel=GetPixelChannelMapChannel(image,i);
634         traits=GetPixelChannelMapTraits(image,channel);
635         crop_traits=GetPixelChannelMapTraits(crop_image,channel);
636         if ((traits == UndefinedPixelTrait) ||
637             (crop_traits == UndefinedPixelTrait))
638           continue;
639         SetPixelChannel(crop_image,channel,p[i],q);
640       }
641       p+=GetPixelChannels(image);
642       q+=GetPixelChannels(crop_image);
643     }
644     if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
645       status=MagickFalse;
646     if (image->progress_monitor != (MagickProgressMonitor) NULL)
647       {
648         MagickBooleanType
649           proceed;
650
651 #if defined(MAGICKCORE_OPENMP_SUPPORT)
652         #pragma omp critical (MagickCore_CropImage)
653 #endif
654         proceed=SetImageProgress(image,CropImageTag,progress++,image->rows);
655         if (proceed == MagickFalse)
656           status=MagickFalse;
657       }
658   }
659   crop_view=DestroyCacheView(crop_view);
660   image_view=DestroyCacheView(image_view);
661   crop_image->type=image->type;
662   if (status == MagickFalse)
663     crop_image=DestroyImage(crop_image);
664   return(crop_image);
665 }
666 \f
667 /*
668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669 %                                                                             %
670 %                                                                             %
671 %                                                                             %
672 %   C r o p I m a g e T o T i l e s                                           %
673 %                                                                             %
674 %                                                                             %
675 %                                                                             %
676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
677 %
678 %  CropImageToTiles() crops a single image, into a possible list of tiles.
679 %  This may include a single sub-region of the image.  This basically applies
680 %  all the normal geometry flags for Crop.
681 %
682 %      Image *CropImageToTiles(const Image *image,
683 %         const RectangleInfo *crop_geometry, ExceptionInfo *exception)
684 %
685 %  A description of each parameter follows:
686 %
687 %    o image: the image The transformed image is returned as this parameter.
688 %
689 %    o crop_geometry: A crop geometry string.
690 %
691 %    o exception: return any errors or warnings in this structure.
692 %
693 */
694
695 static inline ssize_t MagickRound(MagickRealType x)
696 {
697   /*
698     Round the fraction to nearest integer.
699   */
700   if (x >= 0.0)
701     return((ssize_t) (x+0.5));
702   return((ssize_t) (x-0.5));
703 }
704
705 MagickExport Image *CropImageToTiles(const Image *image,
706   const char *crop_geometry,ExceptionInfo *exception)
707 {
708   Image
709     *next,
710     *crop_image;
711
712   MagickStatusType
713     flags;
714
715   RectangleInfo
716     geometry;
717
718   assert(image != (Image *) NULL);
719   assert(image->signature == MagickSignature);
720   if (image->debug != MagickFalse)
721     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
722   crop_image=NewImageList();
723   next=NewImageList();
724   flags=ParseGravityGeometry(image,crop_geometry,&geometry,exception);
725   if ((flags & AreaValue) != 0)
726     {
727       PointInfo
728         delta,
729         offset;
730
731       RectangleInfo
732         crop;
733
734       size_t
735         height,
736         width;
737
738       /*
739         Crop into NxM tiles (@ flag).
740       */
741       width=image->columns;
742       height=image->rows;
743       if (geometry.width == 0)
744         geometry.width=1;
745       if (geometry.height == 0)
746         geometry.height=1;
747       if ((flags & AspectValue) == 0)
748         {
749           width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
750           height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
751         }
752       else
753         {
754           width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
755           height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
756         }
757       delta.x=(double) width/geometry.width;
758       delta.y=(double) height/geometry.height;
759       if (delta.x < 1.0)
760         delta.x=1.0;
761       if (delta.y < 1.0)
762         delta.y=1.0;
763       for (offset.y=0; offset.y < (double) height; )
764       {
765         if ((flags & AspectValue) == 0)
766           {
767             crop.y=(ssize_t) MagickRound((MagickRealType) (offset.y-
768               (geometry.y > 0 ? 0 : geometry.y)));
769             offset.y+=delta.y;   /* increment now to find width */
770             crop.height=(size_t) MagickRound((MagickRealType) (offset.y+
771               (geometry.y < 0 ? 0 : geometry.y)));
772           }
773         else
774           {
775             crop.y=(ssize_t) MagickRound((MagickRealType) (offset.y-
776               (geometry.y > 0 ? geometry.y : 0)));
777             offset.y+=delta.y;  /* increment now to find width */
778             crop.height=(size_t) MagickRound((MagickRealType)
779               (offset.y+(geometry.y < -1 ? geometry.y : 0)));
780           }
781         crop.height-=crop.y;
782         crop.y+=image->page.y;
783         for (offset.x=0; offset.x < (double) width; )
784         {
785           if ((flags & AspectValue) == 0)
786             {
787               crop.x=(ssize_t) MagickRound((MagickRealType) (offset.x-
788                 (geometry.x > 0 ? 0 : geometry.x)));
789               offset.x+=delta.x;  /* increment now to find height */
790               crop.width=(size_t) MagickRound((MagickRealType) (offset.x+
791                 (geometry.x < 0 ? 0 : geometry.x)));
792             }
793           else
794             {
795               crop.x=(ssize_t) MagickRound((MagickRealType) (offset.x-
796                 (geometry.x > 0 ? geometry.x : 0)));
797               offset.x+=delta.x;  /* increment now to find height */
798               crop.width=(size_t) MagickRound((MagickRealType) (offset.x+
799                 (geometry.x < 0 ? geometry.x : 0)));
800             }
801           crop.width-=crop.x;
802           crop.x+=image->page.x;
803           next=CropImage(image,&crop,exception);
804           if (next == (Image *) NULL)
805             break;
806           AppendImageToList(&crop_image,next);
807         }
808         if (next == (Image *) NULL)
809           break;
810       }
811       ClearMagickException(exception);
812       return(crop_image);
813     }
814   if (((geometry.width == 0) && (geometry.height == 0)) ||
815       ((flags & XValue) != 0) || ((flags & YValue) != 0))
816     {
817       /*
818         Crop a single region at +X+Y.
819       */
820       crop_image=CropImage(image,&geometry,exception);
821       if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
822         {
823           crop_image->page.width=geometry.width;
824           crop_image->page.height=geometry.height;
825           crop_image->page.x-=geometry.x;
826           crop_image->page.y-=geometry.y;
827         }
828       return(crop_image);
829     }
830   if ((image->columns > geometry.width) || (image->rows > geometry.height))
831     {
832       RectangleInfo
833         page;
834
835       size_t
836         height,
837         width;
838
839       ssize_t
840         x,
841         y;
842
843       /*
844         Crop into tiles of fixed size WxH.
845       */
846       page=image->page;
847       if (page.width == 0)
848         page.width=image->columns;
849       if (page.height == 0)
850         page.height=image->rows;
851       width=geometry.width;
852       if (width == 0)
853         width=page.width;
854       height=geometry.height;
855       if (height == 0)
856         height=page.height;
857       next=NewImageList();
858       for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
859       {
860         for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
861         {
862           geometry.width=width;
863           geometry.height=height;
864           geometry.x=x;
865           geometry.y=y;
866           next=CropImage(image,&geometry,exception);
867           if (next == (Image *) NULL)
868             break;
869           AppendImageToList(&crop_image,next);
870         }
871         if (next == (Image *) NULL)
872           break;
873       }
874       return(crop_image);
875     }
876   return(CloneImage(image,0,0,MagickTrue,exception));
877 }
878 \f
879 /*
880 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
881 %                                                                             %
882 %                                                                             %
883 %                                                                             %
884 %   E x c e r p t I m a g e                                                   %
885 %                                                                             %
886 %                                                                             %
887 %                                                                             %
888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
889 %
890 %  ExcerptImage() returns a excerpt of the image as defined by the geometry.
891 %
892 %  The format of the ExcerptImage method is:
893 %
894 %      Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
895 %        ExceptionInfo *exception)
896 %
897 %  A description of each parameter follows:
898 %
899 %    o image: the image.
900 %
901 %    o geometry: Define the region of the image to extend with members
902 %      x, y, width, and height.
903 %
904 %    o exception: return any errors or warnings in this structure.
905 %
906 */
907 MagickExport Image *ExcerptImage(const Image *image,
908   const RectangleInfo *geometry,ExceptionInfo *exception)
909 {
910 #define ExcerptImageTag  "Excerpt/Image"
911
912   CacheView
913     *excerpt_view,
914     *image_view;
915
916   Image
917     *excerpt_image;
918
919   MagickBooleanType
920     status;
921
922   MagickOffsetType
923     progress;
924
925   ssize_t
926     y;
927
928   /*
929     Allocate excerpt image.
930   */
931   assert(image != (const Image *) NULL);
932   assert(image->signature == MagickSignature);
933   if (image->debug != MagickFalse)
934     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
935   assert(geometry != (const RectangleInfo *) NULL);
936   assert(exception != (ExceptionInfo *) NULL);
937   assert(exception->signature == MagickSignature);
938   excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
939     exception);
940   if (excerpt_image == (Image *) NULL)
941     return((Image *) NULL);
942   /*
943     Excerpt each row.
944   */
945   status=MagickTrue;
946   progress=0;
947   image_view=AcquireCacheView(image);
948   excerpt_view=AcquireCacheView(excerpt_image);
949 #if defined(MAGICKCORE_OPENMP_SUPPORT)
950   #pragma omp parallel for schedule(static,4) shared(progress,status)
951 #endif
952   for (y=0; y < (ssize_t) excerpt_image->rows; y++)
953   {
954     register const Quantum
955       *restrict p;
956
957     register Quantum
958       *restrict q;
959
960     register ssize_t
961       x;
962
963     if (status == MagickFalse)
964       continue;
965     p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
966       geometry->width,1,exception);
967     q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
968       exception);
969     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
970       {
971         status=MagickFalse;
972         continue;
973       }
974     for (x=0; x < (ssize_t) excerpt_image->columns; x++)
975     {
976       register ssize_t
977         i;
978
979       if (GetPixelMask(image,p) != 0)
980         {
981           p+=GetPixelChannels(image);
982           q+=GetPixelChannels(excerpt_image);
983           continue;
984         }
985       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
986       {
987         PixelChannel
988           channel;
989
990         PixelTrait
991           excerpt_traits,
992           traits;
993
994         channel=GetPixelChannelMapChannel(image,i);
995         traits=GetPixelChannelMapTraits(image,channel);
996         excerpt_traits=GetPixelChannelMapTraits(excerpt_image,channel);
997         if ((traits == UndefinedPixelTrait) ||
998             (excerpt_traits == UndefinedPixelTrait))
999           continue;
1000         SetPixelChannel(excerpt_image,channel,p[i],q);
1001       }
1002       p+=GetPixelChannels(image);
1003       q+=GetPixelChannels(excerpt_image);
1004     }
1005     if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
1006       status=MagickFalse;
1007     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1008       {
1009         MagickBooleanType
1010           proceed;
1011
1012 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1013         #pragma omp critical (MagickCore_ExcerptImage)
1014 #endif
1015         proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows);
1016         if (proceed == MagickFalse)
1017           status=MagickFalse;
1018       }
1019   }
1020   excerpt_view=DestroyCacheView(excerpt_view);
1021   image_view=DestroyCacheView(image_view);
1022   excerpt_image->type=image->type;
1023   if (status == MagickFalse)
1024     excerpt_image=DestroyImage(excerpt_image);
1025   return(excerpt_image);
1026 }
1027 \f
1028 /*
1029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1030 %                                                                             %
1031 %                                                                             %
1032 %                                                                             %
1033 %   E x t e n t I m a g e                                                     %
1034 %                                                                             %
1035 %                                                                             %
1036 %                                                                             %
1037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1038 %
1039 %  ExtentImage() extends the image as defined by the geometry, gravity, and
1040 %  image background color.  Set the (x,y) offset of the geometry to move the
1041 %  original image relative to the extended image.
1042 %
1043 %  The format of the ExtentImage method is:
1044 %
1045 %      Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
1046 %        ExceptionInfo *exception)
1047 %
1048 %  A description of each parameter follows:
1049 %
1050 %    o image: the image.
1051 %
1052 %    o geometry: Define the region of the image to extend with members
1053 %      x, y, width, and height.
1054 %
1055 %    o exception: return any errors or warnings in this structure.
1056 %
1057 */
1058 MagickExport Image *ExtentImage(const Image *image,
1059   const RectangleInfo *geometry,ExceptionInfo *exception)
1060 {
1061   Image
1062     *extent_image;
1063
1064   /*
1065     Allocate extent image.
1066   */
1067   assert(image != (const Image *) NULL);
1068   assert(image->signature == MagickSignature);
1069   if (image->debug != MagickFalse)
1070     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1071   assert(geometry != (const RectangleInfo *) NULL);
1072   assert(exception != (ExceptionInfo *) NULL);
1073   assert(exception->signature == MagickSignature);
1074   extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1075     exception);
1076   if (extent_image == (Image *) NULL)
1077     return((Image *) NULL);
1078   if (SetImageStorageClass(extent_image,DirectClass,exception) == MagickFalse)
1079     {
1080       extent_image=DestroyImage(extent_image);
1081       return((Image *) NULL);
1082     }
1083   if (extent_image->background_color.alpha != OpaqueAlpha)
1084     extent_image->matte=MagickTrue;
1085   (void) SetImageBackgroundColor(extent_image,exception);
1086   (void) CompositeImage(extent_image,image,image->compose,MagickFalse,
1087     -geometry->x,-geometry->y,exception);
1088   return(extent_image);
1089 }
1090 \f
1091 /*
1092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1093 %                                                                             %
1094 %                                                                             %
1095 %                                                                             %
1096 %   F l i p I m a g e                                                         %
1097 %                                                                             %
1098 %                                                                             %
1099 %                                                                             %
1100 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1101 %
1102 %  FlipImage() creates a vertical mirror image by reflecting the pixels
1103 %  around the central x-axis.
1104 %
1105 %  The format of the FlipImage method is:
1106 %
1107 %      Image *FlipImage(const Image *image,ExceptionInfo *exception)
1108 %
1109 %  A description of each parameter follows:
1110 %
1111 %    o image: the image.
1112 %
1113 %    o exception: return any errors or warnings in this structure.
1114 %
1115 */
1116 MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1117 {
1118 #define FlipImageTag  "Flip/Image"
1119
1120   CacheView
1121     *flip_view,
1122     *image_view;
1123
1124   Image
1125     *flip_image;
1126
1127   MagickBooleanType
1128     status;
1129
1130   MagickOffsetType
1131     progress;
1132
1133   RectangleInfo
1134     page;
1135
1136   ssize_t
1137     y;
1138
1139   assert(image != (const Image *) NULL);
1140   assert(image->signature == MagickSignature);
1141   if (image->debug != MagickFalse)
1142     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1143   assert(exception != (ExceptionInfo *) NULL);
1144   assert(exception->signature == MagickSignature);
1145   flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1146   if (flip_image == (Image *) NULL)
1147     return((Image *) NULL);
1148   /*
1149     Flip image.
1150   */
1151   status=MagickTrue;
1152   progress=0;
1153   page=image->page;
1154   image_view=AcquireCacheView(image);
1155   flip_view=AcquireCacheView(flip_image);
1156 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1157   #pragma omp parallel for schedule(static) shared(progress,status)
1158 #endif
1159   for (y=0; y < (ssize_t) flip_image->rows; y++)
1160   {
1161     register const Quantum
1162       *restrict p;
1163
1164     register Quantum
1165       *restrict q;
1166
1167     register ssize_t
1168       x;
1169
1170     if (status == MagickFalse)
1171       continue;
1172     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1173     q=QueueCacheViewAuthenticPixels(flip_view,0,(ssize_t) (flip_image->rows-y-
1174       1),flip_image->columns,1,exception);
1175     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1176       {
1177         status=MagickFalse;
1178         continue;
1179       }
1180     for (x=0; x < (ssize_t) flip_image->columns; x++)
1181     {
1182       register ssize_t
1183         i;
1184
1185       if (GetPixelMask(image,p) != 0)
1186         {
1187           p+=GetPixelChannels(image);
1188           q+=GetPixelChannels(flip_image);
1189           continue;
1190         }
1191       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1192       {
1193         PixelChannel
1194           channel;
1195
1196         PixelTrait
1197           flip_traits,
1198           traits;
1199
1200         channel=GetPixelChannelMapChannel(image,i);
1201         traits=GetPixelChannelMapTraits(image,channel);
1202         flip_traits=GetPixelChannelMapTraits(flip_image,channel);
1203         if ((traits == UndefinedPixelTrait) ||
1204             (flip_traits == UndefinedPixelTrait))
1205           continue;
1206         SetPixelChannel(flip_image,channel,p[i],q);
1207       }
1208       p+=GetPixelChannels(image);
1209       q+=GetPixelChannels(flip_image);
1210     }
1211     if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
1212       status=MagickFalse;
1213     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1214       {
1215         MagickBooleanType
1216           proceed;
1217
1218 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1219         #pragma omp critical (MagickCore_FlipImage)
1220 #endif
1221         proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
1222         if (proceed == MagickFalse)
1223           status=MagickFalse;
1224       }
1225   }
1226   flip_view=DestroyCacheView(flip_view);
1227   image_view=DestroyCacheView(image_view);
1228   flip_image->type=image->type;
1229   if (page.height != 0)
1230     page.y=(ssize_t) (page.height-flip_image->rows-page.y);
1231   flip_image->page=page;
1232   if (status == MagickFalse)
1233     flip_image=DestroyImage(flip_image);
1234   return(flip_image);
1235 }
1236 \f
1237 /*
1238 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1239 %                                                                             %
1240 %                                                                             %
1241 %                                                                             %
1242 %   F l o p I m a g e                                                         %
1243 %                                                                             %
1244 %                                                                             %
1245 %                                                                             %
1246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1247 %
1248 %  FlopImage() creates a horizontal mirror image by reflecting the pixels
1249 %  around the central y-axis.
1250 %
1251 %  The format of the FlopImage method is:
1252 %
1253 %      Image *FlopImage(const Image *image,ExceptionInfo *exception)
1254 %
1255 %  A description of each parameter follows:
1256 %
1257 %    o image: the image.
1258 %
1259 %    o exception: return any errors or warnings in this structure.
1260 %
1261 */
1262 MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1263 {
1264 #define FlopImageTag  "Flop/Image"
1265
1266   CacheView
1267     *flop_view,
1268     *image_view;
1269
1270   Image
1271     *flop_image;
1272
1273   MagickBooleanType
1274     status;
1275
1276   MagickOffsetType
1277     progress;
1278
1279   RectangleInfo
1280     page;
1281
1282   ssize_t
1283     y;
1284
1285   assert(image != (const Image *) NULL);
1286   assert(image->signature == MagickSignature);
1287   if (image->debug != MagickFalse)
1288     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1289   assert(exception != (ExceptionInfo *) NULL);
1290   assert(exception->signature == MagickSignature);
1291   flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1292   if (flop_image == (Image *) NULL)
1293     return((Image *) NULL);
1294   /*
1295     Flop each row.
1296   */
1297   status=MagickTrue;
1298   progress=0;
1299   page=image->page;
1300   image_view=AcquireCacheView(image);
1301   flop_view=AcquireCacheView(flop_image);
1302 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1303   #pragma omp parallel for schedule(static) shared(progress,status)
1304 #endif
1305   for (y=0; y < (ssize_t) flop_image->rows; y++)
1306   {
1307     register const Quantum
1308       *restrict p;
1309
1310     register ssize_t
1311       x;
1312
1313     register Quantum
1314       *restrict q;
1315
1316     if (status == MagickFalse)
1317       continue;
1318     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1319     q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1320       exception);
1321     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1322       {
1323         status=MagickFalse;
1324         continue;
1325       }
1326     q+=GetPixelChannels(flop_image)*flop_image->columns;
1327     for (x=0; x < (ssize_t) flop_image->columns; x++)
1328     {
1329       register ssize_t
1330         i;
1331
1332       q-=GetPixelChannels(flop_image);
1333       if (GetPixelMask(image,p) != 0)
1334         {
1335           p+=GetPixelChannels(image);
1336           continue;
1337         }
1338       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1339       {
1340         PixelChannel
1341           channel;
1342
1343         PixelTrait
1344           flop_traits,
1345           traits;
1346
1347         channel=GetPixelChannelMapChannel(image,i);
1348         traits=GetPixelChannelMapTraits(image,channel);
1349         flop_traits=GetPixelChannelMapTraits(flop_image,channel);
1350         if ((traits == UndefinedPixelTrait) ||
1351             (flop_traits == UndefinedPixelTrait))
1352           continue;
1353         SetPixelChannel(flop_image,channel,p[i],q);
1354       }
1355       p+=GetPixelChannels(image);
1356     }
1357     if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1358       status=MagickFalse;
1359     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1360       {
1361         MagickBooleanType
1362           proceed;
1363
1364 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1365         #pragma omp critical (MagickCore_FlopImage)
1366 #endif
1367         proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
1368         if (proceed == MagickFalse)
1369           status=MagickFalse;
1370       }
1371   }
1372   flop_view=DestroyCacheView(flop_view);
1373   image_view=DestroyCacheView(image_view);
1374   flop_image->type=image->type;
1375   if (page.width != 0)
1376     page.x=(ssize_t) (page.width-flop_image->columns-page.x);
1377   flop_image->page=page;
1378   if (status == MagickFalse)
1379     flop_image=DestroyImage(flop_image);
1380   return(flop_image);
1381 }
1382 \f
1383 /*
1384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1385 %                                                                             %
1386 %                                                                             %
1387 %                                                                             %
1388 %   R o l l I m a g e                                                         %
1389 %                                                                             %
1390 %                                                                             %
1391 %                                                                             %
1392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1393 %
1394 %  RollImage() offsets an image as defined by x_offset and y_offset.
1395 %
1396 %  The format of the RollImage method is:
1397 %
1398 %      Image *RollImage(const Image *image,const ssize_t x_offset,
1399 %        const ssize_t y_offset,ExceptionInfo *exception)
1400 %
1401 %  A description of each parameter follows:
1402 %
1403 %    o image: the image.
1404 %
1405 %    o x_offset: the number of columns to roll in the horizontal direction.
1406 %
1407 %    o y_offset: the number of rows to roll in the vertical direction.
1408 %
1409 %    o exception: return any errors or warnings in this structure.
1410 %
1411 */
1412
1413 static inline MagickBooleanType CopyImageRegion(Image *destination,
1414   const Image *source,const size_t columns,const size_t rows,
1415   const ssize_t sx,const ssize_t sy,const ssize_t dx,const ssize_t dy,
1416   ExceptionInfo *exception)
1417 {
1418   CacheView
1419     *source_view,
1420     *destination_view;
1421
1422   MagickBooleanType
1423     status;
1424
1425   ssize_t
1426     y;
1427
1428   status=MagickTrue;
1429   source_view=AcquireCacheView(source);
1430   destination_view=AcquireCacheView(destination);
1431 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1432   #pragma omp parallel for schedule(static) shared(status)
1433 #endif
1434   for (y=0; y < (ssize_t) rows; y++)
1435   {
1436     MagickBooleanType
1437       sync;
1438
1439     register const Quantum
1440       *restrict p;
1441
1442     register Quantum
1443       *restrict q;
1444
1445     register ssize_t
1446       x;
1447
1448     /*
1449       Transfer scanline.
1450     */
1451     if (status == MagickFalse)
1452       continue;
1453     p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1454     q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1455     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1456       {
1457         status=MagickFalse;
1458         continue;
1459       }
1460     for (x=0; x < (ssize_t) columns; x++)
1461     {
1462       register ssize_t
1463         i;
1464
1465       if (GetPixelMask(source,p) != 0)
1466         {
1467           p+=GetPixelChannels(source);
1468           q+=GetPixelChannels(destination);
1469           continue;
1470         }
1471       for (i=0; i < (ssize_t) GetPixelChannels(source); i++)
1472       {
1473         PixelChannel
1474           channel;
1475
1476         PixelTrait
1477           destination_traits,
1478           source_traits;
1479
1480         channel=GetPixelChannelMapChannel(source,i);
1481         source_traits=GetPixelChannelMapTraits(source,channel);
1482         destination_traits=GetPixelChannelMapTraits(destination,channel);
1483         if ((source_traits == UndefinedPixelTrait) ||
1484             (destination_traits == UndefinedPixelTrait))
1485           continue;
1486         SetPixelChannel(destination,channel,p[i],q);
1487       }
1488       p+=GetPixelChannels(source);
1489       q+=GetPixelChannels(destination);
1490     }
1491     sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1492     if (sync == MagickFalse)
1493       status=MagickFalse;
1494   }
1495   destination_view=DestroyCacheView(destination_view);
1496   source_view=DestroyCacheView(source_view);
1497   return(status);
1498 }
1499
1500 MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1501   const ssize_t y_offset,ExceptionInfo *exception)
1502 {
1503 #define RollImageTag  "Roll/Image"
1504
1505   Image
1506     *roll_image;
1507
1508   MagickStatusType
1509     status;
1510
1511   RectangleInfo
1512     offset;
1513
1514   /*
1515     Initialize roll image attributes.
1516   */
1517   assert(image != (const Image *) NULL);
1518   assert(image->signature == MagickSignature);
1519   if (image->debug != MagickFalse)
1520     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1521   assert(exception != (ExceptionInfo *) NULL);
1522   assert(exception->signature == MagickSignature);
1523   roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1524   if (roll_image == (Image *) NULL)
1525     return((Image *) NULL);
1526   offset.x=x_offset;
1527   offset.y=y_offset;
1528   while (offset.x < 0)
1529     offset.x+=(ssize_t) image->columns;
1530   while (offset.x >= (ssize_t) image->columns)
1531     offset.x-=(ssize_t) image->columns;
1532   while (offset.y < 0)
1533     offset.y+=(ssize_t) image->rows;
1534   while (offset.y >= (ssize_t) image->rows)
1535     offset.y-=(ssize_t) image->rows;
1536   /*
1537     Roll image.
1538   */
1539   status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1540     (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
1541     offset.y,0,0,exception);
1542   (void) SetImageProgress(image,RollImageTag,0,3);
1543   status|=CopyImageRegion(roll_image,image,image->columns-offset.x,
1544     (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0,
1545     exception);
1546   (void) SetImageProgress(image,RollImageTag,1,3);
1547   status|=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows-
1548     offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception);
1549   (void) SetImageProgress(image,RollImageTag,2,3);
1550   status|=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
1551     offset.y,0,0,offset.x,offset.y,exception);
1552   (void) SetImageProgress(image,RollImageTag,3,3);
1553   roll_image->type=image->type;
1554   if (status == MagickFalse)
1555     roll_image=DestroyImage(roll_image);
1556   return(roll_image);
1557 }
1558 \f
1559 /*
1560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1561 %                                                                             %
1562 %                                                                             %
1563 %                                                                             %
1564 %   S h a v e I m a g e                                                       %
1565 %                                                                             %
1566 %                                                                             %
1567 %                                                                             %
1568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1569 %
1570 %  ShaveImage() shaves pixels from the image edges.  It allocates the memory
1571 %  necessary for the new Image structure and returns a pointer to the new
1572 %  image.
1573 %
1574 %  The format of the ShaveImage method is:
1575 %
1576 %      Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1577 %        ExceptionInfo *exception)
1578 %
1579 %  A description of each parameter follows:
1580 %
1581 %    o shave_image: Method ShaveImage returns a pointer to the shaved
1582 %      image.  A null image is returned if there is a memory shortage or
1583 %      if the image width or height is zero.
1584 %
1585 %    o image: the image.
1586 %
1587 %    o shave_info: Specifies a pointer to a RectangleInfo which defines the
1588 %      region of the image to crop.
1589 %
1590 %    o exception: return any errors or warnings in this structure.
1591 %
1592 */
1593 MagickExport Image *ShaveImage(const Image *image,
1594   const RectangleInfo *shave_info,ExceptionInfo *exception)
1595 {
1596   Image
1597     *shave_image;
1598
1599   RectangleInfo
1600     geometry;
1601
1602   assert(image != (const Image *) NULL);
1603   assert(image->signature == MagickSignature);
1604   if (image->debug != MagickFalse)
1605     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1606   if (((2*shave_info->width) >= image->columns) ||
1607       ((2*shave_info->height) >= image->rows))
1608     ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1609   SetGeometry(image,&geometry);
1610   geometry.width-=2*shave_info->width;
1611   geometry.height-=2*shave_info->height;
1612   geometry.x=(ssize_t) shave_info->width+image->page.x;
1613   geometry.y=(ssize_t) shave_info->height+image->page.y;
1614   shave_image=CropImage(image,&geometry,exception);
1615   if (shave_image == (Image *) NULL)
1616     return((Image *) NULL);
1617   shave_image->page.width-=2*shave_info->width;
1618   shave_image->page.height-=2*shave_info->height;
1619   shave_image->page.x-=(ssize_t) shave_info->width;
1620   shave_image->page.y-=(ssize_t) shave_info->height;
1621   return(shave_image);
1622 }
1623 \f
1624 /*
1625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1626 %                                                                             %
1627 %                                                                             %
1628 %                                                                             %
1629 %   S p l i c e I m a g e                                                     %
1630 %                                                                             %
1631 %                                                                             %
1632 %                                                                             %
1633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1634 %
1635 %  SpliceImage() splices a solid color into the image as defined by the
1636 %  geometry.
1637 %
1638 %  The format of the SpliceImage method is:
1639 %
1640 %      Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1641 %        ExceptionInfo *exception)
1642 %
1643 %  A description of each parameter follows:
1644 %
1645 %    o image: the image.
1646 %
1647 %    o geometry: Define the region of the image to splice with members
1648 %      x, y, width, and height.
1649 %
1650 %    o exception: return any errors or warnings in this structure.
1651 %
1652 */
1653 MagickExport Image *SpliceImage(const Image *image,
1654   const RectangleInfo *geometry,ExceptionInfo *exception)
1655 {
1656 #define SpliceImageTag  "Splice/Image"
1657
1658   CacheView
1659     *image_view,
1660     *splice_view;
1661
1662   Image
1663     *splice_image;
1664
1665   MagickBooleanType
1666     status;
1667
1668   MagickOffsetType
1669     progress;
1670
1671   RectangleInfo
1672     splice_geometry;
1673
1674   ssize_t
1675     y;
1676
1677   /*
1678     Allocate splice image.
1679   */
1680   assert(image != (const Image *) NULL);
1681   assert(image->signature == MagickSignature);
1682   if (image->debug != MagickFalse)
1683     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1684   assert(geometry != (const RectangleInfo *) NULL);
1685   assert(exception != (ExceptionInfo *) NULL);
1686   assert(exception->signature == MagickSignature);
1687   splice_geometry=(*geometry);
1688   splice_image=CloneImage(image,image->columns+splice_geometry.width,
1689     image->rows+splice_geometry.height,MagickTrue,exception);
1690   if (splice_image == (Image *) NULL)
1691     return((Image *) NULL);
1692   if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse)
1693     {
1694       splice_image=DestroyImage(splice_image);
1695       return((Image *) NULL);
1696     }
1697   (void) SetImageBackgroundColor(splice_image,exception);
1698   /*
1699     Respect image geometry.
1700   */
1701   switch (image->gravity)
1702   {
1703     default:
1704     case UndefinedGravity:
1705     case NorthWestGravity:
1706       break;
1707     case NorthGravity:
1708     {
1709       splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1710       break;
1711     }
1712     case NorthEastGravity:
1713     {
1714       splice_geometry.x+=(ssize_t) splice_geometry.width;
1715       break;
1716     }
1717     case WestGravity:
1718     {
1719       splice_geometry.y+=(ssize_t) splice_geometry.width/2;
1720       break;
1721     }
1722     case StaticGravity:
1723     case CenterGravity:
1724     {
1725       splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1726       splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1727       break;
1728     }
1729     case EastGravity:
1730     {
1731       splice_geometry.x+=(ssize_t) splice_geometry.width;
1732       splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1733       break;
1734     }
1735     case SouthWestGravity:
1736     {
1737       splice_geometry.y+=(ssize_t) splice_geometry.height;
1738       break;
1739     }
1740     case SouthGravity:
1741     {
1742       splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1743       splice_geometry.y+=(ssize_t) splice_geometry.height;
1744       break;
1745     }
1746     case SouthEastGravity:
1747     {
1748       splice_geometry.x+=(ssize_t) splice_geometry.width;
1749       splice_geometry.y+=(ssize_t) splice_geometry.height;
1750       break;
1751     }
1752   }
1753   /*
1754     Splice image.
1755   */
1756   status=MagickTrue;
1757   progress=0;
1758   image_view=AcquireCacheView(image);
1759   splice_view=AcquireCacheView(splice_image);
1760 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1761   #pragma omp parallel for schedule(static,4) shared(progress,status)
1762 #endif
1763   for (y=0; y < (ssize_t) splice_geometry.y; y++)
1764   {
1765     register const Quantum
1766       *restrict p;
1767
1768     register ssize_t
1769       x;
1770
1771     register Quantum
1772       *restrict q;
1773
1774     if (status == MagickFalse)
1775       continue;
1776     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1777     q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1778       exception);
1779     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1780       {
1781         status=MagickFalse;
1782         continue;
1783       }
1784     for (x=0; x < splice_geometry.x; x++)
1785     {
1786       register ssize_t
1787         i;
1788
1789       if (GetPixelMask(image,p) != 0)
1790         {
1791           p+=GetPixelChannels(image);
1792           q+=GetPixelChannels(splice_image);
1793           continue;
1794         }
1795       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1796       {
1797         PixelChannel
1798           channel;
1799
1800         PixelTrait
1801           splice_traits,
1802           traits;
1803
1804         channel=GetPixelChannelMapChannel(image,i);
1805         traits=GetPixelChannelMapTraits(image,channel);
1806         splice_traits=GetPixelChannelMapTraits(splice_image,channel);
1807         if ((traits == UndefinedPixelTrait) ||
1808             (splice_traits == UndefinedPixelTrait))
1809           continue;
1810         SetPixelChannel(splice_image,channel,p[i],q);
1811       }
1812       p+=GetPixelChannels(image);
1813       q+=GetPixelChannels(splice_image);
1814     }
1815     for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
1816       q+=GetPixelChannels(splice_image);
1817     for ( ; x < (ssize_t) splice_image->columns; x++)
1818     {
1819       register ssize_t
1820         i;
1821
1822       if (GetPixelMask(image,p) != 0)
1823         {
1824           p+=GetPixelChannels(image);
1825           q+=GetPixelChannels(splice_image);
1826           continue;
1827         }
1828       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1829       {
1830         PixelChannel
1831           channel;
1832
1833         PixelTrait
1834           traits,
1835           splice_traits;
1836
1837         channel=GetPixelChannelMapChannel(image,i);
1838         traits=GetPixelChannelMapTraits(image,channel);
1839         splice_traits=GetPixelChannelMapTraits(splice_image,channel);
1840         if ((traits == UndefinedPixelTrait) ||
1841             (splice_traits == UndefinedPixelTrait))
1842           continue;
1843         SetPixelChannel(splice_image,channel,p[i],q);
1844       }
1845       p+=GetPixelChannels(image);
1846       q+=GetPixelChannels(splice_image);
1847     }
1848     if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1849       status=MagickFalse;
1850     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1851       {
1852         MagickBooleanType
1853           proceed;
1854
1855 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1856         #pragma omp critical (MagickCore_TransposeImage)
1857 #endif
1858         proceed=SetImageProgress(image,SpliceImageTag,progress++,
1859           splice_image->rows);
1860         if (proceed == MagickFalse)
1861           status=MagickFalse;
1862       }
1863   }
1864 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1865   #pragma omp parallel for schedule(static,4) shared(progress,status)
1866 #endif
1867   for (y=(ssize_t) (splice_geometry.y+splice_geometry.height);
1868        y < (ssize_t) splice_image->rows; y++)
1869   {
1870     register const Quantum
1871       *restrict p;
1872
1873     register ssize_t
1874       x;
1875
1876     register Quantum
1877       *restrict q;
1878
1879     if (status == MagickFalse)
1880       continue;
1881     p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
1882       image->columns,1,exception);
1883     if ((y < 0) || (y >= (ssize_t) splice_image->rows))
1884       continue;
1885     q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1886       exception);
1887     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1888       {
1889         status=MagickFalse;
1890         continue;
1891       }
1892     for (x=0; x < splice_geometry.x; x++)
1893     {
1894       register ssize_t
1895         i;
1896
1897       if (GetPixelMask(image,q) != 0)
1898         {
1899           p+=GetPixelChannels(image);
1900           q+=GetPixelChannels(splice_image);
1901           continue;
1902         }
1903       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1904       {
1905         PixelChannel
1906           channel;
1907
1908         PixelTrait
1909           traits,
1910           splice_traits;
1911
1912         channel=GetPixelChannelMapChannel(image,i);
1913         traits=GetPixelChannelMapTraits(image,channel);
1914         splice_traits=GetPixelChannelMapTraits(splice_image,channel);
1915         if ((traits == UndefinedPixelTrait) ||
1916             (splice_traits == UndefinedPixelTrait))
1917           continue;
1918         SetPixelChannel(splice_image,channel,p[i],q);
1919       }
1920       p+=GetPixelChannels(image);
1921       q+=GetPixelChannels(splice_image);
1922     }
1923     for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
1924       q+=GetPixelChannels(splice_image);
1925     for ( ; x < (ssize_t) splice_image->columns; x++)
1926     {
1927       register ssize_t
1928         i;
1929
1930       if (GetPixelMask(image,q) != 0)
1931         {
1932           p+=GetPixelChannels(image);
1933           q+=GetPixelChannels(splice_image);
1934           continue;
1935         }
1936       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1937       {
1938         PixelChannel
1939           channel;
1940
1941         PixelTrait
1942           traits,
1943           splice_traits;
1944
1945         channel=GetPixelChannelMapChannel(image,i);
1946         traits=GetPixelChannelMapTraits(image,channel);
1947         splice_traits=GetPixelChannelMapTraits(splice_image,channel);
1948         if ((traits == UndefinedPixelTrait) ||
1949             (splice_traits == UndefinedPixelTrait))
1950           continue;
1951         SetPixelChannel(splice_image,channel,p[i],q);
1952       }
1953       p+=GetPixelChannels(image);
1954       q+=GetPixelChannels(splice_image);
1955     }
1956     if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1957       status=MagickFalse;
1958     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1959       {
1960         MagickBooleanType
1961           proceed;
1962
1963 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1964         #pragma omp critical (MagickCore_TransposeImage)
1965 #endif
1966         proceed=SetImageProgress(image,SpliceImageTag,progress++,
1967           splice_image->rows);
1968         if (proceed == MagickFalse)
1969           status=MagickFalse;
1970       }
1971   }
1972   splice_view=DestroyCacheView(splice_view);
1973   image_view=DestroyCacheView(image_view);
1974   if (status == MagickFalse)
1975     splice_image=DestroyImage(splice_image);
1976   return(splice_image);
1977 }
1978 \f
1979 /*
1980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1981 %                                                                             %
1982 %                                                                             %
1983 %                                                                             %
1984 %   T r a n s f o r m I m a g e                                               %
1985 %                                                                             %
1986 %                                                                             %
1987 %                                                                             %
1988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1989 %
1990 %  TransformImage() is a convenience method that behaves like ResizeImage() or
1991 %  CropImage() but accepts scaling and/or cropping information as a region
1992 %  geometry specification.  If the operation fails, the original image handle
1993 %  is left as is.
1994 %
1995 %  This should only be used for single images.
1996 %
1997 %  This function destroys what it assumes to be a single image list.
1998 %  If the input image is part of a larger list, all other images in that list
1999 %  will be simply 'lost', not destroyed.
2000 %
2001 %  Also if the crop generates a list of images only the first image is resized.
2002 %  And finally if the crop succeeds and the resize failed, you will get a
2003 %  cropped image, as well as a 'false' or 'failed' report.
2004 %
2005 %  This function and should probably be depreciated in favor of direct calls
2006 %  to CropImageToTiles() or ResizeImage(), as appropriate.
2007 %
2008 %  The format of the TransformImage method is:
2009 %
2010 %      MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
2011 %        const char *image_geometry,ExceptionInfo *exception)
2012 %
2013 %  A description of each parameter follows:
2014 %
2015 %    o image: the image The transformed image is returned as this parameter.
2016 %
2017 %    o crop_geometry: A crop geometry string.  This geometry defines a
2018 %      subregion of the image to crop.
2019 %
2020 %    o image_geometry: An image geometry string.  This geometry defines the
2021 %      final size of the image.
2022 %
2023 %    o exception: return any errors or warnings in this structure.
2024 %
2025 */
2026 MagickExport MagickBooleanType TransformImage(Image **image,
2027   const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
2028 {
2029   Image
2030     *resize_image,
2031     *transform_image;
2032
2033   MagickStatusType
2034     flags;
2035
2036   RectangleInfo
2037     geometry;
2038
2039   assert(image != (Image **) NULL);
2040   assert((*image)->signature == MagickSignature);
2041   if ((*image)->debug != MagickFalse)
2042     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
2043   transform_image=(*image);
2044   if (crop_geometry != (const char *) NULL)
2045     {
2046       Image
2047         *crop_image;
2048
2049       /*
2050         Crop image to a user specified size.
2051       */
2052       crop_image=CropImageToTiles(*image,crop_geometry,exception);
2053       if (crop_image == (Image *) NULL)
2054         transform_image=CloneImage(*image,0,0,MagickTrue,exception);
2055       else
2056         {
2057           transform_image=DestroyImage(transform_image);
2058           transform_image=GetFirstImageInList(crop_image);
2059         }
2060       *image=transform_image;
2061     }
2062   if (image_geometry == (const char *) NULL)
2063     return(MagickTrue);
2064
2065   /*
2066     Scale image to a user specified size.
2067   */
2068   flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,exception);
2069   (void) flags;
2070   if ((transform_image->columns == geometry.width) &&
2071       (transform_image->rows == geometry.height))
2072     return(MagickTrue);
2073   resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
2074     transform_image->filter,exception);
2075   if (resize_image == (Image *) NULL)
2076     return(MagickFalse);
2077   transform_image=DestroyImage(transform_image);
2078   transform_image=resize_image;
2079   *image=transform_image;
2080   return(MagickTrue);
2081 }
2082 \f
2083 /*
2084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2085 %                                                                             %
2086 %                                                                             %
2087 %                                                                             %
2088 %   T r a n s f o r m I m a g e s                                             %
2089 %                                                                             %
2090 %                                                                             %
2091 %                                                                             %
2092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2093 %
2094 %  TransformImages() calls TransformImage() on each image of a sequence.
2095 %
2096 %  The format of the TransformImage method is:
2097 %
2098 %      MagickBooleanType TransformImages(Image **image,
2099 %        const char *crop_geometry,const char *image_geometry,
2100 %        ExceptionInfo *exception)
2101 %
2102 %  A description of each parameter follows:
2103 %
2104 %    o image: the image The transformed image is returned as this parameter.
2105 %
2106 %    o crop_geometry: A crop geometry string.  This geometry defines a
2107 %      subregion of the image to crop.
2108 %
2109 %    o image_geometry: An image geometry string.  This geometry defines the
2110 %      final size of the image.
2111 %
2112 %    o exception: return any errors or warnings in this structure.
2113 %
2114 */
2115 MagickExport MagickBooleanType TransformImages(Image **images,
2116   const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
2117 {
2118   Image
2119     *image,
2120     **image_list,
2121     *transform_images;
2122
2123   MagickStatusType
2124     status;
2125
2126   register ssize_t
2127     i;
2128
2129   assert(images != (Image **) NULL);
2130   assert((*images)->signature == MagickSignature);
2131   if ((*images)->debug != MagickFalse)
2132     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2133       (*images)->filename);
2134   image_list=ImageListToArray(*images,exception);
2135   if (image_list == (Image **) NULL)
2136     return(MagickFalse);
2137   status=MagickTrue;
2138   transform_images=NewImageList();
2139   for (i=0; image_list[i] != (Image *) NULL; i++)
2140   {
2141     image=image_list[i];
2142     status|=TransformImage(&image,crop_geometry,image_geometry,exception);
2143     AppendImageToList(&transform_images,image);
2144   }
2145   *images=transform_images;
2146   image_list=(Image **) RelinquishMagickMemory(image_list);
2147   return(status != 0 ? MagickTrue : MagickFalse);
2148 }
2149 \f
2150 /*
2151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2152 %                                                                             %
2153 %                                                                             %
2154 %                                                                             %
2155 %   T r a n s p o s e I m a g e                                               %
2156 %                                                                             %
2157 %                                                                             %
2158 %                                                                             %
2159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2160 %
2161 %  TransposeImage() creates a horizontal mirror image by reflecting the pixels
2162 %  around the central y-axis while rotating them by 90 degrees.
2163 %
2164 %  The format of the TransposeImage method is:
2165 %
2166 %      Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2167 %
2168 %  A description of each parameter follows:
2169 %
2170 %    o image: the image.
2171 %
2172 %    o exception: return any errors or warnings in this structure.
2173 %
2174 */
2175 MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2176 {
2177 #define TransposeImageTag  "Transpose/Image"
2178
2179   CacheView
2180     *image_view,
2181     *transpose_view;
2182
2183   Image
2184     *transpose_image;
2185
2186   MagickBooleanType
2187     status;
2188
2189   MagickOffsetType
2190     progress;
2191
2192   RectangleInfo
2193     page;
2194
2195   ssize_t
2196     y;
2197
2198   assert(image != (const Image *) NULL);
2199   assert(image->signature == MagickSignature);
2200   if (image->debug != MagickFalse)
2201     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2202   assert(exception != (ExceptionInfo *) NULL);
2203   assert(exception->signature == MagickSignature);
2204   transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2205     exception);
2206   if (transpose_image == (Image *) NULL)
2207     return((Image *) NULL);
2208   /*
2209     Transpose image.
2210   */
2211   status=MagickTrue;
2212   progress=0;
2213   image_view=AcquireCacheView(image);
2214   transpose_view=AcquireCacheView(transpose_image);
2215 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2216   #pragma omp parallel for schedule(static,4) shared(progress,status)
2217 #endif
2218   for (y=0; y < (ssize_t) image->rows; y++)
2219   {
2220     register const Quantum
2221       *restrict p;
2222
2223     register Quantum
2224       *restrict q;
2225
2226     register ssize_t
2227       x;
2228
2229     if (status == MagickFalse)
2230       continue;
2231     p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
2232       image->columns,1,exception);
2233     q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1),
2234       0,1,transpose_image->rows,exception);
2235     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2236       {
2237         status=MagickFalse;
2238         continue;
2239       }
2240     for (x=0; x < (ssize_t) image->columns; x++)
2241     {
2242       register ssize_t
2243         i;
2244
2245       if (GetPixelMask(image,q) != 0)
2246         {
2247           p+=GetPixelChannels(image);
2248           q+=GetPixelChannels(transpose_image);
2249           continue;
2250         }
2251       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2252       {
2253         PixelChannel
2254           channel;
2255
2256         PixelTrait
2257           traits,
2258           transpose_traits;
2259
2260         channel=GetPixelChannelMapChannel(image,i);
2261         traits=GetPixelChannelMapTraits(image,channel);
2262         transpose_traits=GetPixelChannelMapTraits(transpose_image,channel);
2263         if ((traits == UndefinedPixelTrait) ||
2264             (transpose_traits == UndefinedPixelTrait))
2265           continue;
2266         SetPixelChannel(transpose_image,channel,p[i],q);
2267       }
2268       p+=GetPixelChannels(image);
2269       q+=GetPixelChannels(transpose_image);
2270     }
2271     if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2272       status=MagickFalse;
2273     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2274       {
2275         MagickBooleanType
2276           proceed;
2277
2278 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2279         #pragma omp critical (MagickCore_TransposeImage)
2280 #endif
2281         proceed=SetImageProgress(image,TransposeImageTag,progress++,
2282           image->rows);
2283         if (proceed == MagickFalse)
2284           status=MagickFalse;
2285       }
2286   }
2287   transpose_view=DestroyCacheView(transpose_view);
2288   image_view=DestroyCacheView(image_view);
2289   transpose_image->type=image->type;
2290   page=transpose_image->page;
2291   Swap(page.width,page.height);
2292   Swap(page.x,page.y);
2293   transpose_image->page=page;
2294   if (status == MagickFalse)
2295     transpose_image=DestroyImage(transpose_image);
2296   return(transpose_image);
2297 }
2298 \f
2299 /*
2300 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2301 %                                                                             %
2302 %                                                                             %
2303 %                                                                             %
2304 %   T r a n s v e r s e I m a g e                                             %
2305 %                                                                             %
2306 %                                                                             %
2307 %                                                                             %
2308 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2309 %
2310 %  TransverseImage() creates a vertical mirror image by reflecting the pixels
2311 %  around the central x-axis while rotating them by 270 degrees.
2312 %
2313 %  The format of the TransverseImage method is:
2314 %
2315 %      Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2316 %
2317 %  A description of each parameter follows:
2318 %
2319 %    o image: the image.
2320 %
2321 %    o exception: return any errors or warnings in this structure.
2322 %
2323 */
2324 MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2325 {
2326 #define TransverseImageTag  "Transverse/Image"
2327
2328   CacheView
2329     *image_view,
2330     *transverse_view;
2331
2332   Image
2333     *transverse_image;
2334
2335   MagickBooleanType
2336     status;
2337
2338   MagickOffsetType
2339     progress;
2340
2341   RectangleInfo
2342     page;
2343
2344   ssize_t
2345     y;
2346
2347   assert(image != (const Image *) NULL);
2348   assert(image->signature == MagickSignature);
2349   if (image->debug != MagickFalse)
2350     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2351   assert(exception != (ExceptionInfo *) NULL);
2352   assert(exception->signature == MagickSignature);
2353   transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2354     exception);
2355   if (transverse_image == (Image *) NULL)
2356     return((Image *) NULL);
2357   /*
2358     Transverse image.
2359   */
2360   status=MagickTrue;
2361   progress=0;
2362   image_view=AcquireCacheView(image);
2363   transverse_view=AcquireCacheView(transverse_image);
2364 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2365   #pragma omp parallel for schedule(static,4) shared(progress,status)
2366 #endif
2367   for (y=0; y < (ssize_t) image->rows; y++)
2368   {
2369     MagickBooleanType
2370       sync;
2371
2372     register const Quantum
2373       *restrict p;
2374
2375     register Quantum
2376       *restrict q;
2377
2378     register ssize_t
2379       x;
2380
2381     if (status == MagickFalse)
2382       continue;
2383     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2384     q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-1),
2385       0,1,transverse_image->rows,exception);
2386     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2387       {
2388         status=MagickFalse;
2389         continue;
2390       }
2391     q+=GetPixelChannels(transverse_image)*image->columns;
2392     for (x=0; x < (ssize_t) image->columns; x++)
2393     {
2394       register ssize_t
2395         i;
2396
2397       q-=GetPixelChannels(transverse_image);
2398       if (GetPixelMask(image,p) != 0)
2399         {
2400           p+=GetPixelChannels(image);
2401           continue;
2402         }
2403       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2404       {
2405         PixelChannel
2406           channel;
2407
2408         PixelTrait
2409           traits,
2410           transverse_traits;
2411
2412         channel=GetPixelChannelMapChannel(image,i);
2413         traits=GetPixelChannelMapTraits(image,channel);
2414         transverse_traits=GetPixelChannelMapTraits(transverse_image,channel);
2415         if ((traits == UndefinedPixelTrait) ||
2416             (transverse_traits == UndefinedPixelTrait))
2417           continue;
2418         SetPixelChannel(transverse_image,channel,p[i],q);
2419       }
2420       p+=GetPixelChannels(image);
2421     }
2422     sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2423     if (sync == MagickFalse)
2424       status=MagickFalse;
2425     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2426       {
2427         MagickBooleanType
2428           proceed;
2429
2430 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2431         #pragma omp critical (MagickCore_TransverseImage)
2432 #endif
2433         proceed=SetImageProgress(image,TransverseImageTag,progress++,
2434           image->rows);
2435         if (proceed == MagickFalse)
2436           status=MagickFalse;
2437       }
2438   }
2439   transverse_view=DestroyCacheView(transverse_view);
2440   image_view=DestroyCacheView(image_view);
2441   transverse_image->type=image->type;
2442   page=transverse_image->page;
2443   Swap(page.width,page.height);
2444   Swap(page.x,page.y);
2445   if (page.width != 0)
2446     page.x=(ssize_t) (page.width-transverse_image->columns-page.x);
2447   if (page.height != 0)
2448     page.y=(ssize_t) (page.height-transverse_image->rows-page.y);
2449   transverse_image->page=page;
2450   if (status == MagickFalse)
2451     transverse_image=DestroyImage(transverse_image);
2452   return(transverse_image);
2453 }
2454 \f
2455 /*
2456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2457 %                                                                             %
2458 %                                                                             %
2459 %                                                                             %
2460 %   T r i m I m a g e                                                         %
2461 %                                                                             %
2462 %                                                                             %
2463 %                                                                             %
2464 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2465 %
2466 %  TrimImage() trims pixels from the image edges.  It allocates the memory
2467 %  necessary for the new Image structure and returns a pointer to the new
2468 %  image.
2469 %
2470 %  The format of the TrimImage method is:
2471 %
2472 %      Image *TrimImage(const Image *image,ExceptionInfo *exception)
2473 %
2474 %  A description of each parameter follows:
2475 %
2476 %    o image: the image.
2477 %
2478 %    o exception: return any errors or warnings in this structure.
2479 %
2480 */
2481 MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2482 {
2483   RectangleInfo
2484     geometry;
2485
2486   assert(image != (const Image *) NULL);
2487   assert(image->signature == MagickSignature);
2488   if (image->debug != MagickFalse)
2489     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2490   geometry=GetImageBoundingBox(image,exception);
2491   if ((geometry.width == 0) || (geometry.height == 0))
2492     {
2493       Image
2494         *crop_image;
2495
2496       crop_image=CloneImage(image,1,1,MagickTrue,exception);
2497       if (crop_image == (Image *) NULL)
2498         return((Image *) NULL);
2499       crop_image->background_color.alpha=(Quantum) TransparentAlpha;
2500       (void) SetImageBackgroundColor(crop_image,exception);
2501       crop_image->page=image->page;
2502       crop_image->page.x=(-1);
2503       crop_image->page.y=(-1);
2504       return(crop_image);
2505     }
2506   geometry.x+=image->page.x;
2507   geometry.y+=image->page.y;
2508   return(CropImage(image,&geometry,exception));
2509 }