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