2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7 % C O O MM MM P P O O SS I T E %
8 % C O O M M M PPPP O O SSS I T EEE %
9 % C O O M M P O O SS I T E %
10 % CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
13 % MagickCore Image Composite Methods %
20 % Copyright 1999-2017 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://www.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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-private.h"
47 #include "MagickCore/cache-view.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/color-private.h"
52 #include "MagickCore/colorspace.h"
53 #include "MagickCore/colorspace-private.h"
54 #include "MagickCore/composite.h"
55 #include "MagickCore/composite-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/draw.h"
58 #include "MagickCore/fx.h"
59 #include "MagickCore/gem.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/image.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/monitor.h"
66 #include "MagickCore/monitor-private.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/option.h"
69 #include "MagickCore/pixel-accessor.h"
70 #include "MagickCore/property.h"
71 #include "MagickCore/quantum.h"
72 #include "MagickCore/resample.h"
73 #include "MagickCore/resource_.h"
74 #include "MagickCore/string_.h"
75 #include "MagickCore/thread-private.h"
76 #include "MagickCore/threshold.h"
77 #include "MagickCore/token.h"
78 #include "MagickCore/utility.h"
79 #include "MagickCore/utility-private.h"
80 #include "MagickCore/version.h"
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 % C o m p o s i t e I m a g e %
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93 % CompositeImage() returns the second image composited onto the first
94 % at the specified offset, using the specified composite method.
96 % The format of the CompositeImage method is:
98 % MagickBooleanType CompositeImage(Image *image,
99 % const Image *source_image,const CompositeOperator compose,
100 % const MagickBooleanType clip_to_self,const ssize_t x_offset,
101 % const ssize_t y_offset,ExceptionInfo *exception)
103 % A description of each parameter follows:
105 % o image: the canvas image, modified by he composition
107 % o source_image: the source image.
109 % o compose: This operator affects how the composite is applied to
110 % the image. The operators and how they are utilized are listed here
111 % http://www.w3.org/TR/SVG12/#compositing.
113 % o clip_to_self: set to MagickTrue to limit composition to area composed.
115 % o x_offset: the column offset of the composited image.
117 % o y_offset: the row offset of the composited image.
119 % Extra Controls from Image meta-data in 'image' (artifacts)
122 % A string containing extra numerical arguments for specific compose
123 % methods, generally expressed as a 'geometry' or a comma separated list
126 % Compose methods needing such arguments include "BlendCompositeOp" and
127 % "DisplaceCompositeOp".
129 % o exception: return any errors or warnings in this structure.
134 Composition based on the SVG specification:
136 A Composition is defined by...
137 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
138 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
139 Y = 1 for source preserved
140 Z = 1 for canvas preserved
142 Conversion to transparency (then optimized)
143 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
144 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
147 Sca = Sc*Sa normalized Source color divided by Source alpha
148 Dca = Dc*Da normalized Dest color divided by Dest alpha
149 Dc' = Dca'/Da' the desired color value for this channel.
151 Da' in in the follow formula as 'gamma' The resulting alpla value.
153 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
154 the following optimizations...
156 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
157 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
159 The above SVG definitions also definate that Mathematical Composition
160 methods should use a 'Over' blending mode for Alpha Channel.
161 It however was not applied for composition modes of 'Plus', 'Minus',
162 the modulus versions of 'Add' and 'Subtract'.
164 Mathematical operator changes to be applied from IM v6.7...
166 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
167 'ModulusAdd' and 'ModulusSubtract' for clarity.
169 2) All mathematical compositions work as per the SVG specification
170 with regard to blending. This now includes 'ModulusAdd' and
173 3) When the special channel flag 'sync' (syncronize channel updates)
174 is turned off (enabled by default) then mathematical compositions are
175 only performed on the channels specified, and are applied
176 independantally of each other. In other words the mathematics is
177 performed as 'pure' mathematical operations, rather than as image
181 static void HCLComposite(const MagickRealType hue,const MagickRealType chroma,
182 const MagickRealType luma,MagickRealType *red,MagickRealType *green,
183 MagickRealType *blue)
195 Convert HCL to RGB colorspace.
197 assert(red != (MagickRealType *) NULL);
198 assert(green != (MagickRealType *) NULL);
199 assert(blue != (MagickRealType *) NULL);
202 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
206 if ((0.0 <= h) && (h < 1.0))
212 if ((1.0 <= h) && (h < 2.0))
218 if ((2.0 <= h) && (h < 3.0))
224 if ((3.0 <= h) && (h < 4.0))
230 if ((4.0 <= h) && (h < 5.0))
236 if ((5.0 <= h) && (h < 6.0))
241 m=luma-(0.298839*r+0.586811*g+0.114350*b);
242 *red=QuantumRange*(r+m);
243 *green=QuantumRange*(g+m);
244 *blue=QuantumRange*(b+m);
247 static void CompositeHCL(const MagickRealType red,const MagickRealType green,
248 const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
249 MagickRealType *luma)
260 Convert RGB to HCL colorspace.
262 assert(hue != (MagickRealType *) NULL);
263 assert(chroma != (MagickRealType *) NULL);
264 assert(luma != (MagickRealType *) NULL);
268 max=MagickMax(r,MagickMax(g,b));
269 c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
275 h=fmod((g-b)/c+6.0,6.0);
283 *chroma=QuantumScale*c;
284 *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b);
287 static MagickBooleanType CompositeOverImage(Image *image,
288 const Image *source_image,const MagickBooleanType clip_to_self,
289 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
291 #define CompositeImageTag "Composite/Image"
316 value=GetImageArtifact(image,"compose:clamp");
317 if (value != (const char *) NULL)
318 clamp=IsStringTrue(value);
321 source_view=AcquireVirtualCacheView(source_image,exception);
322 image_view=AcquireAuthenticCacheView(image,exception);
323 #if defined(MAGICKCORE_OPENMP_SUPPORT)
324 #pragma omp parallel for schedule(static,4) shared(progress,status) \
325 magick_threads(source_image,image,image->rows,1)
327 for (y=0; y < (ssize_t) image->rows; y++)
336 register const Quantum
345 if (status == MagickFalse)
347 if (clip_to_self != MagickFalse)
351 if ((y-y_offset) >= (ssize_t) source_image->rows)
355 If pixels is NULL, y is outside overlay region.
357 pixels=(Quantum *) NULL;
359 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
361 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
362 source_image->columns,1,exception);
363 if (p == (const Quantum *) NULL)
370 p-=x_offset*GetPixelChannels(source_image);
372 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
373 if (q == (Quantum *) NULL)
378 GetPixelInfo(image,&canvas_pixel);
379 GetPixelInfo(source_image,&source_pixel);
380 for (x=0; x < (ssize_t) image->columns; x++)
400 if (clip_to_self != MagickFalse)
404 q+=GetPixelChannels(image);
407 if ((x-x_offset) >= (ssize_t) source_image->columns)
410 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
411 ((x-x_offset) >= (ssize_t) source_image->columns))
414 source[MaxPixelChannels];
421 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
423 if (GetPixelWriteMask(image,q) == 0)
425 q+=GetPixelChannels(image);
428 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
433 PixelChannel channel=GetPixelChannelChannel(image,i);
434 PixelTrait traits=GetPixelChannelTraits(image,channel);
435 PixelTrait source_traits=GetPixelChannelTraits(source_image,
437 if ((traits == UndefinedPixelTrait) ||
438 (source_traits == UndefinedPixelTrait))
440 if (channel == AlphaPixelChannel)
441 pixel=(MagickRealType) TransparentAlpha;
443 pixel=(MagickRealType) q[i];
444 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
445 ClampToQuantum(pixel);
447 q+=GetPixelChannels(image);
452 Sa: normalized source alpha.
453 Da: normalized canvas alpha.
455 Sa=QuantumScale*GetPixelAlpha(source_image,p);
456 Da=QuantumScale*GetPixelAlpha(image,q);
458 if (GetPixelWriteMask(image,q) == 0)
460 p+=GetPixelChannels(source_image);
461 q+=GetPixelChannels(image);
464 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
469 PixelChannel channel=GetPixelChannelChannel(image,i);
470 PixelTrait traits=GetPixelChannelTraits(image,channel);
471 PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
472 if (traits == UndefinedPixelTrait)
474 if ((source_traits == UndefinedPixelTrait) &&
475 (channel != AlphaPixelChannel))
477 if (channel == AlphaPixelChannel)
482 pixel=QuantumRange*alpha;
483 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
484 ClampToQuantum(pixel);
491 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
492 Dc=(MagickRealType) q[i];
493 if ((traits & CopyPixelTrait) != 0)
502 Porter-Duff compositions:
503 Sca: source normalized color multiplied by alpha.
504 Dca: normalized canvas color multiplied by alpha.
506 Sca=QuantumScale*Sa*Sc;
507 Dca=QuantumScale*Da*Dc;
508 gamma=PerceptibleReciprocal(alpha);
509 pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
510 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
512 p+=GetPixelChannels(source_image);
513 channels=GetPixelChannels(source_image);
514 if (p >= (pixels+channels*source_image->columns))
516 q+=GetPixelChannels(image);
518 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
520 if (image->progress_monitor != (MagickProgressMonitor) NULL)
525 #if defined(MAGICKCORE_OPENMP_SUPPORT)
526 #pragma omp critical (MagickCore_CompositeImage)
528 proceed=SetImageProgress(image,CompositeImageTag,progress++,
530 if (proceed == MagickFalse)
534 source_view=DestroyCacheView(source_view);
535 image_view=DestroyCacheView(image_view);
539 MagickExport MagickBooleanType CompositeImage(Image *image,
540 const Image *composite,const CompositeOperator compose,
541 const MagickBooleanType clip_to_self,const ssize_t x_offset,
542 const ssize_t y_offset,ExceptionInfo *exception)
544 #define CompositeImageTag "Composite/Image"
582 assert(image != (Image *) NULL);
583 assert(image->signature == MagickCoreSignature);
584 if (image->debug != MagickFalse)
585 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
586 assert(composite != (Image *) NULL);
587 assert(composite->signature == MagickCoreSignature);
588 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
590 source_image=CloneImage(composite,0,0,MagickTrue,exception);
591 if (source_image == (const Image *) NULL)
593 if (IsGrayColorspace(image->colorspace) != MagickFalse)
594 (void) SetImageColorspace(image,sRGBColorspace,exception);
595 (void) SetImageColorspace(source_image,image->colorspace,exception);
596 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
598 status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
600 source_image=DestroyImage(source_image);
604 canvas_image=(Image *) NULL;
607 value=GetImageArtifact(image,"compose:clamp");
608 if (value != (const char *) NULL)
609 clamp=IsStringTrue(value);
610 SetGeometryInfo(&geometry_info);
612 percent_chroma=100.0;
617 case CopyCompositeOp:
619 if ((x_offset < 0) || (y_offset < 0))
621 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
623 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
626 source_view=AcquireVirtualCacheView(source_image,exception);
627 image_view=AcquireAuthenticCacheView(image,exception);
628 #if defined(MAGICKCORE_OPENMP_SUPPORT)
629 #pragma omp parallel for schedule(static,4) shared(status) \
630 magick_threads(source_image,image,source_image->rows,1)
632 for (y=0; y < (ssize_t) source_image->rows; y++)
637 register const Quantum
646 if (status == MagickFalse)
648 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
650 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
651 source_image->columns,1,exception);
652 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
657 for (x=0; x < (ssize_t) source_image->columns; x++)
662 if (GetPixelReadMask(source_image,p) == 0)
664 p+=GetPixelChannels(source_image);
665 q+=GetPixelChannels(image);
668 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
670 PixelChannel channel=GetPixelChannelChannel(image,i);
671 PixelTrait traits=GetPixelChannelTraits(image,channel);
672 PixelTrait source_traits=GetPixelChannelTraits(source_image,
674 if (traits == UndefinedPixelTrait)
676 if (source_traits != UndefinedPixelTrait)
677 SetPixelChannel(image,channel,p[i],q);
678 else if (channel == AlphaPixelChannel)
679 SetPixelChannel(image,channel,OpaqueAlpha,q);
681 p+=GetPixelChannels(source_image);
682 q+=GetPixelChannels(image);
684 sync=SyncCacheViewAuthenticPixels(image_view,exception);
685 if (sync == MagickFalse)
687 if (image->progress_monitor != (MagickProgressMonitor) NULL)
692 #if defined(MAGICKCORE_OPENMP_SUPPORT)
693 #pragma omp critical (MagickCore_CompositeImage)
695 proceed=SetImageProgress(image,CompositeImageTag,
696 (MagickOffsetType) y,image->rows);
697 if (proceed == MagickFalse)
701 source_view=DestroyCacheView(source_view);
702 image_view=DestroyCacheView(image_view);
703 source_image=DestroyImage(source_image);
706 case IntensityCompositeOp:
708 if ((x_offset < 0) || (y_offset < 0))
710 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
712 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
715 source_view=AcquireVirtualCacheView(source_image,exception);
716 image_view=AcquireAuthenticCacheView(image,exception);
717 #if defined(MAGICKCORE_OPENMP_SUPPORT)
718 #pragma omp parallel for schedule(static,4) shared(status) \
719 magick_threads(source_image,image,source_image->rows,1)
721 for (y=0; y < (ssize_t) source_image->rows; y++)
726 register const Quantum
735 if (status == MagickFalse)
737 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
739 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
740 source_image->columns,1,exception);
741 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
746 for (x=0; x < (ssize_t) source_image->columns; x++)
748 if (GetPixelReadMask(source_image,p) == 0)
750 p+=GetPixelChannels(source_image);
751 q+=GetPixelChannels(image);
754 SetPixelAlpha(image,clamp != MagickFalse ?
755 ClampPixel(GetPixelIntensity(source_image,p)) :
756 ClampToQuantum(GetPixelIntensity(source_image,p)),q);
757 p+=GetPixelChannels(source_image);
758 q+=GetPixelChannels(image);
760 sync=SyncCacheViewAuthenticPixels(image_view,exception);
761 if (sync == MagickFalse)
763 if (image->progress_monitor != (MagickProgressMonitor) NULL)
768 #if defined(MAGICKCORE_OPENMP_SUPPORT)
769 #pragma omp critical (MagickCore_CompositeImage)
771 proceed=SetImageProgress(image,CompositeImageTag,
772 (MagickOffsetType) y,image->rows);
773 if (proceed == MagickFalse)
777 source_view=DestroyCacheView(source_view);
778 image_view=DestroyCacheView(image_view);
779 source_image=DestroyImage(source_image);
782 case CopyAlphaCompositeOp:
783 case ChangeMaskCompositeOp:
786 Modify canvas outside the overlaid region and require an alpha
787 channel to exist, to add transparency.
789 if (image->alpha_trait == UndefinedPixelTrait)
790 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
793 case BlurCompositeOp:
814 Blur Image by resampling.
816 Blur Image dictated by an overlay gradient map: X = red_channel;
817 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
819 canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
821 if (canvas_image == (Image *) NULL)
823 source_image=DestroyImage(source_image);
827 Gather the maximum blur sigma values from user.
830 value=GetImageArtifact(image,"compose:args");
831 if (value != (const char *) NULL)
832 flags=ParseGeometry(value,&geometry_info);
833 if ((flags & WidthValue) == 0)
835 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
836 "InvalidSetting","'%s' '%s'","compose:args",value);
837 source_image=DestroyImage(source_image);
838 canvas_image=DestroyImage(canvas_image);
842 Users input sigma now needs to be converted to the EWA ellipse size.
843 The filter defaults to a sigma of 0.5 so to make this match the
844 users input the ellipse size needs to be doubled.
846 width=height=geometry_info.rho*2.0;
847 if ((flags & HeightValue) != 0 )
848 height=geometry_info.sigma*2.0;
850 Default the unrotated ellipse width and height axis vectors.
856 /* rotate vectors if a rotation angle is given */
857 if ((flags & XValue) != 0 )
862 angle=DegreesToRadians(geometry_info.xi);
863 blur.x1=width*cos(angle);
864 blur.x2=width*sin(angle);
865 blur.y1=(-height*sin(angle));
866 blur.y2=height*cos(angle);
868 /* Otherwise lets set a angle range and calculate in the loop */
871 if ((flags & YValue) != 0 )
873 angle_start=DegreesToRadians(geometry_info.xi);
874 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
877 Set up a gaussian cylindrical filter for EWA Bluring.
879 As the minimum ellipse radius of support*1.0 the EWA algorithm
880 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
881 This means that even 'No Blur' will be still a little blurry!
883 The solution (as well as the problem of preventing any user
884 expert filter settings, is to set our own user settings, then
885 restore them afterwards.
887 resample_filter=AcquireResampleFilter(image,exception);
888 SetResampleFilter(resample_filter,GaussianFilter);
890 /* do the variable blurring of each pixel in image */
891 GetPixelInfo(image,&pixel);
892 source_view=AcquireVirtualCacheView(source_image,exception);
893 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
894 for (y=0; y < (ssize_t) source_image->rows; y++)
899 register const Quantum
908 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
910 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
912 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
914 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
916 for (x=0; x < (ssize_t) source_image->columns; x++)
918 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
920 p+=GetPixelChannels(source_image);
923 if (fabs((double) angle_range) > MagickEpsilon)
928 angle=angle_start+angle_range*QuantumScale*
929 GetPixelBlue(source_image,p);
930 blur.x1=width*cos(angle);
931 blur.x2=width*sin(angle);
932 blur.y1=(-height*sin(angle));
933 blur.y2=height*cos(angle);
936 if ( x == 10 && y == 60 ) {
937 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
938 blur.x2,blur.y1, blur.y2);
939 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
940 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
942 ScaleResampleFilter(resample_filter,
943 blur.x1*QuantumScale*GetPixelRed(source_image,p),
944 blur.y1*QuantumScale*GetPixelGreen(source_image,p),
945 blur.x2*QuantumScale*GetPixelRed(source_image,p),
946 blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
947 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
948 (double) y_offset+y,&pixel,exception);
949 SetPixelViaPixelInfo(canvas_image,&pixel,q);
950 p+=GetPixelChannels(source_image);
951 q+=GetPixelChannels(canvas_image);
953 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
954 if (sync == MagickFalse)
957 resample_filter=DestroyResampleFilter(resample_filter);
958 source_view=DestroyCacheView(source_view);
959 canvas_view=DestroyCacheView(canvas_view);
960 source_image=DestroyImage(source_image);
961 source_image=canvas_image;
964 case DisplaceCompositeOp:
965 case DistortCompositeOp:
982 Displace/Distort based on overlay gradient map:
983 X = red_channel; Y = green_channel;
984 compose:args = x_scale[,y_scale[,center.x,center.y]]
986 canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
988 if (canvas_image == (Image *) NULL)
990 source_image=DestroyImage(source_image);
993 SetGeometryInfo(&geometry_info);
995 value=GetImageArtifact(image,"compose:args");
996 if (value != (char *) NULL)
997 flags=ParseGeometry(value,&geometry_info);
998 if ((flags & (WidthValue | HeightValue)) == 0 )
1000 if ((flags & AspectValue) == 0)
1002 horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
1003 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
1007 horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
1008 vertical_scale=(MagickRealType) (image->rows-1)/2.0;
1013 horizontal_scale=geometry_info.rho;
1014 vertical_scale=geometry_info.sigma;
1015 if ((flags & PercentValue) != 0)
1017 if ((flags & AspectValue) == 0)
1019 horizontal_scale*=(source_image->columns-1)/200.0;
1020 vertical_scale*=(source_image->rows-1)/200.0;
1024 horizontal_scale*=(image->columns-1)/200.0;
1025 vertical_scale*=(image->rows-1)/200.0;
1028 if ((flags & HeightValue) == 0)
1029 vertical_scale=horizontal_scale;
1032 Determine fixed center point for absolute distortion map
1034 Displace offset relative to a fixed absolute point
1035 Select that point according to +X+Y user inputs.
1036 default = center of overlay image
1037 arg flag '!' = locations/percentage relative to background image
1039 center.x=(MagickRealType) x_offset;
1040 center.y=(MagickRealType) y_offset;
1041 if (compose == DistortCompositeOp)
1043 if ((flags & XValue) == 0)
1044 if ((flags & AspectValue) != 0)
1045 center.x=(MagickRealType) ((image->columns-1)/2.0);
1047 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1050 if ((flags & AspectValue) != 0)
1051 center.x=geometry_info.xi;
1053 center.x=(MagickRealType) (x_offset+geometry_info.xi);
1054 if ((flags & YValue) == 0)
1055 if ((flags & AspectValue) != 0)
1056 center.y=(MagickRealType) ((image->rows-1)/2.0);
1058 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1060 if ((flags & AspectValue) != 0)
1061 center.y=geometry_info.psi;
1063 center.y=(MagickRealType) (y_offset+geometry_info.psi);
1066 Shift the pixel offset point as defined by the provided,
1067 displacement/distortion map. -- Like a lens...
1069 GetPixelInfo(image,&pixel);
1070 image_view=AcquireVirtualCacheView(image,exception);
1071 source_view=AcquireVirtualCacheView(source_image,exception);
1072 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1073 for (y=0; y < (ssize_t) source_image->rows; y++)
1078 register const Quantum
1087 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1089 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1091 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1093 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1095 for (x=0; x < (ssize_t) source_image->columns; x++)
1097 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1099 p+=GetPixelChannels(source_image);
1103 Displace the offset.
1105 offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1106 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1107 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1109 offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1110 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1111 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1113 (void) InterpolatePixelInfo(image,image_view,
1114 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1117 Mask with the 'invalid pixel mask' in alpha channel.
1119 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
1120 (QuantumScale*GetPixelAlpha(source_image,p));
1121 SetPixelViaPixelInfo(canvas_image,&pixel,q);
1122 p+=GetPixelChannels(source_image);
1123 q+=GetPixelChannels(canvas_image);
1125 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1126 if (sync == MagickFalse)
1129 canvas_view=DestroyCacheView(canvas_view);
1130 source_view=DestroyCacheView(source_view);
1131 image_view=DestroyCacheView(image_view);
1132 source_image=DestroyImage(source_image);
1133 source_image=canvas_image;
1136 case DissolveCompositeOp:
1139 Geometry arguments to dissolve factors.
1141 value=GetImageArtifact(image,"compose:args");
1142 if (value != (char *) NULL)
1144 flags=ParseGeometry(value,&geometry_info);
1145 source_dissolve=geometry_info.rho/100.0;
1146 canvas_dissolve=1.0;
1147 if ((source_dissolve-MagickEpsilon) < 0.0)
1148 source_dissolve=0.0;
1149 if ((source_dissolve+MagickEpsilon) > 1.0)
1151 canvas_dissolve=2.0-source_dissolve;
1152 source_dissolve=1.0;
1154 if ((flags & SigmaValue) != 0)
1155 canvas_dissolve=geometry_info.sigma/100.0;
1156 if ((canvas_dissolve-MagickEpsilon) < 0.0)
1157 canvas_dissolve=0.0;
1161 case BlendCompositeOp:
1163 value=GetImageArtifact(image,"compose:args");
1164 if (value != (char *) NULL)
1166 flags=ParseGeometry(value,&geometry_info);
1167 source_dissolve=geometry_info.rho/100.0;
1168 canvas_dissolve=1.0-source_dissolve;
1169 if ((flags & SigmaValue) != 0)
1170 canvas_dissolve=geometry_info.sigma/100.0;
1174 case MathematicsCompositeOp:
1177 Just collect the values from "compose:args", setting.
1178 Unused values are set to zero automagically.
1180 Arguments are normally a comma separated list, so this probably should
1181 be changed to some 'general comma list' parser, (with a minimum
1184 SetGeometryInfo(&geometry_info);
1185 value=GetImageArtifact(image,"compose:args");
1186 if (value != (char *) NULL)
1187 (void) ParseGeometry(value,&geometry_info);
1190 case ModulateCompositeOp:
1193 Determine the luma and chroma scale.
1195 value=GetImageArtifact(image,"compose:args");
1196 if (value != (char *) NULL)
1198 flags=ParseGeometry(value,&geometry_info);
1199 percent_luma=geometry_info.rho;
1200 if ((flags & SigmaValue) != 0)
1201 percent_chroma=geometry_info.sigma;
1205 case ThresholdCompositeOp:
1208 Determine the amount and threshold.
1210 value=GetImageArtifact(image,"compose:args");
1211 if (value != (char *) NULL)
1213 flags=ParseGeometry(value,&geometry_info);
1214 amount=geometry_info.rho;
1215 threshold=geometry_info.sigma;
1216 if ((flags & SigmaValue) == 0)
1219 threshold*=QuantumRange;
1230 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1231 source_view=AcquireVirtualCacheView(source_image,exception);
1232 image_view=AcquireAuthenticCacheView(image,exception);
1233 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1234 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1235 magick_threads(source_image,image,image->rows,1)
1237 for (y=0; y < (ssize_t) image->rows; y++)
1254 register const Quantum
1263 if (status == MagickFalse)
1265 if (clip_to_self != MagickFalse)
1269 if ((y-y_offset) >= (ssize_t) source_image->rows)
1273 If pixels is NULL, y is outside overlay region.
1275 pixels=(Quantum *) NULL;
1277 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1279 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1280 source_image->columns,1,exception);
1281 if (p == (const Quantum *) NULL)
1288 p-=x_offset*GetPixelChannels(source_image);
1290 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1291 if (q == (Quantum *) NULL)
1299 GetPixelInfo(image,&canvas_pixel);
1300 GetPixelInfo(source_image,&source_pixel);
1301 for (x=0; x < (ssize_t) image->columns; x++)
1321 if (clip_to_self != MagickFalse)
1325 q+=GetPixelChannels(image);
1328 if ((x-x_offset) >= (ssize_t) source_image->columns)
1331 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1332 ((x-x_offset) >= (ssize_t) source_image->columns))
1335 source[MaxPixelChannels];
1342 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
1344 if (GetPixelWriteMask(image,q) == 0)
1346 q+=GetPixelChannels(image);
1349 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1354 PixelChannel channel=GetPixelChannelChannel(image,i);
1355 PixelTrait traits=GetPixelChannelTraits(image,channel);
1356 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1358 if ((traits == UndefinedPixelTrait) ||
1359 (source_traits == UndefinedPixelTrait))
1363 case AlphaCompositeOp:
1364 case ChangeMaskCompositeOp:
1365 case CopyAlphaCompositeOp:
1366 case DstAtopCompositeOp:
1367 case DstInCompositeOp:
1369 case OutCompositeOp:
1370 case SrcInCompositeOp:
1371 case SrcOutCompositeOp:
1373 if (channel == AlphaPixelChannel)
1374 pixel=(MagickRealType) TransparentAlpha;
1376 pixel=(MagickRealType) q[i];
1379 case ClearCompositeOp:
1380 case CopyCompositeOp:
1381 case ReplaceCompositeOp:
1382 case SrcCompositeOp:
1384 if (channel == AlphaPixelChannel)
1385 pixel=(MagickRealType) TransparentAlpha;
1390 case BlendCompositeOp:
1391 case DissolveCompositeOp:
1393 if (channel == AlphaPixelChannel)
1394 pixel=canvas_dissolve*GetPixelAlpha(source_image,source);
1396 pixel=(MagickRealType) source[channel];
1401 pixel=(MagickRealType) source[channel];
1405 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1406 ClampToQuantum(pixel);
1408 q+=GetPixelChannels(image);
1412 Authentic composite:
1413 Sa: normalized source alpha.
1414 Da: normalized canvas alpha.
1416 Sa=QuantumScale*GetPixelAlpha(source_image,p);
1417 Da=QuantumScale*GetPixelAlpha(image,q);
1420 case BumpmapCompositeOp:
1422 alpha=GetPixelIntensity(source_image,p)*Sa;
1425 case ColorBurnCompositeOp:
1426 case ColorDodgeCompositeOp:
1427 case DarkenCompositeOp:
1428 case DifferenceCompositeOp:
1429 case DivideDstCompositeOp:
1430 case DivideSrcCompositeOp:
1431 case ExclusionCompositeOp:
1432 case HardLightCompositeOp:
1433 case HardMixCompositeOp:
1434 case LinearBurnCompositeOp:
1435 case LinearDodgeCompositeOp:
1436 case LinearLightCompositeOp:
1437 case LightenCompositeOp:
1438 case MathematicsCompositeOp:
1439 case MinusDstCompositeOp:
1440 case MinusSrcCompositeOp:
1441 case ModulusAddCompositeOp:
1442 case ModulusSubtractCompositeOp:
1443 case MultiplyCompositeOp:
1444 case OverlayCompositeOp:
1445 case PegtopLightCompositeOp:
1446 case PinLightCompositeOp:
1447 case ScreenCompositeOp:
1448 case SoftLightCompositeOp:
1449 case VividLightCompositeOp:
1451 alpha=RoundToUnity(Sa+Da-Sa*Da);
1454 case DstAtopCompositeOp:
1455 case DstInCompositeOp:
1457 case SrcInCompositeOp:
1462 case DissolveCompositeOp:
1464 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
1468 case DstOverCompositeOp:
1469 case OverCompositeOp:
1470 case SrcOverCompositeOp:
1475 case DstOutCompositeOp:
1480 case OutCompositeOp:
1481 case SrcOutCompositeOp:
1486 case BlendCompositeOp:
1487 case PlusCompositeOp:
1489 alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
1492 case XorCompositeOp:
1494 alpha=Sa+Da-2.0*Sa*Da;
1503 if (GetPixelWriteMask(image,q) == 0)
1505 p+=GetPixelChannels(source_image);
1506 q+=GetPixelChannels(image);
1511 case ColorizeCompositeOp:
1512 case HueCompositeOp:
1513 case LuminizeCompositeOp:
1514 case ModulateCompositeOp:
1515 case SaturateCompositeOp:
1517 GetPixelInfoPixel(source_image,p,&source_pixel);
1518 GetPixelInfoPixel(image,q,&canvas_pixel);
1524 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1530 PixelChannel channel=GetPixelChannelChannel(image,i);
1531 PixelTrait traits=GetPixelChannelTraits(image,channel);
1532 PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
1533 if (traits == UndefinedPixelTrait)
1535 if ((source_traits == UndefinedPixelTrait) &&
1536 (channel != AlphaPixelChannel))
1538 if (channel == AlphaPixelChannel)
1545 case AlphaCompositeOp:
1547 pixel=QuantumRange*Sa;
1550 case AtopCompositeOp:
1551 case CopyBlackCompositeOp:
1552 case CopyBlueCompositeOp:
1553 case CopyCyanCompositeOp:
1554 case CopyGreenCompositeOp:
1555 case CopyMagentaCompositeOp:
1556 case CopyRedCompositeOp:
1557 case CopyYellowCompositeOp:
1558 case SrcAtopCompositeOp:
1559 case DstCompositeOp:
1562 pixel=QuantumRange*Da;
1565 case ChangeMaskCompositeOp:
1572 pixel=(MagickRealType) TransparentAlpha;
1575 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1576 if (equivalent != MagickFalse)
1577 pixel=(MagickRealType) TransparentAlpha;
1579 pixel=(MagickRealType) OpaqueAlpha;
1582 case ClearCompositeOp:
1584 pixel=(MagickRealType) TransparentAlpha;
1587 case ColorizeCompositeOp:
1588 case HueCompositeOp:
1589 case LuminizeCompositeOp:
1590 case SaturateCompositeOp:
1592 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1594 pixel=QuantumRange*Da;
1597 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1599 pixel=QuantumRange*Sa;
1604 pixel=QuantumRange*Da;
1607 pixel=QuantumRange*Sa;
1610 case CopyAlphaCompositeOp:
1612 if (source_image->alpha_trait == UndefinedPixelTrait)
1613 pixel=GetPixelIntensity(source_image,p);
1615 pixel=QuantumRange*Sa;
1618 case CopyCompositeOp:
1619 case DisplaceCompositeOp:
1620 case DistortCompositeOp:
1621 case DstAtopCompositeOp:
1622 case ReplaceCompositeOp:
1623 case SrcCompositeOp:
1625 pixel=QuantumRange*Sa;
1628 case DarkenIntensityCompositeOp:
1630 pixel=Sa*GetPixelIntensity(source_image,p) <
1631 Da*GetPixelIntensity(image,q) ? Sa : Da;
1634 case LightenIntensityCompositeOp:
1636 pixel=Sa*GetPixelIntensity(source_image,p) >
1637 Da*GetPixelIntensity(image,q) ? Sa : Da;
1640 case ModulateCompositeOp:
1642 pixel=QuantumRange*Da;
1647 pixel=QuantumRange*alpha;
1651 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1652 ClampToQuantum(pixel);
1659 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1660 Dc=(MagickRealType) q[i];
1661 if ((traits & CopyPixelTrait) != 0)
1670 Porter-Duff compositions:
1671 Sca: source normalized color multiplied by alpha.
1672 Dca: normalized canvas color multiplied by alpha.
1674 Sca=QuantumScale*Sa*Sc;
1675 Dca=QuantumScale*Da*Dc;
1678 case DarkenCompositeOp:
1679 case LightenCompositeOp:
1680 case ModulusSubtractCompositeOp:
1682 gamma=PerceptibleReciprocal(1.0-alpha);
1687 gamma=PerceptibleReciprocal(alpha);
1694 case AlphaCompositeOp:
1696 pixel=QuantumRange*Sa;
1699 case AtopCompositeOp:
1700 case SrcAtopCompositeOp:
1702 pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1705 case BlendCompositeOp:
1707 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1710 case BlurCompositeOp:
1711 case CopyCompositeOp:
1712 case ReplaceCompositeOp:
1713 case SrcCompositeOp:
1715 pixel=QuantumRange*Sca;
1718 case DisplaceCompositeOp:
1719 case DistortCompositeOp:
1724 case BumpmapCompositeOp:
1726 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1731 pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1734 case ChangeMaskCompositeOp:
1739 case ClearCompositeOp:
1744 case ColorBurnCompositeOp:
1746 if ((Sca == 0.0) && (Dca == Da))
1748 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1753 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1756 pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-Dca/Da)*Sa/
1757 Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1760 case ColorDodgeCompositeOp:
1762 if ((Sca*Da+Dca*Sa) >= Sa*Da)
1763 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1765 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
1769 case ColorizeCompositeOp:
1771 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1776 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1781 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1783 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1784 &hue,&chroma,&sans);
1785 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1788 case RedPixelChannel: pixel=red; break;
1789 case GreenPixelChannel: pixel=green; break;
1790 case BluePixelChannel: pixel=blue; break;
1791 default: pixel=Dc; break;
1795 case CopyAlphaCompositeOp:
1800 case CopyBlackCompositeOp:
1802 if (channel == BlackPixelChannel)
1803 pixel=(MagickRealType) (QuantumRange-
1804 GetPixelBlack(source_image,p));
1807 case CopyBlueCompositeOp:
1808 case CopyYellowCompositeOp:
1810 if (channel == BluePixelChannel)
1811 pixel=(MagickRealType) GetPixelBlue(source_image,p);
1814 case CopyGreenCompositeOp:
1815 case CopyMagentaCompositeOp:
1817 if (channel == GreenPixelChannel)
1818 pixel=(MagickRealType) GetPixelGreen(source_image,p);
1821 case CopyRedCompositeOp:
1822 case CopyCyanCompositeOp:
1824 if (channel == RedPixelChannel)
1825 pixel=(MagickRealType) GetPixelRed(source_image,p);
1828 case DarkenCompositeOp:
1831 Darken is equivalent to a 'Minimum' method
1832 OR a greyscale version of a binary 'Or'
1833 OR the 'Intersection' of pixel sets.
1835 if ((Sca*Da) < (Dca*Sa))
1837 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1840 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1843 case DarkenIntensityCompositeOp:
1845 pixel=Sa*GetPixelIntensity(source_image,p) <
1846 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1849 case DifferenceCompositeOp:
1851 pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1854 case DissolveCompositeOp:
1856 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1857 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1860 case DivideDstCompositeOp:
1862 if ((fabs((double) Sca) < MagickEpsilon) &&
1863 (fabs((double) Dca) < MagickEpsilon))
1865 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1868 if (fabs((double) Dca) < MagickEpsilon)
1870 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1873 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1876 case DivideSrcCompositeOp:
1878 if ((fabs((double) Dca) < MagickEpsilon) &&
1879 (fabs((double) Sca) < MagickEpsilon))
1881 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1884 if (fabs((double) Sca) < MagickEpsilon)
1886 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1889 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1892 case DstAtopCompositeOp:
1894 pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1897 case DstCompositeOp:
1900 pixel=QuantumRange*Dca;
1903 case DstInCompositeOp:
1905 pixel=QuantumRange*(Dca*Sa);
1908 case DstOutCompositeOp:
1910 pixel=QuantumRange*(Dca*(1.0-Sa));
1913 case DstOverCompositeOp:
1915 pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1918 case ExclusionCompositeOp:
1920 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1924 case HardLightCompositeOp:
1928 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1932 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1936 case HardMixCompositeOp:
1938 pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1941 case HueCompositeOp:
1943 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1948 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1953 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1954 &hue,&chroma,&luma);
1955 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1957 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1960 case RedPixelChannel: pixel=red; break;
1961 case GreenPixelChannel: pixel=green; break;
1962 case BluePixelChannel: pixel=blue; break;
1963 default: pixel=Dc; break;
1968 case SrcInCompositeOp:
1970 pixel=QuantumRange*(Sca*Da);
1973 case LinearBurnCompositeOp:
1976 LinearBurn: as defined by Abode Photoshop, according to
1977 http://www.simplefilter.de/en/basics/mixmods.html is:
1979 f(Sc,Dc) = Sc + Dc - 1
1981 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1984 case LinearDodgeCompositeOp:
1986 pixel=gamma*(Sa*Sc+Da*Dc);
1989 case LinearLightCompositeOp:
1992 LinearLight: as defined by Abode Photoshop, according to
1993 http://www.simplefilter.de/en/basics/mixmods.html is:
1995 f(Sc,Dc) = Dc + 2*Sc - 1
1997 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
2000 case LightenCompositeOp:
2002 if ((Sca*Da) > (Dca*Sa))
2004 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2007 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2010 case LightenIntensityCompositeOp:
2013 Lighten is equivalent to a 'Maximum' method
2014 OR a greyscale version of a binary 'And'
2015 OR the 'Union' of pixel sets.
2017 pixel=Sa*GetPixelIntensity(source_image,p) >
2018 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2021 case LuminizeCompositeOp:
2023 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2028 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2033 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2034 &hue,&chroma,&luma);
2035 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2037 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2040 case RedPixelChannel: pixel=red; break;
2041 case GreenPixelChannel: pixel=green; break;
2042 case BluePixelChannel: pixel=blue; break;
2043 default: pixel=Dc; break;
2047 case MathematicsCompositeOp:
2050 'Mathematics' a free form user control mathematical composition
2053 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2055 Where the arguments A,B,C,D are (currently) passed to composite
2056 as a command separated 'geometry' string in "compose:args" image
2059 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2061 Applying the SVG transparency formula (see above), we get...
2063 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2065 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2068 pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
2069 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2070 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2073 case MinusDstCompositeOp:
2075 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2078 case MinusSrcCompositeOp:
2081 Minus source from canvas.
2085 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2088 case ModulateCompositeOp:
2093 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2098 offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2104 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2105 &hue,&chroma,&luma);
2106 luma+=(0.01*percent_luma*offset)/midpoint;
2107 chroma*=0.01*percent_chroma;
2108 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2111 case RedPixelChannel: pixel=red; break;
2112 case GreenPixelChannel: pixel=green; break;
2113 case BluePixelChannel: pixel=blue; break;
2114 default: pixel=Dc; break;
2118 case ModulusAddCompositeOp:
2121 while (pixel > QuantumRange)
2122 pixel-=QuantumRange;
2124 pixel+=QuantumRange;
2125 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2128 case ModulusSubtractCompositeOp:
2131 while (pixel > QuantumRange)
2132 pixel-=QuantumRange;
2134 pixel+=QuantumRange;
2135 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2138 case MultiplyCompositeOp:
2140 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2143 case OutCompositeOp:
2144 case SrcOutCompositeOp:
2146 pixel=QuantumRange*(Sca*(1.0-Da));
2149 case OverCompositeOp:
2150 case SrcOverCompositeOp:
2152 pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
2155 case OverlayCompositeOp:
2159 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-
2163 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2167 case PegtopLightCompositeOp:
2170 PegTop: A Soft-Light alternative: A continuous version of the
2171 Softlight function, producing very similar results.
2173 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2175 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2177 if (fabs((double) Da) < MagickEpsilon)
2179 pixel=QuantumRange*gamma*(Sca);
2182 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2186 case PinLightCompositeOp:
2189 PinLight: A Photoshop 7 composition method
2190 http://www.simplefilter.de/en/basics/mixmods.html
2192 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2194 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2196 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2199 if ((Dca*Sa) > (2.0*Sca*Da))
2201 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2204 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2207 case PlusCompositeOp:
2209 pixel=QuantumRange*(Sca+Dca);
2212 case SaturateCompositeOp:
2214 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2219 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2224 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2225 &hue,&chroma,&luma);
2226 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2227 &sans,&chroma,&sans);
2228 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2231 case RedPixelChannel: pixel=red; break;
2232 case GreenPixelChannel: pixel=green; break;
2233 case BluePixelChannel: pixel=blue; break;
2234 default: pixel=Dc; break;
2238 case ScreenCompositeOp:
2241 Screen: a negated multiply:
2243 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2245 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2248 case SoftLightCompositeOp:
2252 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2253 Sca*(1.0-Da)+Dca*(1.0-Sa));
2256 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2258 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2259 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2263 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2264 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2267 case ThresholdCompositeOp:
2273 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2278 pixel=gamma*(Dc+delta*amount);
2281 case VividLightCompositeOp:
2284 VividLight: A Photoshop 7 composition method. See
2285 http://www.simplefilter.de/en/basics/mixmods.html.
2287 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2289 if ((fabs((double) Sa) < MagickEpsilon) ||
2290 (fabs((double) (Sca-Sa)) < MagickEpsilon))
2292 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2295 if ((2.0*Sca) <= Sa)
2297 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2298 (1.0-Da)+Dca*(1.0-Sa));
2301 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*
2305 case XorCompositeOp:
2307 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2316 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2318 p+=GetPixelChannels(source_image);
2319 channels=GetPixelChannels(source_image);
2320 if (p >= (pixels+channels*source_image->columns))
2322 q+=GetPixelChannels(image);
2324 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2326 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2331 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2332 #pragma omp critical (MagickCore_CompositeImage)
2334 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2336 if (proceed == MagickFalse)
2340 source_view=DestroyCacheView(source_view);
2341 image_view=DestroyCacheView(image_view);
2342 if (canvas_image != (Image * ) NULL)
2343 canvas_image=DestroyImage(canvas_image);
2345 source_image=DestroyImage(source_image);
2350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2354 % T e x t u r e I m a g e %
2358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2360 % TextureImage() repeatedly tiles the texture image across and down the image
2363 % The format of the TextureImage method is:
2365 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2366 % ExceptionInfo *exception)
2368 % A description of each parameter follows:
2370 % o image: the image.
2372 % o texture_image: This image is the texture to layer on the background.
2375 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2376 ExceptionInfo *exception)
2378 #define TextureImageTag "Texture/Image"
2393 assert(image != (Image *) NULL);
2394 if (image->debug != MagickFalse)
2395 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2396 assert(image->signature == MagickCoreSignature);
2397 if (texture == (const Image *) NULL)
2398 return(MagickFalse);
2399 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2400 return(MagickFalse);
2401 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2402 if (texture_image == (const Image *) NULL)
2403 return(MagickFalse);
2404 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2405 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2408 if ((image->compose != CopyCompositeOp) &&
2409 ((image->compose != OverCompositeOp) ||
2410 (image->alpha_trait != UndefinedPixelTrait) ||
2411 (texture_image->alpha_trait != UndefinedPixelTrait)))
2414 Tile texture onto the image background.
2416 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2421 if (status == MagickFalse)
2423 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2428 thread_status=CompositeImage(image,texture_image,image->compose,
2429 MagickTrue,x+texture_image->tile_offset.x,y+
2430 texture_image->tile_offset.y,exception);
2431 if (thread_status == MagickFalse)
2433 status=thread_status;
2437 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2442 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2444 if (proceed == MagickFalse)
2448 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2449 image->rows,image->rows);
2450 texture_image=DestroyImage(texture_image);
2454 Tile texture onto the image background (optimized).
2457 texture_view=AcquireVirtualCacheView(texture_image,exception);
2458 image_view=AcquireAuthenticCacheView(image,exception);
2459 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2460 #pragma omp parallel for schedule(static,4) shared(status) \
2461 magick_threads(texture_image,image,1,1)
2463 for (y=0; y < (ssize_t) image->rows; y++)
2468 register const Quantum
2481 if (status == MagickFalse)
2483 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2484 (y+texture_image->tile_offset.y) % texture_image->rows,
2485 texture_image->columns,1,exception);
2486 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2487 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2492 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2498 width=texture_image->columns;
2499 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2500 width=image->columns-x;
2501 for (j=0; j < (ssize_t) width; j++)
2506 if (GetPixelWriteMask(image,q) == 0)
2508 p+=GetPixelChannels(texture_image);
2509 q+=GetPixelChannels(image);
2512 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2514 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2515 PixelTrait traits=GetPixelChannelTraits(image,channel);
2516 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2518 if ((traits == UndefinedPixelTrait) ||
2519 (texture_traits == UndefinedPixelTrait))
2521 SetPixelChannel(image,channel,p[i],q);
2523 p+=GetPixelChannels(texture_image);
2524 q+=GetPixelChannels(image);
2527 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2528 if (sync == MagickFalse)
2530 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2535 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2537 if (proceed == MagickFalse)
2541 texture_view=DestroyCacheView(texture_view);
2542 image_view=DestroyCacheView(image_view);
2543 texture_image=DestroyImage(texture_image);