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