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