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