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