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