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