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