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