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