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