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