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