]> granicus.if.org Git - imagemagick/blob - magick/transform.c
Fixed bug in Equal-Size Tile Cropping, when image has a page offset.
[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 IndexPacket
1144       *restrict indexes;
1145
1146     register const PixelPacket
1147       *restrict p;
1148
1149     register IndexPacket
1150       *restrict destination_indexes;
1151
1152     register PixelPacket
1153       *restrict q;
1154
1155     /*
1156       Transfer scanline.
1157     */
1158     if (status == MagickFalse)
1159       continue;
1160     p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1161     q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1162     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1163       {
1164         status=MagickFalse;
1165         continue;
1166       }
1167     indexes=GetCacheViewVirtualIndexQueue(source_view);
1168     (void) CopyMagickMemory(q,p,(size_t) columns*sizeof(*p));
1169     if (indexes != (IndexPacket *) NULL)
1170       {
1171         destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
1172         if (destination_indexes != (IndexPacket *) NULL)
1173           (void) CopyMagickMemory(destination_indexes,indexes,(size_t)
1174             columns*sizeof(*indexes));
1175       }
1176     sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1177     if (sync == MagickFalse)
1178       status=MagickFalse;
1179   }
1180   destination_view=DestroyCacheView(destination_view);
1181   source_view=DestroyCacheView(source_view);
1182   return(status);
1183 }
1184
1185 MagickExport Image *RollImage(const Image *image,const long x_offset,
1186   const long y_offset,ExceptionInfo *exception)
1187 {
1188 #define RollImageTag  "Roll/Image"
1189
1190   Image
1191     *roll_image;
1192
1193   MagickStatusType
1194     status;
1195
1196   RectangleInfo
1197     offset;
1198
1199   /*
1200     Initialize roll image attributes.
1201   */
1202   assert(image != (const Image *) NULL);
1203   assert(image->signature == MagickSignature);
1204   if (image->debug != MagickFalse)
1205     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1206   assert(exception != (ExceptionInfo *) NULL);
1207   assert(exception->signature == MagickSignature);
1208   roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1209   if (roll_image == (Image *) NULL)
1210     return((Image *) NULL);
1211   offset.x=x_offset;
1212   offset.y=y_offset;
1213   while (offset.x < 0)
1214     offset.x+=image->columns;
1215   while (offset.x >= (long) image->columns)
1216     offset.x-=image->columns;
1217   while (offset.y < 0)
1218     offset.y+=image->rows;
1219   while (offset.y >= (long) image->rows)
1220     offset.y-=image->rows;
1221   /*
1222     Roll image.
1223   */
1224   status=CopyImageRegion(roll_image,image,(unsigned long) offset.x,
1225     (unsigned long) offset.y,(long) image->columns-offset.x,(long) image->rows-
1226     offset.y,0,0,exception);
1227   (void) SetImageProgress(image,RollImageTag,0,3);
1228   status|=CopyImageRegion(roll_image,image,image->columns-offset.x,
1229     (unsigned long) offset.y,0,(long) image->rows-offset.y,offset.x,0,
1230     exception);
1231   (void) SetImageProgress(image,RollImageTag,1,3);
1232   status|=CopyImageRegion(roll_image,image,(unsigned long) offset.x,image->rows-
1233     offset.y,(long) image->columns-offset.x,0,0,offset.y,exception);
1234   (void) SetImageProgress(image,RollImageTag,2,3);
1235   status|=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
1236     offset.y,0,0,offset.x,offset.y,exception);
1237   (void) SetImageProgress(image,RollImageTag,3,3);
1238   roll_image->type=image->type;
1239   if (status == MagickFalse)
1240     roll_image=DestroyImage(roll_image);
1241   return(roll_image);
1242 }
1243 \f
1244 /*
1245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1246 %                                                                             %
1247 %                                                                             %
1248 %                                                                             %
1249 %   S h a v e I m a g e                                                       %
1250 %                                                                             %
1251 %                                                                             %
1252 %                                                                             %
1253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254 %
1255 %  ShaveImage() shaves pixels from the image edges.  It allocates the memory
1256 %  necessary for the new Image structure and returns a pointer to the new
1257 %  image.
1258 %
1259 %  The format of the ShaveImage method is:
1260 %
1261 %      Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1262 %        ExceptionInfo *exception)
1263 %
1264 %  A description of each parameter follows:
1265 %
1266 %    o shave_image: Method ShaveImage returns a pointer to the shaved
1267 %      image.  A null image is returned if there is a memory shortage or
1268 %      if the image width or height is zero.
1269 %
1270 %    o image: the image.
1271 %
1272 %    o shave_info: Specifies a pointer to a RectangleInfo which defines the
1273 %      region of the image to crop.
1274 %
1275 %    o exception: return any errors or warnings in this structure.
1276 %
1277 */
1278 MagickExport Image *ShaveImage(const Image *image,
1279   const RectangleInfo *shave_info,ExceptionInfo *exception)
1280 {
1281   Image
1282     *shave_image;
1283
1284   RectangleInfo
1285     geometry;
1286
1287   assert(image != (const Image *) NULL);
1288   assert(image->signature == MagickSignature);
1289   if (image->debug != MagickFalse)
1290     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1291   if (((2*shave_info->width) >= image->columns) ||
1292       ((2*shave_info->height) >= image->rows))
1293     ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1294   SetGeometry(image,&geometry);
1295   geometry.width-=2*shave_info->width;
1296   geometry.height-=2*shave_info->height;
1297   geometry.x=(long) shave_info->width+image->page.x;
1298   geometry.y=(long) shave_info->height+image->page.y;
1299   shave_image=CropImage(image,&geometry,exception);
1300   if (shave_image == (Image *) NULL)
1301     return((Image *) NULL);
1302   shave_image->page.width-=2*shave_info->width;
1303   shave_image->page.height-=2*shave_info->height;
1304   shave_image->page.x-=shave_info->width;
1305   shave_image->page.y-=shave_info->height;
1306   return(shave_image);
1307 }
1308 \f
1309 /*
1310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1311 %                                                                             %
1312 %                                                                             %
1313 %                                                                             %
1314 %   S p l i c e I m a g e                                                     %
1315 %                                                                             %
1316 %                                                                             %
1317 %                                                                             %
1318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1319 %
1320 %  SpliceImage() splices a solid color into the image as defined by the
1321 %  geometry.
1322 %
1323 %  The format of the SpliceImage method is:
1324 %
1325 %      Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1326 %        ExceptionInfo *exception)
1327 %
1328 %  A description of each parameter follows:
1329 %
1330 %    o image: the image.
1331 %
1332 %    o geometry: Define the region of the image to splice with members
1333 %      x, y, width, and height.
1334 %
1335 %    o exception: return any errors or warnings in this structure.
1336 %
1337 */
1338 MagickExport Image *SpliceImage(const Image *image,
1339   const RectangleInfo *geometry,ExceptionInfo *exception)
1340 {
1341 #define SpliceImageTag  "Splice/Image"
1342
1343   CacheView
1344     *image_view,
1345     *splice_view;
1346
1347   Image
1348     *splice_image;
1349
1350   long
1351     progress,
1352     y;
1353
1354   MagickBooleanType
1355     proceed,
1356     status;
1357
1358   RectangleInfo
1359     splice_geometry;
1360
1361   register long
1362     i;
1363
1364   /*
1365     Allocate splice image.
1366   */
1367   assert(image != (const Image *) NULL);
1368   assert(image->signature == MagickSignature);
1369   if (image->debug != MagickFalse)
1370     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1371   assert(geometry != (const RectangleInfo *) NULL);
1372   assert(exception != (ExceptionInfo *) NULL);
1373   assert(exception->signature == MagickSignature);
1374   splice_geometry=(*geometry);
1375   splice_image=CloneImage(image,image->columns+splice_geometry.width,
1376     image->rows+splice_geometry.height,MagickTrue,exception);
1377   if (splice_image == (Image *) NULL)
1378     return((Image *) NULL);
1379   if (SetImageStorageClass(splice_image,DirectClass) == MagickFalse)
1380     {
1381       InheritException(exception,&splice_image->exception);
1382       splice_image=DestroyImage(splice_image);
1383       return((Image *) NULL);
1384     }
1385   (void) SetImageBackgroundColor(splice_image);
1386   /*
1387     Respect image geometry.
1388   */
1389   switch (image->gravity)
1390   {
1391     default:
1392     case UndefinedGravity:
1393     case NorthWestGravity:
1394       break;
1395     case NorthGravity:
1396     {
1397       splice_geometry.x+=splice_geometry.width/2;
1398       break;
1399     }
1400     case NorthEastGravity:
1401     {
1402       splice_geometry.x+=splice_geometry.width;
1403       break;
1404     }
1405     case WestGravity:
1406     {
1407       splice_geometry.y+=splice_geometry.width/2;
1408       break;
1409     }
1410     case StaticGravity:
1411     case CenterGravity:
1412     {
1413       splice_geometry.x+=splice_geometry.width/2;
1414       splice_geometry.y+=splice_geometry.height/2;
1415       break;
1416     }
1417     case EastGravity:
1418     {
1419       splice_geometry.x+=splice_geometry.width;
1420       splice_geometry.y+=splice_geometry.height/2;
1421       break;
1422     }
1423     case SouthWestGravity:
1424     {
1425       splice_geometry.y+=splice_geometry.height;
1426       break;
1427     }
1428     case SouthGravity:
1429     {
1430       splice_geometry.x+=splice_geometry.width/2;
1431       splice_geometry.y+=splice_geometry.height;
1432       break;
1433     }
1434     case SouthEastGravity:
1435     {
1436       splice_geometry.x+=splice_geometry.width;
1437       splice_geometry.y+=splice_geometry.height;
1438       break;
1439     }
1440   }
1441   /*
1442     Splice image.
1443   */
1444   status=MagickTrue;
1445   i=0;
1446   progress=0;
1447   image_view=AcquireCacheView(image);
1448   splice_view=AcquireCacheView(splice_image);
1449 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1450   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1451 #endif
1452   for (y=0; y < (long) splice_geometry.y; y++)
1453   {
1454     register const PixelPacket
1455       *restrict p;
1456
1457     register IndexPacket
1458       *restrict indexes,
1459       *restrict splice_indexes;
1460
1461     register long
1462       x;
1463
1464     register PixelPacket
1465       *restrict q;
1466
1467     if (status == MagickFalse)
1468       continue;
1469     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1470     q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1471       exception);
1472     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1473       {
1474         status=MagickFalse;
1475         continue;
1476       }
1477     indexes=GetCacheViewAuthenticIndexQueue(image_view);
1478     splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1479     for (x=0; x < splice_geometry.x; x++)
1480     {
1481       SetRedPixelComponent(q,GetRedPixelComponent(p));
1482       SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1483       SetBluePixelComponent(q,GetBluePixelComponent(p));
1484       SetOpacityPixelComponent(q,OpaqueOpacity);
1485       if (image->matte != MagickFalse)
1486         SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
1487       if (image->colorspace == CMYKColorspace)
1488         splice_indexes[x]=(*indexes++);
1489       p++;
1490       q++;
1491     }
1492     for ( ; x < (long) (splice_geometry.x+splice_geometry.width); x++)
1493       q++;
1494     for ( ; x < (long) splice_image->columns; x++)
1495     {
1496       SetRedPixelComponent(q,GetRedPixelComponent(p));
1497       SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1498       SetBluePixelComponent(q,GetBluePixelComponent(p));
1499       SetOpacityPixelComponent(q,OpaqueOpacity);
1500       if (image->matte != MagickFalse)
1501         SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
1502       if (image->colorspace == CMYKColorspace)
1503         splice_indexes[x]=(*indexes++);
1504       p++;
1505       q++;
1506     }
1507     if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1508       status=MagickFalse;
1509     proceed=SetImageProgress(image,SpliceImageTag,y,splice_image->rows);
1510     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1511       {
1512         MagickBooleanType
1513           proceed;
1514
1515 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1516   #pragma omp critical (MagickCore_TransposeImage)
1517 #endif
1518         proceed=SetImageProgress(image,SpliceImageTag,progress++,
1519           splice_image->rows);
1520         if (proceed == MagickFalse)
1521           status=MagickFalse;
1522       }
1523   }
1524 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1525   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1526 #endif
1527   for (y=(long) (splice_geometry.y+splice_geometry.height);
1528        y < (long) splice_image->rows; y++)
1529   {
1530     register const PixelPacket
1531       *restrict p;
1532
1533     register IndexPacket
1534       *restrict indexes,
1535       *restrict splice_indexes;
1536
1537     register long
1538       x;
1539
1540     register PixelPacket
1541       *restrict q;
1542
1543     if (status == MagickFalse)
1544       continue;
1545     p=GetCacheViewVirtualPixels(image_view,0,y-splice_geometry.height,
1546       image->columns,1,exception);
1547     q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1548       exception);
1549     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1550       {
1551         status=MagickFalse;
1552         continue;
1553       }
1554     indexes=GetCacheViewAuthenticIndexQueue(image_view);
1555     splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1556     for (x=0; x < splice_geometry.x; x++)
1557     {
1558       SetRedPixelComponent(q,GetRedPixelComponent(p));
1559       SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1560       SetBluePixelComponent(q,GetBluePixelComponent(p));
1561       SetOpacityPixelComponent(q,OpaqueOpacity);
1562       if (image->matte != MagickFalse)
1563         SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
1564       if (image->colorspace == CMYKColorspace)
1565         splice_indexes[x]=(*indexes++);
1566       p++;
1567       q++;
1568     }
1569     for ( ; x < (long) (splice_geometry.x+splice_geometry.width); x++)
1570       q++;
1571     for ( ; x < (long) splice_image->columns; x++)
1572     {
1573       SetRedPixelComponent(q,GetRedPixelComponent(p));
1574       SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1575       SetBluePixelComponent(q,GetBluePixelComponent(p));
1576       SetOpacityPixelComponent(q,OpaqueOpacity);
1577       if (image->matte != MagickFalse)
1578         SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
1579       if (image->colorspace == CMYKColorspace)
1580         splice_indexes[x]=(*indexes++);
1581       p++;
1582       q++;
1583     }
1584     if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1585       status=MagickFalse;
1586     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1587       {
1588         MagickBooleanType
1589           proceed;
1590
1591 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1592   #pragma omp critical (MagickCore_TransposeImage)
1593 #endif
1594         proceed=SetImageProgress(image,SpliceImageTag,progress++,
1595           splice_image->rows);
1596         if (proceed == MagickFalse)
1597           status=MagickFalse;
1598       }
1599   }
1600   splice_view=DestroyCacheView(splice_view);
1601   image_view=DestroyCacheView(image_view);
1602   if (status == MagickFalse)
1603     splice_image=DestroyImage(splice_image);
1604   return(splice_image);
1605 }
1606 \f
1607 /*
1608 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1609 %                                                                             %
1610 %                                                                             %
1611 %                                                                             %
1612 %   T r a n s f o r m I m a g e                                               %
1613 %                                                                             %
1614 %                                                                             %
1615 %                                                                             %
1616 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1617 %
1618 %  TransformImage() is a convenience method that behaves like ResizeImage() or
1619 %  CropImage() but accepts scaling and/or cropping information as a region
1620 %  geometry specification.  If the operation fails, the original image handle
1621 %  is returned.
1622 %
1623 %  The format of the TransformImage method is:
1624 %
1625 %      MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
1626 %        const char *image_geometry)
1627 %
1628 %  A description of each parameter follows:
1629 %
1630 %    o image: the image The transformed image is returned as this parameter.
1631 %
1632 %    o crop_geometry: A crop geometry string.  This geometry defines a
1633 %      subregion of the image to crop.
1634 %
1635 %    o image_geometry: An image geometry string.  This geometry defines the
1636 %      final size of the image.
1637 %
1638 */
1639 static inline long MagickRound(MagickRealType x)
1640 {
1641   /*
1642     Round the fraction to nearest integer.
1643   */
1644   if (x >= 0.0)
1645     return((long) (x+0.5));
1646   return((long) (x-0.5));
1647 }
1648
1649 MagickExport MagickBooleanType TransformImage(Image **image,
1650   const char *crop_geometry,const char *image_geometry)
1651 {
1652   Image
1653     *next,
1654     *resize_image,
1655     *transform_image;
1656
1657   long
1658     x,
1659     y;
1660
1661   MagickStatusType
1662     flags;
1663
1664   RectangleInfo
1665     geometry;
1666
1667   unsigned long
1668     height,
1669     width;
1670
1671   assert(image != (Image **) NULL);
1672   assert((*image)->signature == MagickSignature);
1673   if ((*image)->debug != MagickFalse)
1674     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
1675   transform_image=(*image);
1676   if (crop_geometry != (const char *) NULL)
1677     {
1678       Image
1679         *crop_image;
1680
1681       RectangleInfo
1682         geometry;
1683
1684       /*
1685         Crop image to a user specified size.
1686       */
1687       crop_image=NewImageList();
1688       flags=ParseGravityGeometry(transform_image,crop_geometry,&geometry,
1689         &(*image)->exception);
1690       if ((flags & AreaValue) != 0)
1691         {
1692           PointInfo
1693             delta,
1694             offset;
1695
1696           RectangleInfo
1697             crop;
1698
1699           /*
1700             Crop into NxM tiles (@ flag) - AT.
1701           */
1702           if (geometry.width == 0)
1703             geometry.width=1;
1704           if (geometry.height == 0)
1705             geometry.height=1;
1706           width=transform_image->columns;
1707           height=transform_image->rows;
1708           if ((flags & AspectValue) == 0)
1709             {
1710               width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
1711               height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
1712             }
1713           else
1714             {
1715               width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
1716               height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
1717             }
1718           delta.x=(double) width/geometry.width;
1719           delta.y=(double) height/geometry.height;
1720           next=NewImageList();
1721           for (offset.y=0; offset.y < (double) height; )
1722           {
1723             if ((flags & AspectValue) == 0)
1724               {
1725                 crop.y=(long) MagickRound((MagickRealType) (offset.y-
1726                   (geometry.y > 0 ? 0 : geometry.y)));
1727                 offset.y+=delta.y;
1728                 crop.height=(unsigned long) MagickRound((MagickRealType)
1729                   (offset.y+(geometry.y < 0 ? 0 : geometry.y)));
1730               }
1731             else
1732               {
1733                 crop.y=(long) MagickRound((MagickRealType) (offset.y-
1734                   (geometry.y > 0 ? geometry.y : 0)));
1735                 offset.y+=delta.y;
1736                 crop.height=(unsigned long) MagickRound((MagickRealType)
1737                   (offset.y+(geometry.y < 0 ? geometry.y : 0)));
1738               }
1739             crop.height-=crop.y;
1740             crop.y += transform_image->page.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               crop.x += transform_image->page.x;
1761               next=CropImage(transform_image,&crop,&(*image)->exception);
1762               if (next == (Image *) NULL)
1763                 break;
1764               AppendImageToList(&crop_image,next);
1765             }
1766             if (next == (Image *) NULL)
1767               break;
1768           }
1769         }
1770       else
1771         if (((geometry.width == 0) && (geometry.height == 0)) ||
1772             ((flags & XValue) != 0) || ((flags & YValue) != 0))
1773           {
1774             /*
1775               Crop a single region at +X+Y.
1776             */
1777             crop_image=CropImage(transform_image,&geometry,
1778               &(*image)->exception);
1779             if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
1780               {
1781                 crop_image->page.width=geometry.width;
1782                 crop_image->page.height=geometry.height;
1783                 crop_image->page.x-=geometry.x;
1784                 crop_image->page.y-=geometry.y;
1785               }
1786          }
1787        else
1788          if ((transform_image->columns > geometry.width) ||
1789              (transform_image->rows > geometry.height))
1790            {
1791              MagickBooleanType
1792                proceed;
1793
1794              MagickProgressMonitor
1795                progress_monitor;
1796
1797              MagickOffsetType
1798                i;
1799
1800              MagickSizeType
1801                number_images;
1802
1803              /*
1804                Crop into tiles of fixed size WxH.
1805              */
1806              if (transform_image->page.width == 0)
1807                transform_image->page.width=transform_image->columns;
1808              if (transform_image->page.height == 0)
1809                transform_image->page.height=transform_image->rows;
1810              width=geometry.width;
1811              if (width == 0)
1812                width=transform_image->page.width;
1813              height=geometry.height;
1814              if (height == 0)
1815                height=transform_image->page.height;
1816              next=NewImageList();
1817              proceed=MagickTrue;
1818              i=0;
1819              number_images=0;
1820              for (y=0; y < (long) transform_image->page.height; y+=height)
1821                for (x=0; x < (long) transform_image->page.width; x+=width)
1822                  number_images++;
1823              for (y=0; y < (long) transform_image->page.height; y+=height)
1824              {
1825                for (x=0; x < (long) transform_image->page.width; x+=width)
1826                {
1827                  progress_monitor=SetImageProgressMonitor(transform_image,
1828                    (MagickProgressMonitor) NULL,transform_image->client_data);
1829                  geometry.width=width;
1830                  geometry.height=height;
1831                  geometry.x=x;
1832                  geometry.y=y;
1833                  next=CropImage(transform_image,&geometry,&(*image)->exception);
1834                  (void) SetImageProgressMonitor(transform_image,
1835                    progress_monitor,transform_image->client_data);
1836                  proceed=SetImageProgress(transform_image,CropImageTag,i++,
1837                    number_images);
1838                  if (proceed == MagickFalse)
1839                    break;
1840                  if (next == (Image *) NULL)
1841                    break;
1842                  (void) SetImageProgressMonitor(next,progress_monitor,
1843                    next->client_data);
1844                  if (crop_image == (Image *) NULL)
1845                    crop_image=next;
1846                  else
1847                    {
1848                      next->previous=crop_image;
1849                      crop_image->next=next;
1850                      crop_image=crop_image->next;
1851                    }
1852                }
1853                if (next == (Image *) NULL)
1854                  break;
1855                if (proceed == MagickFalse)
1856                  break;
1857              }
1858            }
1859       if (crop_image == (Image *) NULL)
1860         transform_image=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
1861       else
1862         {
1863           transform_image=DestroyImage(transform_image);
1864           transform_image=GetFirstImageInList(crop_image);
1865         }
1866       *image=transform_image;
1867     }
1868   if (image_geometry == (const char *) NULL)
1869     return(MagickTrue);
1870   /*
1871     Scale image to a user specified size.
1872   */
1873   flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,
1874     &(*image)->exception);
1875   if ((transform_image->columns == geometry.width) &&
1876       (transform_image->rows == geometry.height))
1877     return(MagickTrue);
1878   resize_image=ZoomImage(transform_image,geometry.width,geometry.height,
1879     &(*image)->exception);
1880   if (resize_image == (Image *) NULL)
1881     return(MagickFalse);
1882   transform_image=DestroyImage(transform_image);
1883   transform_image=resize_image;
1884   *image=transform_image;
1885   return(MagickTrue);
1886 }
1887 \f
1888 /*
1889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1890 %                                                                             %
1891 %                                                                             %
1892 %                                                                             %
1893 %   T r a n s f o r m I m a g e s                                             %
1894 %                                                                             %
1895 %                                                                             %
1896 %                                                                             % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1897 %
1898 %  TransformImages() calls TransformImage() on each image of a sequence.
1899 %
1900 %  The format of the TransformImage method is:
1901 %
1902 %      MagickBooleanType TransformImages(Image **image,
1903 %        const char *crop_geometry,const char *image_geometry)
1904 %
1905 %  A description of each parameter follows:
1906 %
1907 %    o image: the image The transformed image is returned as this parameter.
1908 %
1909 %    o crop_geometry: A crop geometry string.  This geometry defines a
1910 %      subregion of the image to crop.
1911 %
1912 %    o image_geometry: An image geometry string.  This geometry defines the
1913 %      final size of the image.
1914 %
1915 */
1916 MagickExport MagickBooleanType TransformImages(Image **images,
1917   const char *crop_geometry,const char *image_geometry)
1918 {
1919   Image
1920     *image,
1921     **image_list,
1922     *transform_images;
1923
1924   MagickStatusType
1925     status;
1926
1927   register long
1928     i;
1929
1930   assert(images != (Image **) NULL);
1931   assert((*images)->signature == MagickSignature);
1932   if ((*images)->debug != MagickFalse)
1933     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1934       (*images)->filename);
1935   image_list=ImageListToArray(*images,&(*images)->exception);
1936   if (image_list == (Image **) NULL)
1937     return(MagickFalse);
1938   status=MagickTrue;
1939   transform_images=NewImageList();
1940   for (i=0; image_list[i] != (Image *) NULL; i++)
1941   {
1942     image=image_list[i];
1943     status|=TransformImage(&image,crop_geometry,image_geometry);
1944     AppendImageToList(&transform_images,image);
1945   }
1946   *images=transform_images;
1947   image_list=(Image **) RelinquishMagickMemory(image_list);
1948   return(status != 0 ? MagickTrue : MagickFalse);
1949 }
1950 \f
1951 /*
1952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1953 %                                                                             %
1954 %                                                                             %
1955 %                                                                             %
1956 %   T r a n s p o s e I m a g e                                               %
1957 %                                                                             %
1958 %                                                                             %
1959 %                                                                             %
1960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1961 %
1962 %  TransposeImage() creates a horizontal mirror image by reflecting the pixels
1963 %  around the central y-axis while rotating them by 90 degrees.
1964 %
1965 %  The format of the TransposeImage method is:
1966 %
1967 %      Image *TransposeImage(const Image *image,ExceptionInfo *exception)
1968 %
1969 %  A description of each parameter follows:
1970 %
1971 %    o image: the image.
1972 %
1973 %    o exception: return any errors or warnings in this structure.
1974 %
1975 */
1976 MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
1977 {
1978 #define TransposeImageTag  "Transpose/Image"
1979
1980   CacheView
1981     *image_view,
1982     *transpose_view;
1983
1984   Image
1985     *transpose_image;
1986
1987   long
1988     progress,
1989     y;
1990
1991   MagickBooleanType
1992     status;
1993
1994   RectangleInfo
1995     page;
1996
1997   assert(image != (const Image *) NULL);
1998   assert(image->signature == MagickSignature);
1999   if (image->debug != MagickFalse)
2000     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2001   assert(exception != (ExceptionInfo *) NULL);
2002   assert(exception->signature == MagickSignature);
2003   transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2004     exception);
2005   if (transpose_image == (Image *) NULL)
2006     return((Image *) NULL);
2007   /*
2008     Transpose image.
2009   */
2010   status=MagickTrue;
2011   progress=0;
2012   image_view=AcquireCacheView(image);
2013   transpose_view=AcquireCacheView(transpose_image);
2014 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2015   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2016 #endif
2017   for (y=0; y < (long) image->rows; y++)
2018   {
2019     register const PixelPacket
2020       *restrict p;
2021
2022     register IndexPacket
2023       *restrict transpose_indexes,
2024       *restrict indexes;
2025
2026     register PixelPacket
2027       *restrict q;
2028
2029     if (status == MagickFalse)
2030       continue;
2031     p=GetCacheViewVirtualPixels(image_view,0,(long) image->rows-y-1,
2032       image->columns,1,exception);
2033     q=QueueCacheViewAuthenticPixels(transpose_view,(long) (image->rows-y-1),0,
2034       1,transpose_image->rows,exception);
2035     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2036       {
2037         status=MagickFalse;
2038         continue;
2039       }
2040     (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
2041     indexes=GetCacheViewAuthenticIndexQueue(image_view);
2042     if (indexes != (IndexPacket *) NULL)
2043       {
2044         transpose_indexes=GetCacheViewAuthenticIndexQueue(transpose_view);
2045         if (transpose_indexes != (IndexPacket *) NULL)
2046           (void) CopyMagickMemory(transpose_indexes,indexes,(size_t)
2047             image->columns*sizeof(*transpose_indexes));
2048       }
2049     if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2050       status=MagickFalse;
2051     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2052       {
2053         MagickBooleanType
2054           proceed;
2055
2056 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2057   #pragma omp critical (MagickCore_TransposeImage)
2058 #endif
2059         proceed=SetImageProgress(image,TransposeImageTag,progress++,
2060           image->rows);
2061         if (proceed == MagickFalse)
2062           status=MagickFalse;
2063       }
2064   }
2065   transpose_view=DestroyCacheView(transpose_view);
2066   image_view=DestroyCacheView(image_view);
2067   transpose_image->type=image->type;
2068   page=transpose_image->page;
2069   Swap(page.width,page.height);
2070   Swap(page.x,page.y);
2071   if (page.width != 0)
2072     page.x=(long) (page.width-transpose_image->columns-page.x);
2073   transpose_image->page=page;
2074   if (status == MagickFalse)
2075     transpose_image=DestroyImage(transpose_image);
2076   return(transpose_image);
2077 }
2078 \f
2079 /*
2080 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2081 %                                                                             %
2082 %                                                                             %
2083 %                                                                             %
2084 %   T r a n s v e r s e I m a g e                                             %
2085 %                                                                             %
2086 %                                                                             %
2087 %                                                                             %
2088 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2089 %
2090 %  TransverseImage() creates a vertical mirror image by reflecting the pixels
2091 %  around the central x-axis while rotating them by 270 degrees.
2092 %
2093 %  The format of the TransverseImage method is:
2094 %
2095 %      Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2096 %
2097 %  A description of each parameter follows:
2098 %
2099 %    o image: the image.
2100 %
2101 %    o exception: return any errors or warnings in this structure.
2102 %
2103 */
2104 MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2105 {
2106 #define TransverseImageTag  "Transverse/Image"
2107
2108   CacheView
2109     *image_view,
2110     *transverse_view;
2111
2112   Image
2113     *transverse_image;
2114
2115   long
2116     progress,
2117     y;
2118
2119   MagickBooleanType
2120     status;
2121
2122   RectangleInfo
2123     page;
2124
2125   assert(image != (const Image *) NULL);
2126   assert(image->signature == MagickSignature);
2127   if (image->debug != MagickFalse)
2128     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2129   assert(exception != (ExceptionInfo *) NULL);
2130   assert(exception->signature == MagickSignature);
2131   transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2132     exception);
2133   if (transverse_image == (Image *) NULL)
2134     return((Image *) NULL);
2135   /*
2136     Transverse image.
2137   */
2138   status=MagickTrue;
2139   progress=0;
2140   image_view=AcquireCacheView(image);
2141   transverse_view=AcquireCacheView(transverse_image);
2142 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2143   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2144 #endif
2145   for (y=0; y < (long) image->rows; y++)
2146   {
2147     MagickBooleanType
2148       sync;
2149
2150     register const PixelPacket
2151       *restrict p;
2152
2153     register IndexPacket
2154       *restrict transverse_indexes,
2155       *restrict indexes;
2156
2157     register long
2158       x;
2159
2160     register PixelPacket
2161       *restrict q;
2162
2163     if (status == MagickFalse)
2164       continue;
2165     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2166     q=QueueCacheViewAuthenticPixels(transverse_view,(long) (image->rows-y-
2167       1),0,1,transverse_image->rows,exception);
2168     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2169       {
2170         status=MagickFalse;
2171         continue;
2172       }
2173     q+=image->columns;
2174     for (x=0; x < (long) image->columns; x++)
2175       *--q=(*p++);
2176     indexes=GetCacheViewAuthenticIndexQueue(image_view);
2177     if (indexes != (IndexPacket *) NULL)
2178       {
2179         transverse_indexes=GetCacheViewAuthenticIndexQueue(transverse_view);
2180         if (transverse_indexes != (IndexPacket *) NULL)
2181           for (x=0; x < (long) image->columns; x++)
2182             transverse_indexes[image->columns-x-1]=indexes[x];
2183       }
2184     sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2185     if (sync == MagickFalse)
2186       status=MagickFalse;
2187     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2188       {
2189         MagickBooleanType
2190           proceed;
2191
2192 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2193   #pragma omp critical (MagickCore_TransverseImage)
2194 #endif
2195         proceed=SetImageProgress(image,TransverseImageTag,progress++,
2196           image->rows);
2197         if (proceed == MagickFalse)
2198           status=MagickFalse;
2199       }
2200   }
2201   transverse_view=DestroyCacheView(transverse_view);
2202   image_view=DestroyCacheView(image_view);
2203   transverse_image->type=image->type;
2204   page=transverse_image->page;
2205   Swap(page.width,page.height);
2206   Swap(page.x,page.y);
2207   if (page.height != 0)
2208     page.y=(long) (page.height-transverse_image->rows-page.y);
2209   transverse_image->page=page;
2210   if (status == MagickFalse)
2211     transverse_image=DestroyImage(transverse_image);
2212   return(transverse_image);
2213 }
2214 \f
2215 /*
2216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2217 %                                                                             %
2218 %                                                                             %
2219 %                                                                             %
2220 %   T r i m I m a g e                                                         %
2221 %                                                                             %
2222 %                                                                             %
2223 %                                                                             %
2224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2225 %
2226 %  TrimImage() trims pixels from the image edges.  It allocates the memory
2227 %  necessary for the new Image structure and returns a pointer to the new
2228 %  image.
2229 %
2230 %  The format of the TrimImage method is:
2231 %
2232 %      Image *TrimImage(const Image *image,ExceptionInfo *exception)
2233 %
2234 %  A description of each parameter follows:
2235 %
2236 %    o image: the image.
2237 %
2238 %    o exception: return any errors or warnings in this structure.
2239 %
2240 */
2241 MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2242 {
2243   RectangleInfo
2244     geometry;
2245
2246   assert(image != (const Image *) NULL);
2247   assert(image->signature == MagickSignature);
2248   if (image->debug != MagickFalse)
2249     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2250   geometry=GetImageBoundingBox(image,exception);
2251   if ((geometry.width == 0) || (geometry.height == 0))
2252     {
2253       Image
2254         *crop_image;
2255
2256       crop_image=CloneImage(image,1,1,MagickTrue,exception);
2257       if (crop_image == (Image *) NULL)
2258         return((Image *) NULL);
2259       crop_image->background_color.opacity=(Quantum) TransparentOpacity;
2260       (void) SetImageBackgroundColor(crop_image);
2261       crop_image->page=image->page;
2262       crop_image->page.x=(-1);
2263       crop_image->page.y=(-1);
2264       return(crop_image);
2265     }
2266   geometry.x+=image->page.x;
2267   geometry.y+=image->page.y;
2268   return(CropImage(image,&geometry,exception));
2269 }