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