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