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