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