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