2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 %
13 % MagickCore Image Transform Methods %
20 % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % https://imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "MagickCore/studio.h"
43 #include "MagickCore/attribute.h"
44 #include "MagickCore/cache.h"
45 #include "MagickCore/cache-view.h"
46 #include "MagickCore/color.h"
47 #include "MagickCore/color-private.h"
48 #include "MagickCore/colorspace-private.h"
49 #include "MagickCore/composite.h"
50 #include "MagickCore/distort.h"
51 #include "MagickCore/draw.h"
52 #include "MagickCore/effect.h"
53 #include "MagickCore/exception.h"
54 #include "MagickCore/exception-private.h"
55 #include "MagickCore/geometry.h"
56 #include "MagickCore/image.h"
57 #include "MagickCore/memory_.h"
58 #include "MagickCore/layer.h"
59 #include "MagickCore/list.h"
60 #include "MagickCore/monitor.h"
61 #include "MagickCore/monitor-private.h"
62 #include "MagickCore/pixel-accessor.h"
63 #include "MagickCore/resource_.h"
64 #include "MagickCore/resize.h"
65 #include "MagickCore/statistic.h"
66 #include "MagickCore/string_.h"
67 #include "MagickCore/thread-private.h"
68 #include "MagickCore/transform.h"
69 #include "MagickCore/transform-private.h"
72 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76 % A u t o O r i e n t I m a g e %
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 % AutoOrientImage() adjusts an image so that its orientation is suitable for
83 % viewing (i.e. top-left orientation).
85 % The format of the AutoOrientImage method is:
87 % Image *AutoOrientImage(const Image *image,
88 % const OrientationType orientation,ExceptionInfo *exception)
90 % A description of each parameter follows:
94 % o orientation: Current image orientation.
96 % o exception: Return any errors or warnings in this structure.
99 MagickExport Image *AutoOrientImage(const Image *image,
100 const OrientationType orientation,ExceptionInfo *exception)
105 assert(image != (const Image *) NULL);
106 assert(image->signature == MagickCoreSignature);
107 assert(exception != (ExceptionInfo *) NULL);
108 assert(exception->signature == MagickCoreSignature);
109 orient_image=(Image *) NULL;
112 case UndefinedOrientation:
113 case TopLeftOrientation:
116 orient_image=CloneImage(image,0,0,MagickTrue,exception);
119 case TopRightOrientation:
121 orient_image=FlopImage(image,exception);
124 case BottomRightOrientation:
126 orient_image=RotateImage(image,180.0,exception);
129 case BottomLeftOrientation:
131 orient_image=FlipImage(image,exception);
134 case LeftTopOrientation:
136 orient_image=TransposeImage(image,exception);
139 case RightTopOrientation:
141 orient_image=RotateImage(image,90.0,exception);
144 case RightBottomOrientation:
146 orient_image=TransverseImage(image,exception);
149 case LeftBottomOrientation:
151 orient_image=RotateImage(image,270.0,exception);
155 if (orient_image != (Image *) NULL)
156 orient_image->orientation=TopLeftOrientation;
157 return(orient_image);
161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
165 % C h o p I m a g e %
169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171 % ChopImage() removes a region of an image and collapses the image to occupy
172 % the removed portion.
174 % The format of the ChopImage method is:
176 % Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
177 % ExceptionInfo *exception)
179 % A description of each parameter follows:
181 % o image: the image.
183 % o chop_info: Define the region of the image to chop.
185 % o exception: return any errors or warnings in this structure.
188 MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
189 ExceptionInfo *exception)
191 #define ChopImageTag "Chop/Image"
215 assert(image != (const Image *) NULL);
216 assert(image->signature == MagickCoreSignature);
217 if (image->debug != MagickFalse)
218 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
219 assert(exception != (ExceptionInfo *) NULL);
220 assert(exception->signature == MagickCoreSignature);
221 assert(chop_info != (RectangleInfo *) NULL);
222 if (((chop_info->x+(ssize_t) chop_info->width) < 0) ||
223 ((chop_info->y+(ssize_t) chop_info->height) < 0) ||
224 (chop_info->x > (ssize_t) image->columns) ||
225 (chop_info->y > (ssize_t) image->rows))
226 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
228 if ((extent.x+(ssize_t) extent.width) > (ssize_t) image->columns)
229 extent.width=(size_t) ((ssize_t) image->columns-extent.x);
230 if ((extent.y+(ssize_t) extent.height) > (ssize_t) image->rows)
231 extent.height=(size_t) ((ssize_t) image->rows-extent.y);
234 extent.width-=(size_t) (-extent.x);
239 extent.height-=(size_t) (-extent.y);
242 chop_image=CloneImage(image,image->columns-extent.width,image->rows-
243 extent.height,MagickTrue,exception);
244 if (chop_image == (Image *) NULL)
245 return((Image *) NULL);
251 image_view=AcquireVirtualCacheView(image,exception);
252 chop_view=AcquireAuthenticCacheView(chop_image,exception);
253 #if defined(MAGICKCORE_OPENMP_SUPPORT)
254 #pragma omp parallel for schedule(static) shared(status) \
255 magick_number_threads(image,chop_image,extent.y,1)
257 for (y=0; y < (ssize_t) extent.y; y++)
259 register const Quantum
268 if (status == MagickFalse)
270 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
271 q=QueueCacheViewAuthenticPixels(chop_view,0,y,chop_image->columns,1,
273 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
278 for (x=0; x < (ssize_t) image->columns; x++)
280 if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
285 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
287 PixelChannel channel = GetPixelChannelChannel(image,i);
288 PixelTrait traits = GetPixelChannelTraits(image,channel);
289 PixelTrait chop_traits=GetPixelChannelTraits(chop_image,channel);
290 if ((traits == UndefinedPixelTrait) ||
291 (chop_traits == UndefinedPixelTrait))
293 SetPixelChannel(chop_image,channel,p[i],q);
295 q+=GetPixelChannels(chop_image);
297 p+=GetPixelChannels(image);
299 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
301 if (image->progress_monitor != (MagickProgressMonitor) NULL)
306 #if defined(MAGICKCORE_OPENMP_SUPPORT)
310 proceed=SetImageProgress(image,ChopImageTag,progress,image->rows);
311 if (proceed == MagickFalse)
318 #if defined(MAGICKCORE_OPENMP_SUPPORT)
319 #pragma omp parallel for schedule(static) shared(progress,status) \
320 magick_number_threads(image,chop_image,image->rows-(extent.y+extent.height),1)
322 for (y=0; y < (ssize_t) (image->rows-(extent.y+extent.height)); y++)
324 register const Quantum
333 if (status == MagickFalse)
335 p=GetCacheViewVirtualPixels(image_view,0,extent.y+extent.height+y,
336 image->columns,1,exception);
337 q=QueueCacheViewAuthenticPixels(chop_view,0,extent.y+y,chop_image->columns,
339 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
344 for (x=0; x < (ssize_t) image->columns; x++)
346 if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
351 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
353 PixelChannel channel = GetPixelChannelChannel(image,i);
354 PixelTrait traits = GetPixelChannelTraits(image,channel);
355 PixelTrait chop_traits=GetPixelChannelTraits(chop_image,channel);
356 if ((traits == UndefinedPixelTrait) ||
357 (chop_traits == UndefinedPixelTrait))
359 SetPixelChannel(chop_image,channel,p[i],q);
361 q+=GetPixelChannels(chop_image);
363 p+=GetPixelChannels(image);
365 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
367 if (image->progress_monitor != (MagickProgressMonitor) NULL)
372 #if defined(MAGICKCORE_OPENMP_SUPPORT)
376 proceed=SetImageProgress(image,ChopImageTag,progress,image->rows);
377 if (proceed == MagickFalse)
381 chop_view=DestroyCacheView(chop_view);
382 image_view=DestroyCacheView(image_view);
383 chop_image->type=image->type;
384 if (status == MagickFalse)
385 chop_image=DestroyImage(chop_image);
390 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
394 + C o n s o l i d a t e C M Y K I m a g e %
398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400 % ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
403 % The format of the ConsolidateCMYKImage method is:
405 % Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
407 % A description of each parameter follows:
409 % o image: the image sequence.
411 % o exception: return any errors or warnings in this structure.
414 MagickExport Image *ConsolidateCMYKImages(const Image *images,
415 ExceptionInfo *exception)
432 Consolidate separate C, M, Y, and K planes into a single image.
434 assert(images != (Image *) NULL);
435 assert(images->signature == MagickCoreSignature);
436 if (images->debug != MagickFalse)
437 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
438 assert(exception != (ExceptionInfo *) NULL);
439 assert(exception->signature == MagickCoreSignature);
440 cmyk_images=NewImageList();
441 for (j=0; j < (ssize_t) GetImageListLength(images); j+=4)
446 assert(images != (Image *) NULL);
447 cmyk_image=CloneImage(images,0,0,MagickTrue,
449 if (cmyk_image == (Image *) NULL)
451 if (SetImageStorageClass(cmyk_image,DirectClass,exception) == MagickFalse)
453 (void) SetImageColorspace(cmyk_image,CMYKColorspace,exception);
454 for (i=0; i < 4; i++)
456 image_view=AcquireVirtualCacheView(images,exception);
457 cmyk_view=AcquireAuthenticCacheView(cmyk_image,exception);
458 for (y=0; y < (ssize_t) images->rows; y++)
460 register const Quantum
469 p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
470 q=QueueCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
472 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
474 for (x=0; x < (ssize_t) images->columns; x++)
479 pixel=ClampToQuantum(QuantumRange-GetPixelIntensity(images,p));
482 case 0: SetPixelCyan(cmyk_image,pixel,q); break;
483 case 1: SetPixelMagenta(cmyk_image,pixel,q); break;
484 case 2: SetPixelYellow(cmyk_image,pixel,q); break;
485 case 3: SetPixelBlack(cmyk_image,pixel,q); break;
488 p+=GetPixelChannels(images);
489 q+=GetPixelChannels(cmyk_image);
491 if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
494 cmyk_view=DestroyCacheView(cmyk_view);
495 image_view=DestroyCacheView(image_view);
496 images=GetNextImageInList(images);
497 if (images == (Image *) NULL)
500 AppendImageToList(&cmyk_images,cmyk_image);
506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510 % C r o p I m a g e %
514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
516 % CropImage() extracts a region of the image starting at the offset defined
517 % by geometry. Region must be fully defined, and no special handling of
518 % geometry flags is performed.
520 % The format of the CropImage method is:
522 % Image *CropImage(const Image *image,const RectangleInfo *geometry,
523 % ExceptionInfo *exception)
525 % A description of each parameter follows:
527 % o image: the image.
529 % o geometry: Define the region of the image to crop with members
530 % x, y, width, and height.
532 % o exception: return any errors or warnings in this structure.
535 MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
536 ExceptionInfo *exception)
538 #define CropImageTag "Crop/Image"
566 assert(image != (const Image *) NULL);
567 assert(image->signature == MagickCoreSignature);
568 if (image->debug != MagickFalse)
569 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
570 assert(geometry != (const RectangleInfo *) NULL);
571 assert(exception != (ExceptionInfo *) NULL);
572 assert(exception->signature == MagickCoreSignature);
573 bounding_box=image->page;
574 if ((bounding_box.width == 0) || (bounding_box.height == 0))
576 bounding_box.width=image->columns;
577 bounding_box.height=image->rows;
581 page.width=bounding_box.width;
582 if (page.height == 0)
583 page.height=bounding_box.height;
584 if (((bounding_box.x-page.x) >= (ssize_t) page.width) ||
585 ((bounding_box.y-page.y) >= (ssize_t) page.height) ||
586 ((page.x-bounding_box.x) > (ssize_t) image->columns) ||
587 ((page.y-bounding_box.y) > (ssize_t) image->rows))
590 Crop is not within virtual canvas, return 1 pixel transparent image.
592 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
593 "GeometryDoesNotContainImage","`%s'",image->filename);
594 crop_image=CloneImage(image,1,1,MagickTrue,exception);
595 if (crop_image == (Image *) NULL)
596 return((Image *) NULL);
597 crop_image->background_color.alpha=(MagickRealType) TransparentAlpha;
598 crop_image->alpha_trait=BlendPixelTrait;
599 (void) SetImageBackgroundColor(crop_image,exception);
600 crop_image->page=bounding_box;
601 crop_image->page.x=(-1);
602 crop_image->page.y=(-1);
603 if (crop_image->dispose == BackgroundDispose)
604 crop_image->dispose=NoneDispose;
607 if ((page.x < 0) && (bounding_box.x >= 0))
609 page.width+=page.x-bounding_box.x;
614 page.width-=bounding_box.x-page.x;
615 page.x-=bounding_box.x;
619 if ((page.y < 0) && (bounding_box.y >= 0))
621 page.height+=page.y-bounding_box.y;
626 page.height-=bounding_box.y-page.y;
627 page.y-=bounding_box.y;
631 if ((page.x+(ssize_t) page.width) > (ssize_t) image->columns)
632 page.width=image->columns-page.x;
633 if ((geometry->width != 0) && (page.width > geometry->width))
634 page.width=geometry->width;
635 if ((page.y+(ssize_t) page.height) > (ssize_t) image->rows)
636 page.height=image->rows-page.y;
637 if ((geometry->height != 0) && (page.height > geometry->height))
638 page.height=geometry->height;
639 bounding_box.x+=page.x;
640 bounding_box.y+=page.y;
641 if ((page.width == 0) || (page.height == 0))
643 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
644 "GeometryDoesNotContainImage","`%s'",image->filename);
645 return((Image *) NULL);
648 Initialize crop image attributes.
650 crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
651 if (crop_image == (Image *) NULL)
652 return((Image *) NULL);
653 crop_image->page.width=image->page.width;
654 crop_image->page.height=image->page.height;
655 offset.x=(ssize_t) (bounding_box.x+bounding_box.width);
656 offset.y=(ssize_t) (bounding_box.y+bounding_box.height);
657 if ((offset.x > (ssize_t) image->page.width) ||
658 (offset.y > (ssize_t) image->page.height))
660 crop_image->page.width=bounding_box.width;
661 crop_image->page.height=bounding_box.height;
663 crop_image->page.x=bounding_box.x;
664 crop_image->page.y=bounding_box.y;
670 image_view=AcquireVirtualCacheView(image,exception);
671 crop_view=AcquireAuthenticCacheView(crop_image,exception);
672 #if defined(MAGICKCORE_OPENMP_SUPPORT)
673 #pragma omp parallel for schedule(static) shared(status) \
674 magick_number_threads(image,crop_image,crop_image->rows,1)
676 for (y=0; y < (ssize_t) crop_image->rows; y++)
678 register const Quantum
687 if (status == MagickFalse)
689 p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
691 q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
693 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
698 for (x=0; x < (ssize_t) crop_image->columns; x++)
703 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
705 PixelChannel channel = GetPixelChannelChannel(image,i);
706 PixelTrait traits = GetPixelChannelTraits(image,channel);
707 PixelTrait crop_traits=GetPixelChannelTraits(crop_image,channel);
708 if ((traits == UndefinedPixelTrait) ||
709 (crop_traits == UndefinedPixelTrait))
711 SetPixelChannel(crop_image,channel,p[i],q);
713 p+=GetPixelChannels(image);
714 q+=GetPixelChannels(crop_image);
716 if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
718 if (image->progress_monitor != (MagickProgressMonitor) NULL)
723 #if defined(MAGICKCORE_OPENMP_SUPPORT)
727 proceed=SetImageProgress(image,CropImageTag,progress,image->rows);
728 if (proceed == MagickFalse)
732 crop_view=DestroyCacheView(crop_view);
733 image_view=DestroyCacheView(image_view);
734 crop_image->type=image->type;
735 if (status == MagickFalse)
736 crop_image=DestroyImage(crop_image);
741 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
745 % C r o p I m a g e T o T i l e s %
749 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751 % CropImageToTiles() crops a single image, into a possible list of tiles.
752 % This may include a single sub-region of the image. This basically applies
753 % all the normal geometry flags for Crop.
755 % Image *CropImageToTiles(const Image *image,
756 % const RectangleInfo *crop_geometry, ExceptionInfo *exception)
758 % A description of each parameter follows:
760 % o image: the image The transformed image is returned as this parameter.
762 % o crop_geometry: A crop geometry string.
764 % o exception: return any errors or warnings in this structure.
768 static inline double MagickRound(double x)
771 Round the fraction to nearest integer.
773 if ((x-floor(x)) < (ceil(x)-x))
778 MagickExport Image *CropImageToTiles(const Image *image,
779 const char *crop_geometry,ExceptionInfo *exception)
791 assert(image != (Image *) NULL);
792 assert(image->signature == MagickCoreSignature);
793 if (image->debug != MagickFalse)
794 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
795 crop_image=NewImageList();
797 flags=ParseGravityGeometry(image,crop_geometry,&geometry,exception);
798 if ((flags & AreaValue) != 0)
812 Crop into NxM tiles (@ flag).
814 width=image->columns;
816 if (geometry.width == 0)
818 if (geometry.height == 0)
820 if ((flags & AspectValue) == 0)
822 width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
823 height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
827 width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
828 height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
830 delta.x=(double) width/geometry.width;
831 delta.y=(double) height/geometry.height;
836 for (offset.y=0; offset.y < (double) height; )
838 if ((flags & AspectValue) == 0)
840 crop.y=(ssize_t) MagickRound((double) (offset.y-
841 (geometry.y > 0 ? 0 : geometry.y)));
842 offset.y+=delta.y; /* increment now to find width */
843 crop.height=(size_t) MagickRound((double) (offset.y+
844 (geometry.y < 0 ? 0 : geometry.y)));
848 crop.y=(ssize_t) MagickRound((double) (offset.y-
849 (geometry.y > 0 ? geometry.y : 0)));
850 offset.y+=delta.y; /* increment now to find width */
851 crop.height=(size_t) MagickRound((double)
852 (offset.y+(geometry.y < -1 ? geometry.y : 0)));
855 crop.y+=image->page.y;
856 for (offset.x=0; offset.x < (double) width; )
858 if ((flags & AspectValue) == 0)
860 crop.x=(ssize_t) MagickRound((double) (offset.x-
861 (geometry.x > 0 ? 0 : geometry.x)));
862 offset.x+=delta.x; /* increment now to find height */
863 crop.width=(size_t) MagickRound((double) (offset.x+
864 (geometry.x < 0 ? 0 : geometry.x)));
868 crop.x=(ssize_t) MagickRound((double) (offset.x-
869 (geometry.x > 0 ? geometry.x : 0)));
870 offset.x+=delta.x; /* increment now to find height */
871 crop.width=(size_t) MagickRound((double) (offset.x+
872 (geometry.x < 0 ? geometry.x : 0)));
875 crop.x+=image->page.x;
876 next=CropImage(image,&crop,exception);
877 if (next != (Image *) NULL)
878 AppendImageToList(&crop_image,next);
881 ClearMagickException(exception);
884 if (((geometry.width == 0) && (geometry.height == 0)) ||
885 ((flags & XValue) != 0) || ((flags & YValue) != 0))
888 Crop a single region at +X+Y.
890 crop_image=CropImage(image,&geometry,exception);
891 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
893 crop_image->page.width=geometry.width;
894 crop_image->page.height=geometry.height;
895 crop_image->page.x-=geometry.x;
896 crop_image->page.y-=geometry.y;
900 if ((image->columns > geometry.width) || (image->rows > geometry.height))
914 Crop into tiles of fixed size WxH.
918 page.width=image->columns;
919 if (page.height == 0)
920 page.height=image->rows;
921 width=geometry.width;
924 height=geometry.height;
928 for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
930 for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
932 geometry.width=width;
933 geometry.height=height;
936 next=CropImage(image,&geometry,exception);
937 if (next == (Image *) NULL)
939 AppendImageToList(&crop_image,next);
941 if (next == (Image *) NULL)
946 return(CloneImage(image,0,0,MagickTrue,exception));
950 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
954 % E x c e r p t I m a g e %
958 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
960 % ExcerptImage() returns a excerpt of the image as defined by the geometry.
962 % The format of the ExcerptImage method is:
964 % Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
965 % ExceptionInfo *exception)
967 % A description of each parameter follows:
969 % o image: the image.
971 % o geometry: Define the region of the image to extend with members
972 % x, y, width, and height.
974 % o exception: return any errors or warnings in this structure.
977 MagickExport Image *ExcerptImage(const Image *image,
978 const RectangleInfo *geometry,ExceptionInfo *exception)
980 #define ExcerptImageTag "Excerpt/Image"
999 Allocate excerpt image.
1001 assert(image != (const Image *) NULL);
1002 assert(image->signature == MagickCoreSignature);
1003 if (image->debug != MagickFalse)
1004 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1005 assert(geometry != (const RectangleInfo *) NULL);
1006 assert(exception != (ExceptionInfo *) NULL);
1007 assert(exception->signature == MagickCoreSignature);
1008 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1010 if (excerpt_image == (Image *) NULL)
1011 return((Image *) NULL);
1017 image_view=AcquireVirtualCacheView(image,exception);
1018 excerpt_view=AcquireAuthenticCacheView(excerpt_image,exception);
1019 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1020 #pragma omp parallel for schedule(static) shared(progress,status) \
1021 magick_number_threads(image,excerpt_image,excerpt_image->rows,1)
1023 for (y=0; y < (ssize_t) excerpt_image->rows; y++)
1025 register const Quantum
1034 if (status == MagickFalse)
1036 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
1037 geometry->width,1,exception);
1038 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
1040 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1045 for (x=0; x < (ssize_t) excerpt_image->columns; x++)
1050 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1052 PixelChannel channel = GetPixelChannelChannel(image,i);
1053 PixelTrait traits = GetPixelChannelTraits(image,channel);
1054 PixelTrait excerpt_traits=GetPixelChannelTraits(excerpt_image,channel);
1055 if ((traits == UndefinedPixelTrait) ||
1056 (excerpt_traits == UndefinedPixelTrait))
1058 SetPixelChannel(excerpt_image,channel,p[i],q);
1060 p+=GetPixelChannels(image);
1061 q+=GetPixelChannels(excerpt_image);
1063 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
1065 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1070 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1074 proceed=SetImageProgress(image,ExcerptImageTag,progress,image->rows);
1075 if (proceed == MagickFalse)
1079 excerpt_view=DestroyCacheView(excerpt_view);
1080 image_view=DestroyCacheView(image_view);
1081 excerpt_image->type=image->type;
1082 if (status == MagickFalse)
1083 excerpt_image=DestroyImage(excerpt_image);
1084 return(excerpt_image);
1088 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092 % E x t e n t I m a g e %
1096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1098 % ExtentImage() extends the image as defined by the geometry, gravity, and
1099 % image background color. Set the (x,y) offset of the geometry to move the
1100 % original image relative to the extended image.
1102 % The format of the ExtentImage method is:
1104 % Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
1105 % ExceptionInfo *exception)
1107 % A description of each parameter follows:
1109 % o image: the image.
1111 % o geometry: Define the region of the image to extend with members
1112 % x, y, width, and height.
1114 % o exception: return any errors or warnings in this structure.
1117 MagickExport Image *ExtentImage(const Image *image,
1118 const RectangleInfo *geometry,ExceptionInfo *exception)
1127 Allocate extent image.
1129 assert(image != (const Image *) NULL);
1130 assert(image->signature == MagickCoreSignature);
1131 if (image->debug != MagickFalse)
1132 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1133 assert(geometry != (const RectangleInfo *) NULL);
1134 assert(exception != (ExceptionInfo *) NULL);
1135 assert(exception->signature == MagickCoreSignature);
1136 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1138 if (extent_image == (Image *) NULL)
1139 return((Image *) NULL);
1140 status=SetImageBackgroundColor(extent_image,exception);
1141 if (status == MagickFalse)
1143 extent_image=DestroyImage(extent_image);
1144 return((Image *) NULL);
1146 status=CompositeImage(extent_image,image,image->compose,MagickTrue,
1147 -geometry->x,-geometry->y,exception);
1148 return(extent_image);
1152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1156 % F l i p I m a g e %
1160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1162 % FlipImage() creates a vertical mirror image by reflecting the pixels
1163 % around the central x-axis.
1165 % The format of the FlipImage method is:
1167 % Image *FlipImage(const Image *image,ExceptionInfo *exception)
1169 % A description of each parameter follows:
1171 % o image: the image.
1173 % o exception: return any errors or warnings in this structure.
1176 MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1178 #define FlipImageTag "Flip/Image"
1199 assert(image != (const Image *) NULL);
1200 assert(image->signature == MagickCoreSignature);
1201 if (image->debug != MagickFalse)
1202 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1203 assert(exception != (ExceptionInfo *) NULL);
1204 assert(exception->signature == MagickCoreSignature);
1205 flip_image=CloneImage(image,0,0,MagickTrue,exception);
1206 if (flip_image == (Image *) NULL)
1207 return((Image *) NULL);
1214 image_view=AcquireVirtualCacheView(image,exception);
1215 flip_view=AcquireAuthenticCacheView(flip_image,exception);
1216 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1217 #pragma omp parallel for schedule(static) shared(status) \
1218 magick_number_threads(image,flip_image,flip_image->rows,1)
1220 for (y=0; y < (ssize_t) flip_image->rows; y++)
1222 register const Quantum
1231 if (status == MagickFalse)
1233 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1234 q=QueueCacheViewAuthenticPixels(flip_view,0,(ssize_t) (flip_image->rows-y-
1235 1),flip_image->columns,1,exception);
1236 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1241 for (x=0; x < (ssize_t) flip_image->columns; x++)
1246 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1248 PixelChannel channel = GetPixelChannelChannel(image,i);
1249 PixelTrait traits = GetPixelChannelTraits(image,channel);
1250 PixelTrait flip_traits=GetPixelChannelTraits(flip_image,channel);
1251 if ((traits == UndefinedPixelTrait) ||
1252 (flip_traits == UndefinedPixelTrait))
1254 SetPixelChannel(flip_image,channel,p[i],q);
1256 p+=GetPixelChannels(image);
1257 q+=GetPixelChannels(flip_image);
1259 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
1261 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1266 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1270 proceed=SetImageProgress(image,FlipImageTag,progress,image->rows);
1271 if (proceed == MagickFalse)
1275 flip_view=DestroyCacheView(flip_view);
1276 image_view=DestroyCacheView(image_view);
1277 flip_image->type=image->type;
1278 if (page.height != 0)
1279 page.y=(ssize_t) (page.height-flip_image->rows-page.y);
1280 flip_image->page=page;
1281 if (status == MagickFalse)
1282 flip_image=DestroyImage(flip_image);
1287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291 % F l o p I m a g e %
1295 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1297 % FlopImage() creates a horizontal mirror image by reflecting the pixels
1298 % around the central y-axis.
1300 % The format of the FlopImage method is:
1302 % Image *FlopImage(const Image *image,ExceptionInfo *exception)
1304 % A description of each parameter follows:
1306 % o image: the image.
1308 % o exception: return any errors or warnings in this structure.
1311 MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1313 #define FlopImageTag "Flop/Image"
1334 assert(image != (const Image *) NULL);
1335 assert(image->signature == MagickCoreSignature);
1336 if (image->debug != MagickFalse)
1337 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1338 assert(exception != (ExceptionInfo *) NULL);
1339 assert(exception->signature == MagickCoreSignature);
1340 flop_image=CloneImage(image,0,0,MagickTrue,exception);
1341 if (flop_image == (Image *) NULL)
1342 return((Image *) NULL);
1349 image_view=AcquireVirtualCacheView(image,exception);
1350 flop_view=AcquireAuthenticCacheView(flop_image,exception);
1351 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1352 #pragma omp parallel for schedule(static) shared(status) \
1353 magick_number_threads(image,flop_image,flop_image->rows,1)
1355 for (y=0; y < (ssize_t) flop_image->rows; y++)
1357 register const Quantum
1366 if (status == MagickFalse)
1368 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1369 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1371 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1376 q+=GetPixelChannels(flop_image)*flop_image->columns;
1377 for (x=0; x < (ssize_t) flop_image->columns; x++)
1382 q-=GetPixelChannels(flop_image);
1383 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1385 PixelChannel channel = GetPixelChannelChannel(image,i);
1386 PixelTrait traits = GetPixelChannelTraits(image,channel);
1387 PixelTrait flop_traits=GetPixelChannelTraits(flop_image,channel);
1388 if ((traits == UndefinedPixelTrait) ||
1389 (flop_traits == UndefinedPixelTrait))
1391 SetPixelChannel(flop_image,channel,p[i],q);
1393 p+=GetPixelChannels(image);
1395 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1397 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1402 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1406 proceed=SetImageProgress(image,FlopImageTag,progress,image->rows);
1407 if (proceed == MagickFalse)
1411 flop_view=DestroyCacheView(flop_view);
1412 image_view=DestroyCacheView(image_view);
1413 flop_image->type=image->type;
1414 if (page.width != 0)
1415 page.x=(ssize_t) (page.width-flop_image->columns-page.x);
1416 flop_image->page=page;
1417 if (status == MagickFalse)
1418 flop_image=DestroyImage(flop_image);
1423 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1427 % R o l l I m a g e %
1431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1433 % RollImage() offsets an image as defined by x_offset and y_offset.
1435 % The format of the RollImage method is:
1437 % Image *RollImage(const Image *image,const ssize_t x_offset,
1438 % const ssize_t y_offset,ExceptionInfo *exception)
1440 % A description of each parameter follows:
1442 % o image: the image.
1444 % o x_offset: the number of columns to roll in the horizontal direction.
1446 % o y_offset: the number of rows to roll in the vertical direction.
1448 % o exception: return any errors or warnings in this structure.
1452 static MagickBooleanType CopyImageRegion(Image *destination,const Image *source, const size_t columns,const size_t rows,const ssize_t sx,const ssize_t sy,
1453 const ssize_t dx,const ssize_t dy,ExceptionInfo *exception)
1468 source_view=AcquireVirtualCacheView(source,exception);
1469 destination_view=AcquireAuthenticCacheView(destination,exception);
1470 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1471 #pragma omp parallel for schedule(static) shared(status) \
1472 magick_number_threads(source,destination,rows,1)
1474 for (y=0; y < (ssize_t) rows; y++)
1479 register const Quantum
1491 if (status == MagickFalse)
1493 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1494 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1495 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1500 for (x=0; x < (ssize_t) columns; x++)
1505 for (i=0; i < (ssize_t) GetPixelChannels(source); i++)
1507 PixelChannel channel = GetPixelChannelChannel(source,i);
1508 PixelTrait source_traits=GetPixelChannelTraits(source,channel);
1509 PixelTrait destination_traits=GetPixelChannelTraits(destination,
1511 if ((source_traits == UndefinedPixelTrait) ||
1512 (destination_traits == UndefinedPixelTrait))
1514 SetPixelChannel(destination,channel,p[i],q);
1516 p+=GetPixelChannels(source);
1517 q+=GetPixelChannels(destination);
1519 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1520 if (sync == MagickFalse)
1523 destination_view=DestroyCacheView(destination_view);
1524 source_view=DestroyCacheView(source_view);
1528 MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1529 const ssize_t y_offset,ExceptionInfo *exception)
1531 #define RollImageTag "Roll/Image"
1543 Initialize roll image attributes.
1545 assert(image != (const Image *) NULL);
1546 assert(image->signature == MagickCoreSignature);
1547 if (image->debug != MagickFalse)
1548 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1549 assert(exception != (ExceptionInfo *) NULL);
1550 assert(exception->signature == MagickCoreSignature);
1551 roll_image=CloneImage(image,0,0,MagickTrue,exception);
1552 if (roll_image == (Image *) NULL)
1553 return((Image *) NULL);
1556 while (offset.x < 0)
1557 offset.x+=(ssize_t) image->columns;
1558 while (offset.x >= (ssize_t) image->columns)
1559 offset.x-=(ssize_t) image->columns;
1560 while (offset.y < 0)
1561 offset.y+=(ssize_t) image->rows;
1562 while (offset.y >= (ssize_t) image->rows)
1563 offset.y-=(ssize_t) image->rows;
1567 status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1568 (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
1569 offset.y,0,0,exception);
1570 (void) SetImageProgress(image,RollImageTag,0,3);
1571 status&=CopyImageRegion(roll_image,image,image->columns-offset.x,
1572 (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0,
1574 (void) SetImageProgress(image,RollImageTag,1,3);
1575 status&=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows-
1576 offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception);
1577 (void) SetImageProgress(image,RollImageTag,2,3);
1578 status&=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
1579 offset.y,0,0,offset.x,offset.y,exception);
1580 (void) SetImageProgress(image,RollImageTag,3,3);
1581 roll_image->type=image->type;
1582 if (status == MagickFalse)
1583 roll_image=DestroyImage(roll_image);
1588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1592 % S h a v e I m a g e %
1596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1598 % ShaveImage() shaves pixels from the image edges. It allocates the memory
1599 % necessary for the new Image structure and returns a pointer to the new
1602 % The format of the ShaveImage method is:
1604 % Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1605 % ExceptionInfo *exception)
1607 % A description of each parameter follows:
1609 % o shave_image: Method ShaveImage returns a pointer to the shaved
1610 % image. A null image is returned if there is a memory shortage or
1611 % if the image width or height is zero.
1613 % o image: the image.
1615 % o shave_info: Specifies a pointer to a RectangleInfo which defines the
1616 % region of the image to crop.
1618 % o exception: return any errors or warnings in this structure.
1621 MagickExport Image *ShaveImage(const Image *image,
1622 const RectangleInfo *shave_info,ExceptionInfo *exception)
1630 assert(image != (const Image *) NULL);
1631 assert(image->signature == MagickCoreSignature);
1632 if (image->debug != MagickFalse)
1633 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1634 if (((2*shave_info->width) >= image->columns) ||
1635 ((2*shave_info->height) >= image->rows))
1636 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1637 SetGeometry(image,&geometry);
1638 geometry.width-=2*shave_info->width;
1639 geometry.height-=2*shave_info->height;
1640 geometry.x=(ssize_t) shave_info->width+image->page.x;
1641 geometry.y=(ssize_t) shave_info->height+image->page.y;
1642 shave_image=CropImage(image,&geometry,exception);
1643 if (shave_image == (Image *) NULL)
1644 return((Image *) NULL);
1645 shave_image->page.width-=2*shave_info->width;
1646 shave_image->page.height-=2*shave_info->height;
1647 shave_image->page.x-=(ssize_t) shave_info->width;
1648 shave_image->page.y-=(ssize_t) shave_info->height;
1649 return(shave_image);
1653 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1657 % S p l i c e I m a g e %
1661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1663 % SpliceImage() splices a solid color into the image as defined by the
1666 % The format of the SpliceImage method is:
1668 % Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1669 % ExceptionInfo *exception)
1671 % A description of each parameter follows:
1673 % o image: the image.
1675 % o geometry: Define the region of the image to splice with members
1676 % x, y, width, and height.
1678 % o exception: return any errors or warnings in this structure.
1681 MagickExport Image *SpliceImage(const Image *image,
1682 const RectangleInfo *geometry,ExceptionInfo *exception)
1684 #define SpliceImageTag "Splice/Image"
1707 Allocate splice image.
1709 assert(image != (const Image *) NULL);
1710 assert(image->signature == MagickCoreSignature);
1711 if (image->debug != MagickFalse)
1712 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1713 assert(geometry != (const RectangleInfo *) NULL);
1714 assert(exception != (ExceptionInfo *) NULL);
1715 assert(exception->signature == MagickCoreSignature);
1716 splice_geometry=(*geometry);
1717 splice_image=CloneImage(image,image->columns+splice_geometry.width,
1718 image->rows+splice_geometry.height,MagickTrue,exception);
1719 if (splice_image == (Image *) NULL)
1720 return((Image *) NULL);
1721 if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse)
1723 splice_image=DestroyImage(splice_image);
1724 return((Image *) NULL);
1726 if ((IsPixelInfoGray(&splice_image->background_color) == MagickFalse) &&
1727 (IsGrayColorspace(splice_image->colorspace) != MagickFalse))
1728 (void) SetImageColorspace(splice_image,sRGBColorspace,exception);
1729 if ((splice_image->background_color.alpha_trait != UndefinedPixelTrait) &&
1730 (splice_image->alpha_trait == UndefinedPixelTrait))
1731 (void) SetImageAlpha(splice_image,OpaqueAlpha,exception);
1732 (void) SetImageBackgroundColor(splice_image,exception);
1734 Respect image geometry.
1736 switch (image->gravity)
1739 case UndefinedGravity:
1740 case NorthWestGravity:
1744 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1747 case NorthEastGravity:
1749 splice_geometry.x+=(ssize_t) splice_geometry.width;
1754 splice_geometry.y+=(ssize_t) splice_geometry.width/2;
1759 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1760 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1765 splice_geometry.x+=(ssize_t) splice_geometry.width;
1766 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1769 case SouthWestGravity:
1771 splice_geometry.y+=(ssize_t) splice_geometry.height;
1776 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1777 splice_geometry.y+=(ssize_t) splice_geometry.height;
1780 case SouthEastGravity:
1782 splice_geometry.x+=(ssize_t) splice_geometry.width;
1783 splice_geometry.y+=(ssize_t) splice_geometry.height;
1792 columns=MagickMin(splice_geometry.x,(ssize_t) splice_image->columns);
1793 image_view=AcquireVirtualCacheView(image,exception);
1794 splice_view=AcquireAuthenticCacheView(splice_image,exception);
1795 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1796 #pragma omp parallel for schedule(static) shared(progress,status) \
1797 magick_number_threads(image,splice_image,splice_geometry.y,1)
1799 for (y=0; y < (ssize_t) splice_geometry.y; y++)
1801 register const Quantum
1810 if (status == MagickFalse)
1812 p=GetCacheViewVirtualPixels(image_view,0,y,splice_image->columns,1,
1814 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1816 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1821 for (x=0; x < columns; x++)
1826 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1828 PixelChannel channel = GetPixelChannelChannel(image,i);
1829 PixelTrait traits = GetPixelChannelTraits(image,channel);
1830 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1831 if ((traits == UndefinedPixelTrait) ||
1832 (splice_traits == UndefinedPixelTrait))
1834 SetPixelChannel(splice_image,channel,p[i],q);
1836 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1837 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1838 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1839 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1840 p+=GetPixelChannels(image);
1841 q+=GetPixelChannels(splice_image);
1843 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
1844 q+=GetPixelChannels(splice_image);
1845 for ( ; x < (ssize_t) splice_image->columns; x++)
1850 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1852 PixelChannel channel = GetPixelChannelChannel(image,i);
1853 PixelTrait traits = GetPixelChannelTraits(image,channel);
1854 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1855 if ((traits == UndefinedPixelTrait) ||
1856 (splice_traits == UndefinedPixelTrait))
1858 SetPixelChannel(splice_image,channel,p[i],q);
1860 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1861 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1862 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1863 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1864 p+=GetPixelChannels(image);
1865 q+=GetPixelChannels(splice_image);
1867 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1869 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1874 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1878 proceed=SetImageProgress(image,SpliceImageTag,progress,
1879 splice_image->rows);
1880 if (proceed == MagickFalse)
1884 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1885 #pragma omp parallel for schedule(static) shared(progress,status) \
1886 magick_number_threads(image,splice_image,splice_image->rows,2)
1888 for (y=(ssize_t) (splice_geometry.y+splice_geometry.height);
1889 y < (ssize_t) splice_image->rows; y++)
1891 register const Quantum
1900 if (status == MagickFalse)
1902 if ((y < 0) || (y >= (ssize_t)splice_image->rows))
1904 p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
1905 splice_image->columns,1,exception);
1906 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1908 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1913 for (x=0; x < columns; x++)
1918 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1920 PixelChannel channel = GetPixelChannelChannel(image,i);
1921 PixelTrait traits = GetPixelChannelTraits(image,channel);
1922 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1923 if ((traits == UndefinedPixelTrait) ||
1924 (splice_traits == UndefinedPixelTrait))
1926 SetPixelChannel(splice_image,channel,p[i],q);
1928 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1929 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1930 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1931 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1932 p+=GetPixelChannels(image);
1933 q+=GetPixelChannels(splice_image);
1935 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
1936 q+=GetPixelChannels(splice_image);
1937 for ( ; x < (ssize_t) splice_image->columns; x++)
1942 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1944 PixelChannel channel = GetPixelChannelChannel(image,i);
1945 PixelTrait traits = GetPixelChannelTraits(image,channel);
1946 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1947 if ((traits == UndefinedPixelTrait) ||
1948 (splice_traits == UndefinedPixelTrait))
1950 SetPixelChannel(splice_image,channel,p[i],q);
1952 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1953 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1954 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1955 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1956 p+=GetPixelChannels(image);
1957 q+=GetPixelChannels(splice_image);
1959 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1961 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1966 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1970 proceed=SetImageProgress(image,SpliceImageTag,progress,
1971 splice_image->rows);
1972 if (proceed == MagickFalse)
1976 splice_view=DestroyCacheView(splice_view);
1977 image_view=DestroyCacheView(image_view);
1978 if (status == MagickFalse)
1979 splice_image=DestroyImage(splice_image);
1980 return(splice_image);
1984 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1988 % T r a n s f o r m I m a g e %
1992 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1994 % TransformImage() is a convenience method that behaves like ResizeImage() or
1995 % CropImage() but accepts scaling and/or cropping information as a region
1996 % geometry specification. If the operation fails, the original image handle
1999 % This should only be used for single images.
2001 % This function destroys what it assumes to be a single image list.
2002 % If the input image is part of a larger list, all other images in that list
2003 % will be simply 'lost', not destroyed.
2005 % Also if the crop generates a list of images only the first image is resized.
2006 % And finally if the crop succeeds and the resize failed, you will get a
2007 % cropped image, as well as a 'false' or 'failed' report.
2009 % This function and should probably be deprecated in favor of direct calls
2010 % to CropImageToTiles() or ResizeImage(), as appropriate.
2012 % The format of the TransformImage method is:
2014 % MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
2015 % const char *image_geometry,ExceptionInfo *exception)
2017 % A description of each parameter follows:
2019 % o image: the image The transformed image is returned as this parameter.
2021 % o crop_geometry: A crop geometry string. This geometry defines a
2022 % subregion of the image to crop.
2024 % o image_geometry: An image geometry string. This geometry defines the
2025 % final size of the image.
2027 % o exception: return any errors or warnings in this structure.
2030 MagickPrivate MagickBooleanType TransformImage(Image **image,
2031 const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
2040 assert(image != (Image **) NULL);
2041 assert((*image)->signature == MagickCoreSignature);
2042 if ((*image)->debug != MagickFalse)
2043 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
2044 transform_image=(*image);
2045 if (crop_geometry != (const char *) NULL)
2051 Crop image to a user specified size.
2053 crop_image=CropImageToTiles(*image,crop_geometry,exception);
2054 if (crop_image == (Image *) NULL)
2055 transform_image=CloneImage(*image,0,0,MagickTrue,exception);
2058 transform_image=DestroyImage(transform_image);
2059 transform_image=GetFirstImageInList(crop_image);
2061 *image=transform_image;
2063 if (image_geometry == (const char *) NULL)
2066 Scale image to a user specified size.
2068 (void) ParseRegionGeometry(transform_image,image_geometry,&geometry,
2070 if ((transform_image->columns == geometry.width) &&
2071 (transform_image->rows == geometry.height))
2073 resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
2074 transform_image->filter,exception);
2075 if (resize_image == (Image *) NULL)
2076 return(MagickFalse);
2077 transform_image=DestroyImage(transform_image);
2078 transform_image=resize_image;
2079 *image=transform_image;
2084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2088 % T r a n s p o s e I m a g e %
2092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2094 % TransposeImage() creates a horizontal mirror image by reflecting the pixels
2095 % around the central y-axis while rotating them by 90 degrees.
2097 % The format of the TransposeImage method is:
2099 % Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2101 % A description of each parameter follows:
2103 % o image: the image.
2105 % o exception: return any errors or warnings in this structure.
2108 MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2110 #define TransposeImageTag "Transpose/Image"
2131 assert(image != (const Image *) NULL);
2132 assert(image->signature == MagickCoreSignature);
2133 if (image->debug != MagickFalse)
2134 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2135 assert(exception != (ExceptionInfo *) NULL);
2136 assert(exception->signature == MagickCoreSignature);
2137 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2139 if (transpose_image == (Image *) NULL)
2140 return((Image *) NULL);
2146 image_view=AcquireVirtualCacheView(image,exception);
2147 transpose_view=AcquireAuthenticCacheView(transpose_image,exception);
2148 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2149 #pragma omp parallel for schedule(static) shared(progress,status) \
2150 magick_number_threads(image,transpose_image,image->rows,1)
2152 for (y=0; y < (ssize_t) image->rows; y++)
2154 register const Quantum
2163 if (status == MagickFalse)
2165 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
2166 image->columns,1,exception);
2167 q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1),
2168 0,1,transpose_image->rows,exception);
2169 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2174 for (x=0; x < (ssize_t) image->columns; x++)
2179 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2181 PixelChannel channel = GetPixelChannelChannel(image,i);
2182 PixelTrait traits = GetPixelChannelTraits(image,channel);
2183 PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image,
2185 if ((traits == UndefinedPixelTrait) ||
2186 (transpose_traits == UndefinedPixelTrait))
2188 SetPixelChannel(transpose_image,channel,p[i],q);
2190 p+=GetPixelChannels(image);
2191 q+=GetPixelChannels(transpose_image);
2193 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2195 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2200 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2204 proceed=SetImageProgress(image,TransposeImageTag,progress,image->rows);
2205 if (proceed == MagickFalse)
2209 transpose_view=DestroyCacheView(transpose_view);
2210 image_view=DestroyCacheView(image_view);
2211 transpose_image->type=image->type;
2212 page=transpose_image->page;
2213 Swap(page.width,page.height);
2214 Swap(page.x,page.y);
2215 transpose_image->page=page;
2216 if (status == MagickFalse)
2217 transpose_image=DestroyImage(transpose_image);
2218 return(transpose_image);
2222 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2226 % T r a n s v e r s e I m a g e %
2230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2232 % TransverseImage() creates a vertical mirror image by reflecting the pixels
2233 % around the central x-axis while rotating them by 270 degrees.
2235 % The format of the TransverseImage method is:
2237 % Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2239 % A description of each parameter follows:
2241 % o image: the image.
2243 % o exception: return any errors or warnings in this structure.
2246 MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2248 #define TransverseImageTag "Transverse/Image"
2269 assert(image != (const Image *) NULL);
2270 assert(image->signature == MagickCoreSignature);
2271 if (image->debug != MagickFalse)
2272 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2273 assert(exception != (ExceptionInfo *) NULL);
2274 assert(exception->signature == MagickCoreSignature);
2275 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2277 if (transverse_image == (Image *) NULL)
2278 return((Image *) NULL);
2284 image_view=AcquireVirtualCacheView(image,exception);
2285 transverse_view=AcquireAuthenticCacheView(transverse_image,exception);
2286 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2287 #pragma omp parallel for schedule(static) shared(progress,status) \
2288 magick_number_threads(image,transverse_image,image->rows,1)
2290 for (y=0; y < (ssize_t) image->rows; y++)
2295 register const Quantum
2304 if (status == MagickFalse)
2306 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2307 q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-1),
2308 0,1,transverse_image->rows,exception);
2309 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2314 q+=GetPixelChannels(transverse_image)*image->columns;
2315 for (x=0; x < (ssize_t) image->columns; x++)
2320 q-=GetPixelChannels(transverse_image);
2321 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2323 PixelChannel channel = GetPixelChannelChannel(image,i);
2324 PixelTrait traits = GetPixelChannelTraits(image,channel);
2325 PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image,
2327 if ((traits == UndefinedPixelTrait) ||
2328 (transverse_traits == UndefinedPixelTrait))
2330 SetPixelChannel(transverse_image,channel,p[i],q);
2332 p+=GetPixelChannels(image);
2334 sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2335 if (sync == MagickFalse)
2337 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2342 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2346 proceed=SetImageProgress(image,TransverseImageTag,progress,image->rows);
2347 if (proceed == MagickFalse)
2351 transverse_view=DestroyCacheView(transverse_view);
2352 image_view=DestroyCacheView(image_view);
2353 transverse_image->type=image->type;
2354 page=transverse_image->page;
2355 Swap(page.width,page.height);
2356 Swap(page.x,page.y);
2357 if (page.width != 0)
2358 page.x=(ssize_t) (page.width-transverse_image->columns-page.x);
2359 if (page.height != 0)
2360 page.y=(ssize_t) (page.height-transverse_image->rows-page.y);
2361 transverse_image->page=page;
2362 if (status == MagickFalse)
2363 transverse_image=DestroyImage(transverse_image);
2364 return(transverse_image);
2368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2372 % T r i m I m a g e %
2376 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2378 % TrimImage() trims pixels from the image edges. It allocates the memory
2379 % necessary for the new Image structure and returns a pointer to the new
2382 % The format of the TrimImage method is:
2384 % Image *TrimImage(const Image *image,ExceptionInfo *exception)
2386 % A description of each parameter follows:
2388 % o image: the image.
2390 % o exception: return any errors or warnings in this structure.
2393 MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2398 assert(image != (const Image *) NULL);
2399 assert(image->signature == MagickCoreSignature);
2400 if (image->debug != MagickFalse)
2401 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2402 geometry=GetImageBoundingBox(image,exception);
2403 if ((geometry.width == 0) || (geometry.height == 0))
2408 crop_image=CloneImage(image,1,1,MagickTrue,exception);
2409 if (crop_image == (Image *) NULL)
2410 return((Image *) NULL);
2411 crop_image->background_color.alpha=(MagickRealType) TransparentAlpha;
2412 crop_image->alpha_trait=BlendPixelTrait;
2413 (void) SetImageBackgroundColor(crop_image,exception);
2414 crop_image->page=image->page;
2415 crop_image->page.x=(-1);
2416 crop_image->page.y=(-1);
2419 geometry.x+=image->page.x;
2420 geometry.y+=image->page.y;
2421 return(CropImage(image,&geometry,exception));