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