]> granicus.if.org Git - imagemagick/blob - magick/transform.c
(no commit message)
[imagemagick] / magick / transform.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %       TTTTT  RRRR    AAA   N   N  SSSSS  FFFFF   OOO   RRRR   M   M         %
7 %         T    R   R  A   A  NN  N  SS     F      O   O  R   R  MM MM         %
8 %         T    RRRR   AAAAA  N N N   SSS   FFF    O   O  RRRR   M M M         %
9 %         T    R R    A   A  N  NN     SS  F      O   O  R R    M   M         %
10 %         T    R  R   A   A  N   N  SSSSS  F       OOO   R  R   M   M         %
11 %                                                                             %
12 %                                                                             %
13 %                    MagickCore Image Transform Methods                       %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/attribute.h"
44 #include "magick/cache.h"
45 #include "magick/cache-view.h"
46 #include "magick/color.h"
47 #include "magick/color-private.h"
48 #include "magick/colorspace-private.h"
49 #include "magick/composite.h"
50 #include "magick/draw.h"
51 #include "magick/effect.h"
52 #include "magick/exception.h"
53 #include "magick/exception-private.h"
54 #include "magick/geometry.h"
55 #include "magick/image.h"
56 #include "magick/memory_.h"
57 #include "magick/layer.h"
58 #include "magick/list.h"
59 #include "magick/monitor.h"
60 #include "magick/monitor-private.h"
61 #include "magick/pixel-private.h"
62 #include "magick/resource_.h"
63 #include "magick/resize.h"
64 #include "magick/statistic.h"
65 #include "magick/string_.h"
66 #include "magick/thread-private.h"
67 #include "magick/transform.h"
68 \f
69 /*
70 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71 %                                                                             %
72 %                                                                             %
73 %                                                                             %
74 %   C h o p I m a g e                                                         %
75 %                                                                             %
76 %                                                                             %
77 %                                                                             %
78 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 %
80 %  ChopImage() removes a region of an image and collapses the image to occupy
81 %  the removed portion.
82 %
83 %  The format of the ChopImage method is:
84 %
85 %      Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
86 %        ExceptionInfo *exception)
87 %
88 %  A description of each parameter follows:
89 %
90 %    o image: the image.
91 %
92 %    o chop_info: Define the region of the image to chop.
93 %
94 %    o exception: return any errors or warnings in this structure.
95 %
96 */
97 MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
98   ExceptionInfo *exception)
99 {
100 #define ChopImageTag  "Chop/Image"
101
102   CacheView
103     *chop_view,
104     *image_view;
105
106   Image
107     *chop_image;
108
109   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   register ssize_t
327     i;
328
329   ssize_t
330     y;
331
332   /*
333     Consolidate separate C, M, Y, and K planes into a single image.
334   */
335   assert(images != (Image *) NULL);
336   assert(images->signature == MagickSignature);
337   if (images->debug != MagickFalse)
338     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
339   assert(exception != (ExceptionInfo *) NULL);
340   assert(exception->signature == MagickSignature);
341   cmyk_images=NewImageList();
342   for (i=0; i < (ssize_t) GetImageListLength(images); i+=4)
343   {
344     cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
345       exception);
346     if (cmyk_image == (Image *) NULL)
347       break;
348     if (SetImageStorageClass(cmyk_image,DirectClass) == MagickFalse)
349       break;
350     (void) SetImageColorspace(cmyk_image,CMYKColorspace);
351     image_view=AcquireCacheView(images);
352     cmyk_view=AcquireCacheView(cmyk_image);
353     for (y=0; y < (ssize_t) images->rows; y++)
354     {
355       register const PixelPacket
356         *restrict p;
357
358       register ssize_t
359         x;
360
361       register PixelPacket
362         *restrict q;
363
364       p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
365       q=QueueCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
366         exception);
367       if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
368         break;
369       for (x=0; x < (ssize_t) images->columns; x++)
370       {
371         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     if ((y < 0) || (y >= (ssize_t) splice_image->rows))
1605       continue;
1606     q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1607       exception);
1608     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1609       {
1610         status=MagickFalse;
1611         continue;
1612       }
1613     indexes=GetCacheViewAuthenticIndexQueue(image_view);
1614     splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1615     for (x=0; x < splice_geometry.x; 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     for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
1629       q++;
1630     for ( ; x < (ssize_t) splice_image->columns; x++)
1631     {
1632       SetRedPixelComponent(q,GetRedPixelComponent(p));
1633       SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1634       SetBluePixelComponent(q,GetBluePixelComponent(p));
1635       SetOpacityPixelComponent(q,OpaqueOpacity);
1636       if (image->matte != MagickFalse)
1637         SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
1638       if (image->colorspace == CMYKColorspace)
1639         splice_indexes[x]=(*indexes++);
1640       p++;
1641       q++;
1642     }
1643     if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1644       status=MagickFalse;
1645     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1646       {
1647         MagickBooleanType
1648           proceed;
1649
1650 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1651   #pragma omp critical (MagickCore_TransposeImage)
1652 #endif
1653         proceed=SetImageProgress(image,SpliceImageTag,progress++,
1654           splice_image->rows);
1655         if (proceed == MagickFalse)
1656           status=MagickFalse;
1657       }
1658   }
1659   splice_view=DestroyCacheView(splice_view);
1660   image_view=DestroyCacheView(image_view);
1661   if (status == MagickFalse)
1662     splice_image=DestroyImage(splice_image);
1663   return(splice_image);
1664 }
1665 \f
1666 /*
1667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1668 %                                                                             %
1669 %                                                                             %
1670 %                                                                             %
1671 %   T r a n s f o r m I m a g e                                               %
1672 %                                                                             %
1673 %                                                                             %
1674 %                                                                             %
1675 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1676 %
1677 %  TransformImage() is a convenience method that behaves like ResizeImage() or
1678 %  CropImage() but accepts scaling and/or cropping information as a region
1679 %  geometry specification.  If the operation fails, the original image handle
1680 %  is returned.
1681 %
1682 %  The format of the TransformImage method is:
1683 %
1684 %      MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
1685 %        const char *image_geometry)
1686 %
1687 %  A description of each parameter follows:
1688 %
1689 %    o image: the image The transformed image is returned as this parameter.
1690 %
1691 %    o crop_geometry: A crop geometry string.  This geometry defines a
1692 %      subregion of the image to crop.
1693 %
1694 %    o image_geometry: An image geometry string.  This geometry defines the
1695 %      final size of the image.
1696 %
1697 */
1698 static inline ssize_t MagickRound(MagickRealType x)
1699 {
1700   /*
1701     Round the fraction to nearest integer.
1702   */
1703   if (x >= 0.0)
1704     return((ssize_t) (x+0.5));
1705   return((ssize_t) (x-0.5));
1706 }
1707
1708 MagickExport MagickBooleanType TransformImage(Image **image,
1709   const char *crop_geometry,const char *image_geometry)
1710 {
1711   Image
1712     *next,
1713     *resize_image,
1714     *transform_image;
1715
1716   ssize_t
1717     x,
1718     y;
1719
1720   MagickStatusType
1721     flags;
1722
1723   RectangleInfo
1724     geometry;
1725
1726   size_t
1727     height,
1728     width;
1729
1730   assert(image != (Image **) NULL);
1731   assert((*image)->signature == MagickSignature);
1732   if ((*image)->debug != MagickFalse)
1733     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
1734   transform_image=(*image);
1735   if (crop_geometry != (const char *) NULL)
1736     {
1737       Image
1738         *crop_image;
1739
1740       RectangleInfo
1741         geometry;
1742
1743       /*
1744         Crop image to a user specified size.
1745       */
1746       crop_image=NewImageList();
1747       flags=ParseGravityGeometry(transform_image,crop_geometry,&geometry,
1748         &(*image)->exception);
1749       if ((flags & AreaValue) != 0)
1750         {
1751           PointInfo
1752             delta,
1753             offset;
1754
1755           RectangleInfo
1756             crop;
1757
1758           /*
1759             Crop into NxM tiles (@ flag) - AT.
1760           */
1761           if (geometry.width == 0)
1762             geometry.width=1;
1763           if (geometry.height == 0)
1764             geometry.height=1;
1765           width=transform_image->columns;
1766           height=transform_image->rows;
1767           if ((flags & AspectValue) == 0)
1768             {
1769               width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
1770               height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
1771             }
1772           else
1773             {
1774               width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
1775               height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
1776             }
1777           delta.x=(double) width/geometry.width;
1778           delta.y=(double) height/geometry.height;
1779           next=NewImageList();
1780           for (offset.y=0; offset.y < (double) height; )
1781           {
1782             if ((flags & AspectValue) == 0)
1783               {
1784                 crop.y=(ssize_t) MagickRound((MagickRealType) (offset.y-
1785                   (geometry.y > 0 ? 0 : geometry.y)));
1786                 offset.y+=delta.y;
1787                 crop.height=(size_t) MagickRound((MagickRealType)
1788                   (offset.y+(geometry.y < 0 ? 0 : geometry.y)));
1789               }
1790             else
1791               {
1792                 crop.y=(ssize_t) MagickRound((MagickRealType) (offset.y-
1793                   (geometry.y > 0 ? geometry.y : 0)));
1794                 offset.y+=delta.y;
1795                 crop.height=(size_t) MagickRound((MagickRealType)
1796                   (offset.y+(geometry.y < 0 ? geometry.y : 0)));
1797               }
1798             crop.height-=crop.y;
1799             crop.y+=transform_image->page.y;
1800             for (offset.x=0; offset.x < (double) width; )
1801             {
1802               if ((flags & AspectValue) == 0)
1803                 {
1804                   crop.x=(ssize_t) MagickRound((MagickRealType) (offset.x-
1805                     (geometry.x > 0 ? 0 : geometry.x)));
1806                   offset.x+=+delta.x;
1807                   crop.width=(size_t) MagickRound((MagickRealType)
1808                     (offset.x+(geometry.x < 0 ? 0 : geometry.x)));
1809                 }
1810               else
1811                 {
1812                   crop.x=(ssize_t) MagickRound((MagickRealType) (offset.x-
1813                     (geometry.x > 0 ? geometry.x : 0)));
1814                   offset.x+=+delta.x;
1815                   crop.width=(size_t) MagickRound((MagickRealType)
1816                     (offset.x+(geometry.x < 0 ? geometry.x : 0)));
1817                 }
1818               crop.width-=crop.x;
1819               crop.x+=transform_image->page.x;
1820               next=CropImage(transform_image,&crop,&(*image)->exception);
1821               if (next == (Image *) NULL)
1822                 break;
1823               AppendImageToList(&crop_image,next);
1824             }
1825             if (next == (Image *) NULL)
1826               break;
1827           }
1828         }
1829       else
1830         if (((geometry.width == 0) && (geometry.height == 0)) ||
1831             ((flags & XValue) != 0) || ((flags & YValue) != 0))
1832           {
1833             /*
1834               Crop a single region at +X+Y.
1835             */
1836             crop_image=CropImage(transform_image,&geometry,
1837               &(*image)->exception);
1838             if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
1839               {
1840                 crop_image->page.width=geometry.width;
1841                 crop_image->page.height=geometry.height;
1842                 crop_image->page.x-=geometry.x;
1843                 crop_image->page.y-=geometry.y;
1844               }
1845          }
1846        else
1847          if ((transform_image->columns > geometry.width) ||
1848              (transform_image->rows > geometry.height))
1849            {
1850              MagickBooleanType
1851                proceed;
1852
1853              MagickProgressMonitor
1854                progress_monitor;
1855
1856              MagickOffsetType
1857                i;
1858
1859              MagickSizeType
1860                number_images;
1861
1862              RectangleInfo
1863                page;
1864
1865              /*
1866                Crop into tiles of fixed size WxH.
1867              */
1868              if (transform_image->page.width == 0)
1869                transform_image->page.width=transform_image->columns;
1870              if (transform_image->page.height == 0)
1871                transform_image->page.height=transform_image->rows;
1872              width=geometry.width;
1873              if (width == 0)
1874                width=transform_image->page.width;
1875              height=geometry.height;
1876              if (height == 0)
1877                height=transform_image->page.height;
1878              next=NewImageList();
1879              proceed=MagickTrue;
1880              i=0;
1881              number_images=0;
1882              page=transform_image->page;
1883              for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
1884                for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
1885                  number_images++;
1886              for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
1887              {
1888                for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
1889                {
1890                  progress_monitor=SetImageProgressMonitor(transform_image,
1891                    (MagickProgressMonitor) NULL,transform_image->client_data);
1892                  geometry.width=width;
1893                  geometry.height=height;
1894                  geometry.x=x;
1895                  geometry.y=y;
1896                  next=CropImage(transform_image,&geometry,&(*image)->exception);
1897                  (void) SetImageProgressMonitor(transform_image,
1898                    progress_monitor,transform_image->client_data);
1899                  proceed=SetImageProgress(transform_image,CropImageTag,i++,
1900                    number_images);
1901                  if (proceed == MagickFalse)
1902                    break;
1903                  if (next == (Image *) NULL)
1904                    break;
1905                  (void) SetImageProgressMonitor(next,progress_monitor,
1906                    next->client_data);
1907                  if (crop_image == (Image *) NULL)
1908                    crop_image=next;
1909                  else
1910                    {
1911                      next->previous=crop_image;
1912                      crop_image->next=next;
1913                      crop_image=crop_image->next;
1914                    }
1915                }
1916                if (next == (Image *) NULL)
1917                  break;
1918                if (proceed == MagickFalse)
1919                  break;
1920              }
1921            }
1922       if (crop_image == (Image *) NULL)
1923         transform_image=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
1924       else
1925         {
1926           transform_image=DestroyImage(transform_image);
1927           transform_image=GetFirstImageInList(crop_image);
1928         }
1929       *image=transform_image;
1930     }
1931   if (image_geometry == (const char *) NULL)
1932     return(MagickTrue);
1933   /*
1934     Scale image to a user specified size.
1935   */
1936   flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,
1937     &(*image)->exception);
1938   if ((transform_image->columns == geometry.width) &&
1939       (transform_image->rows == geometry.height))
1940     return(MagickTrue);
1941   resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
1942     transform_image->filter,transform_image->blur,&(*image)->exception);
1943   if (resize_image == (Image *) NULL)
1944     return(MagickFalse);
1945   transform_image=DestroyImage(transform_image);
1946   transform_image=resize_image;
1947   *image=transform_image;
1948   return(MagickTrue);
1949 }
1950 \f
1951 /*
1952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1953 %                                                                             %
1954 %                                                                             %
1955 %                                                                             %
1956 %   T r a n s f o r m I m a g e s                                             %
1957 %                                                                             %
1958 %                                                                             %
1959 %                                                                             % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1960 %
1961 %  TransformImages() calls TransformImage() on each image of a sequence.
1962 %
1963 %  The format of the TransformImage method is:
1964 %
1965 %      MagickBooleanType TransformImages(Image **image,
1966 %        const char *crop_geometry,const char *image_geometry)
1967 %
1968 %  A description of each parameter follows:
1969 %
1970 %    o image: the image The transformed image is returned as this parameter.
1971 %
1972 %    o crop_geometry: A crop geometry string.  This geometry defines a
1973 %      subregion of the image to crop.
1974 %
1975 %    o image_geometry: An image geometry string.  This geometry defines the
1976 %      final size of the image.
1977 %
1978 */
1979 MagickExport MagickBooleanType TransformImages(Image **images,
1980   const char *crop_geometry,const char *image_geometry)
1981 {
1982   Image
1983     *image,
1984     **image_list,
1985     *transform_images;
1986
1987   MagickStatusType
1988     status;
1989
1990   register ssize_t
1991     i;
1992
1993   assert(images != (Image **) NULL);
1994   assert((*images)->signature == MagickSignature);
1995   if ((*images)->debug != MagickFalse)
1996     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1997       (*images)->filename);
1998   image_list=ImageListToArray(*images,&(*images)->exception);
1999   if (image_list == (Image **) NULL)
2000     return(MagickFalse);
2001   status=MagickTrue;
2002   transform_images=NewImageList();
2003   for (i=0; image_list[i] != (Image *) NULL; i++)
2004   {
2005     image=image_list[i];
2006     status|=TransformImage(&image,crop_geometry,image_geometry);
2007     AppendImageToList(&transform_images,image);
2008   }
2009   *images=transform_images;
2010   image_list=(Image **) RelinquishMagickMemory(image_list);
2011   return(status != 0 ? MagickTrue : MagickFalse);
2012 }
2013 \f
2014 /*
2015 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2016 %                                                                             %
2017 %                                                                             %
2018 %                                                                             %
2019 %   T r a n s p o s e I m a g e                                               %
2020 %                                                                             %
2021 %                                                                             %
2022 %                                                                             %
2023 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2024 %
2025 %  TransposeImage() creates a horizontal mirror image by reflecting the pixels
2026 %  around the central y-axis while rotating them by 90 degrees.
2027 %
2028 %  The format of the TransposeImage method is:
2029 %
2030 %      Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2031 %
2032 %  A description of each parameter follows:
2033 %
2034 %    o image: the image.
2035 %
2036 %    o exception: return any errors or warnings in this structure.
2037 %
2038 */
2039 MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2040 {
2041 #define TransposeImageTag  "Transpose/Image"
2042
2043   CacheView
2044     *image_view,
2045     *transpose_view;
2046
2047   Image
2048     *transpose_image;
2049
2050   MagickBooleanType
2051     status;
2052
2053   MagickOffsetType
2054     progress;
2055
2056   RectangleInfo
2057     page;
2058
2059   ssize_t
2060     y;
2061
2062   assert(image != (const Image *) NULL);
2063   assert(image->signature == MagickSignature);
2064   if (image->debug != MagickFalse)
2065     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2066   assert(exception != (ExceptionInfo *) NULL);
2067   assert(exception->signature == MagickSignature);
2068   transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2069     exception);
2070   if (transpose_image == (Image *) NULL)
2071     return((Image *) NULL);
2072   /*
2073     Transpose image.
2074   */
2075   status=MagickTrue;
2076   progress=0;
2077   image_view=AcquireCacheView(image);
2078   transpose_view=AcquireCacheView(transpose_image);
2079 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2080   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2081 #endif
2082   for (y=0; y < (ssize_t) image->rows; y++)
2083   {
2084     register const PixelPacket
2085       *restrict p;
2086
2087     register IndexPacket
2088       *restrict transpose_indexes,
2089       *restrict indexes;
2090
2091     register PixelPacket
2092       *restrict q;
2093
2094     if (status == MagickFalse)
2095       continue;
2096     p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
2097       image->columns,1,exception);
2098     q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1),
2099       0,1,transpose_image->rows,exception);
2100     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2101       {
2102         status=MagickFalse;
2103         continue;
2104       }
2105     (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
2106     indexes=GetCacheViewAuthenticIndexQueue(image_view);
2107     if (indexes != (IndexPacket *) NULL)
2108       {
2109         transpose_indexes=GetCacheViewAuthenticIndexQueue(transpose_view);
2110         if (transpose_indexes != (IndexPacket *) NULL)
2111           (void) CopyMagickMemory(transpose_indexes,indexes,(size_t)
2112             image->columns*sizeof(*transpose_indexes));
2113       }
2114     if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2115       status=MagickFalse;
2116     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2117       {
2118         MagickBooleanType
2119           proceed;
2120
2121 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2122   #pragma omp critical (MagickCore_TransposeImage)
2123 #endif
2124         proceed=SetImageProgress(image,TransposeImageTag,progress++,
2125           image->rows);
2126         if (proceed == MagickFalse)
2127           status=MagickFalse;
2128       }
2129   }
2130   transpose_view=DestroyCacheView(transpose_view);
2131   image_view=DestroyCacheView(image_view);
2132   transpose_image->type=image->type;
2133   page=transpose_image->page;
2134   Swap(page.width,page.height);
2135   Swap(page.x,page.y);
2136   if (page.width != 0)
2137     page.x=(ssize_t) (page.width-transpose_image->columns-page.x);
2138   transpose_image->page=page;
2139   if (status == MagickFalse)
2140     transpose_image=DestroyImage(transpose_image);
2141   return(transpose_image);
2142 }
2143 \f
2144 /*
2145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2146 %                                                                             %
2147 %                                                                             %
2148 %                                                                             %
2149 %   T r a n s v e r s e I m a g e                                             %
2150 %                                                                             %
2151 %                                                                             %
2152 %                                                                             %
2153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2154 %
2155 %  TransverseImage() creates a vertical mirror image by reflecting the pixels
2156 %  around the central x-axis while rotating them by 270 degrees.
2157 %
2158 %  The format of the TransverseImage method is:
2159 %
2160 %      Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2161 %
2162 %  A description of each parameter follows:
2163 %
2164 %    o image: the image.
2165 %
2166 %    o exception: return any errors or warnings in this structure.
2167 %
2168 */
2169 MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2170 {
2171 #define TransverseImageTag  "Transverse/Image"
2172
2173   CacheView
2174     *image_view,
2175     *transverse_view;
2176
2177   Image
2178     *transverse_image;
2179
2180   MagickBooleanType
2181     status;
2182
2183   MagickOffsetType
2184     progress;
2185
2186   RectangleInfo
2187     page;
2188
2189   ssize_t
2190     y;
2191
2192   assert(image != (const Image *) NULL);
2193   assert(image->signature == MagickSignature);
2194   if (image->debug != MagickFalse)
2195     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2196   assert(exception != (ExceptionInfo *) NULL);
2197   assert(exception->signature == MagickSignature);
2198   transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2199     exception);
2200   if (transverse_image == (Image *) NULL)
2201     return((Image *) NULL);
2202   /*
2203     Transverse image.
2204   */
2205   status=MagickTrue;
2206   progress=0;
2207   image_view=AcquireCacheView(image);
2208   transverse_view=AcquireCacheView(transverse_image);
2209 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2210   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2211 #endif
2212   for (y=0; y < (ssize_t) image->rows; y++)
2213   {
2214     MagickBooleanType
2215       sync;
2216
2217     register const PixelPacket
2218       *restrict p;
2219
2220     register IndexPacket
2221       *restrict transverse_indexes,
2222       *restrict indexes;
2223
2224     register ssize_t
2225       x;
2226
2227     register PixelPacket
2228       *restrict q;
2229
2230     if (status == MagickFalse)
2231       continue;
2232     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2233     q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-
2234       1),0,1,transverse_image->rows,exception);
2235     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2236       {
2237         status=MagickFalse;
2238         continue;
2239       }
2240     q+=image->columns;
2241     for (x=0; x < (ssize_t) image->columns; x++)
2242       *--q=(*p++);
2243     indexes=GetCacheViewAuthenticIndexQueue(image_view);
2244     if (indexes != (IndexPacket *) NULL)
2245       {
2246         transverse_indexes=GetCacheViewAuthenticIndexQueue(transverse_view);
2247         if (transverse_indexes != (IndexPacket *) NULL)
2248           for (x=0; x < (ssize_t) image->columns; x++)
2249             transverse_indexes[image->columns-x-1]=indexes[x];
2250       }
2251     sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2252     if (sync == MagickFalse)
2253       status=MagickFalse;
2254     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2255       {
2256         MagickBooleanType
2257           proceed;
2258
2259 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2260   #pragma omp critical (MagickCore_TransverseImage)
2261 #endif
2262         proceed=SetImageProgress(image,TransverseImageTag,progress++,
2263           image->rows);
2264         if (proceed == MagickFalse)
2265           status=MagickFalse;
2266       }
2267   }
2268   transverse_view=DestroyCacheView(transverse_view);
2269   image_view=DestroyCacheView(image_view);
2270   transverse_image->type=image->type;
2271   page=transverse_image->page;
2272   Swap(page.width,page.height);
2273   Swap(page.x,page.y);
2274   if (page.height != 0)
2275     page.y=(ssize_t) (page.height-transverse_image->rows-page.y);
2276   transverse_image->page=page;
2277   if (status == MagickFalse)
2278     transverse_image=DestroyImage(transverse_image);
2279   return(transverse_image);
2280 }
2281 \f
2282 /*
2283 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2284 %                                                                             %
2285 %                                                                             %
2286 %                                                                             %
2287 %   T r i m I m a g e                                                         %
2288 %                                                                             %
2289 %                                                                             %
2290 %                                                                             %
2291 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2292 %
2293 %  TrimImage() trims pixels from the image edges.  It allocates the memory
2294 %  necessary for the new Image structure and returns a pointer to the new
2295 %  image.
2296 %
2297 %  The format of the TrimImage method is:
2298 %
2299 %      Image *TrimImage(const Image *image,ExceptionInfo *exception)
2300 %
2301 %  A description of each parameter follows:
2302 %
2303 %    o image: the image.
2304 %
2305 %    o exception: return any errors or warnings in this structure.
2306 %
2307 */
2308 MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2309 {
2310   RectangleInfo
2311     geometry;
2312
2313   assert(image != (const Image *) NULL);
2314   assert(image->signature == MagickSignature);
2315   if (image->debug != MagickFalse)
2316     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2317   geometry=GetImageBoundingBox(image,exception);
2318   if ((geometry.width == 0) || (geometry.height == 0))
2319     {
2320       Image
2321         *crop_image;
2322
2323       crop_image=CloneImage(image,1,1,MagickTrue,exception);
2324       if (crop_image == (Image *) NULL)
2325         return((Image *) NULL);
2326       crop_image->background_color.opacity=(Quantum) TransparentOpacity;
2327       (void) SetImageBackgroundColor(crop_image);
2328       crop_image->page=image->page;
2329       crop_image->page.x=(-1);
2330       crop_image->page.y=(-1);
2331       return(crop_image);
2332     }
2333   geometry.x+=image->page.x;
2334   geometry.y+=image->page.y;
2335   return(CropImage(image,&geometry,exception));
2336 }