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