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