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