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