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