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