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