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 % http://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);
319 source_view=AcquireVirtualCacheView(source_image,exception);
320 image_view=AcquireAuthenticCacheView(image,exception);
321 #if defined(MAGICKCORE_OPENMP_SUPPORT)
322 #pragma omp parallel for schedule(static,4) shared(progress,status) \
323 magick_threads(source_image,image,image->rows,1)
325 for (y=0; y < (ssize_t) image->rows; y++)
330 register const Quantum
342 if (status == MagickFalse)
344 if (clip_to_self != MagickFalse)
348 if ((y-y_offset) >= (ssize_t) source_image->rows)
352 If pixels is NULL, y is outside overlay region.
354 pixels=(Quantum *) NULL;
356 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
358 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
359 source_image->columns,1,exception);
360 if (p == (const Quantum *) NULL)
367 p-=x_offset*GetPixelChannels(source_image);
369 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
370 if (q == (Quantum *) NULL)
375 for (x=0; x < (ssize_t) image->columns; x++)
389 if (clip_to_self != MagickFalse)
393 q+=GetPixelChannels(image);
396 if ((x-x_offset) >= (ssize_t) source_image->columns)
399 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
400 ((x-x_offset) >= (ssize_t) source_image->columns))
403 source[MaxPixelChannels];
410 if (GetPixelWriteMask(image,q) == 0)
412 q+=GetPixelChannels(image);
415 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,
417 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
419 PixelChannel channel=GetPixelChannelChannel(image,i);
420 PixelTrait traits=GetPixelChannelTraits(image,channel);
421 PixelTrait source_traits=GetPixelChannelTraits(source_image,
423 if ((traits == UndefinedPixelTrait) ||
424 (source_traits == UndefinedPixelTrait))
426 q[i]=source[channel];
428 q+=GetPixelChannels(image);
433 Sa: normalized source alpha.
434 Da: normalized canvas alpha.
436 if (GetPixelWriteMask(source_image,p) == 0)
438 p+=GetPixelChannels(source_image);
439 channels=GetPixelChannels(source_image);
440 if (p >= (pixels+channels*source_image->columns))
442 q+=GetPixelChannels(image);
445 Sa=QuantumScale*GetPixelAlpha(source_image,p);
446 Da=QuantumScale*GetPixelAlpha(image,q);
448 gamma=PerceptibleReciprocal(gamma);
449 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
451 PixelChannel channel=GetPixelChannelChannel(image,i);
452 PixelTrait traits=GetPixelChannelTraits(image,channel);
453 PixelTrait source_traits=GetPixelChannelTraits(source_image,
455 if ((traits == UndefinedPixelTrait) ||
456 (source_traits == UndefinedPixelTrait))
458 if ((traits & CopyPixelTrait) != 0)
463 q[i]=GetPixelChannel(source_image,channel,p);
466 if (channel == AlphaPixelChannel)
471 q[i]=clamp != MagickFalse ?
472 ClampPixel(QuantumRange*(Sa+Da-Sa*Da)) :
473 ClampToQuantum(QuantumRange*(Sa+Da-Sa*Da));
480 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
481 Dc=(MagickRealType) q[i];
482 Sca=QuantumScale*Sa*Sc;
483 Dca=QuantumScale*Da*Dc;
484 q[i]=clamp != MagickFalse ?
485 ClampPixel(gamma*QuantumRange*(Sca+Dca*(1.0-Sa))) :
486 ClampToQuantum(gamma*QuantumRange*(Sca+Dca*(1.0-Sa)));
488 p+=GetPixelChannels(source_image);
489 channels=GetPixelChannels(source_image);
490 if (p >= (pixels+channels*source_image->columns))
492 q+=GetPixelChannels(image);
494 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
496 if (image->progress_monitor != (MagickProgressMonitor) NULL)
501 #if defined(MAGICKCORE_OPENMP_SUPPORT)
502 #pragma omp critical (MagickCore_CompositeImage)
504 proceed=SetImageProgress(image,CompositeImageTag,progress++,
506 if (proceed == MagickFalse)
510 source_view=DestroyCacheView(source_view);
511 image_view=DestroyCacheView(image_view);
515 MagickExport MagickBooleanType CompositeImage(Image *image,
516 const Image *composite,const CompositeOperator compose,
517 const MagickBooleanType clip_to_self,const ssize_t x_offset,
518 const ssize_t y_offset,ExceptionInfo *exception)
520 #define CompositeImageTag "Composite/Image"
558 assert(image != (Image *) NULL);
559 assert(image->signature == MagickCoreSignature);
560 if (image->debug != MagickFalse)
561 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
562 assert(composite != (Image *) NULL);
563 assert(composite->signature == MagickCoreSignature);
564 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
566 source_image=CloneImage(composite,0,0,MagickTrue,exception);
567 if (source_image == (const Image *) NULL)
569 if (IsGrayColorspace(image->colorspace) != MagickFalse)
570 (void) SetImageColorspace(image,sRGBColorspace,exception);
571 (void) SetImageColorspace(source_image,image->colorspace,exception);
572 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
574 status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
576 source_image=DestroyImage(source_image);
580 canvas_image=(Image *) NULL;
583 value=GetImageArtifact(image,"compose:clamp");
584 if (value != (const char *) NULL)
585 clamp=IsStringTrue(value);
586 SetGeometryInfo(&geometry_info);
588 percent_chroma=100.0;
593 case CopyCompositeOp:
595 if ((x_offset < 0) || (y_offset < 0))
597 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
599 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
602 source_view=AcquireVirtualCacheView(source_image,exception);
603 image_view=AcquireAuthenticCacheView(image,exception);
604 #if defined(MAGICKCORE_OPENMP_SUPPORT)
605 #pragma omp parallel for schedule(static,4) shared(status) \
606 magick_threads(source_image,image,source_image->rows,1)
608 for (y=0; y < (ssize_t) source_image->rows; y++)
613 register const Quantum
622 if (status == MagickFalse)
624 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
626 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
627 source_image->columns,1,exception);
628 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
633 for (x=0; x < (ssize_t) source_image->columns; x++)
638 if (GetPixelWriteMask(source_image,p) == 0)
640 p+=GetPixelChannels(source_image);
641 q+=GetPixelChannels(image);
644 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
646 PixelChannel channel=GetPixelChannelChannel(image,i);
647 PixelTrait traits=GetPixelChannelTraits(image,channel);
648 PixelTrait source_traits=GetPixelChannelTraits(source_image,
650 if (traits == UndefinedPixelTrait)
652 if (source_traits != UndefinedPixelTrait)
653 SetPixelChannel(image,channel,p[i],q);
654 else if (channel == AlphaPixelChannel)
655 SetPixelChannel(image,channel,OpaqueAlpha,q);
657 p+=GetPixelChannels(source_image);
658 q+=GetPixelChannels(image);
660 sync=SyncCacheViewAuthenticPixels(image_view,exception);
661 if (sync == MagickFalse)
663 if (image->progress_monitor != (MagickProgressMonitor) NULL)
668 #if defined(MAGICKCORE_OPENMP_SUPPORT)
669 #pragma omp critical (MagickCore_CompositeImage)
671 proceed=SetImageProgress(image,CompositeImageTag,
672 (MagickOffsetType) y,image->rows);
673 if (proceed == MagickFalse)
677 source_view=DestroyCacheView(source_view);
678 image_view=DestroyCacheView(image_view);
679 source_image=DestroyImage(source_image);
682 case IntensityCompositeOp:
684 if ((x_offset < 0) || (y_offset < 0))
686 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
688 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
691 source_view=AcquireVirtualCacheView(source_image,exception);
692 image_view=AcquireAuthenticCacheView(image,exception);
693 #if defined(MAGICKCORE_OPENMP_SUPPORT)
694 #pragma omp parallel for schedule(static,4) shared(status) \
695 magick_threads(source_image,image,source_image->rows,1)
697 for (y=0; y < (ssize_t) source_image->rows; y++)
702 register const Quantum
711 if (status == MagickFalse)
713 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
715 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
716 source_image->columns,1,exception);
717 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
722 for (x=0; x < (ssize_t) source_image->columns; x++)
724 if (GetPixelWriteMask(source_image,p) == 0)
726 p+=GetPixelChannels(source_image);
727 q+=GetPixelChannels(image);
730 SetPixelAlpha(image,clamp != MagickFalse ?
731 ClampPixel(GetPixelIntensity(source_image,p)) :
732 ClampToQuantum(GetPixelIntensity(source_image,p)),q);
733 p+=GetPixelChannels(source_image);
734 q+=GetPixelChannels(image);
736 sync=SyncCacheViewAuthenticPixels(image_view,exception);
737 if (sync == MagickFalse)
739 if (image->progress_monitor != (MagickProgressMonitor) NULL)
744 #if defined(MAGICKCORE_OPENMP_SUPPORT)
745 #pragma omp critical (MagickCore_CompositeImage)
747 proceed=SetImageProgress(image,CompositeImageTag,
748 (MagickOffsetType) y,image->rows);
749 if (proceed == MagickFalse)
753 source_view=DestroyCacheView(source_view);
754 image_view=DestroyCacheView(image_view);
755 source_image=DestroyImage(source_image);
758 case CopyAlphaCompositeOp:
759 case ChangeMaskCompositeOp:
762 Modify canvas outside the overlaid region and require an alpha
763 channel to exist, to add transparency.
765 if (image->alpha_trait == UndefinedPixelTrait)
766 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
769 case BlurCompositeOp:
790 Blur Image by resampling.
792 Blur Image dictated by an overlay gradient map: X = red_channel;
793 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
795 canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
797 if (canvas_image == (Image *) NULL)
799 source_image=DestroyImage(source_image);
803 Gather the maximum blur sigma values from user.
806 value=GetImageArtifact(image,"compose:args");
807 if (value != (const char *) NULL)
808 flags=ParseGeometry(value,&geometry_info);
809 if ((flags & WidthValue) == 0)
811 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
812 "InvalidSetting","'%s' '%s'","compose:args",value);
813 source_image=DestroyImage(source_image);
814 canvas_image=DestroyImage(canvas_image);
818 Users input sigma now needs to be converted to the EWA ellipse size.
819 The filter defaults to a sigma of 0.5 so to make this match the
820 users input the ellipse size needs to be doubled.
822 width=height=geometry_info.rho*2.0;
823 if ((flags & HeightValue) != 0 )
824 height=geometry_info.sigma*2.0;
826 Default the unrotated ellipse width and height axis vectors.
832 /* rotate vectors if a rotation angle is given */
833 if ((flags & XValue) != 0 )
838 angle=DegreesToRadians(geometry_info.xi);
839 blur.x1=width*cos(angle);
840 blur.x2=width*sin(angle);
841 blur.y1=(-height*sin(angle));
842 blur.y2=height*cos(angle);
844 /* Otherwise lets set a angle range and calculate in the loop */
847 if ((flags & YValue) != 0 )
849 angle_start=DegreesToRadians(geometry_info.xi);
850 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
853 Set up a gaussian cylindrical filter for EWA Bluring.
855 As the minimum ellipse radius of support*1.0 the EWA algorithm
856 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
857 This means that even 'No Blur' will be still a little blurry!
859 The solution (as well as the problem of preventing any user
860 expert filter settings, is to set our own user settings, then
861 restore them afterwards.
863 resample_filter=AcquireResampleFilter(image,exception);
864 SetResampleFilter(resample_filter,GaussianFilter);
866 /* do the variable blurring of each pixel in image */
867 GetPixelInfo(image,&pixel);
868 source_view=AcquireVirtualCacheView(source_image,exception);
869 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
870 for (y=0; y < (ssize_t) source_image->rows; y++)
875 register const Quantum
884 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
886 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
888 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
890 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
892 for (x=0; x < (ssize_t) source_image->columns; x++)
894 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
896 p+=GetPixelChannels(source_image);
899 if (fabs((double) angle_range) > MagickEpsilon)
904 angle=angle_start+angle_range*QuantumScale*
905 GetPixelBlue(source_image,p);
906 blur.x1=width*cos(angle);
907 blur.x2=width*sin(angle);
908 blur.y1=(-height*sin(angle));
909 blur.y2=height*cos(angle);
912 if ( x == 10 && y == 60 ) {
913 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
914 blur.x2,blur.y1, blur.y2);
915 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
916 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
918 ScaleResampleFilter(resample_filter,
919 blur.x1*QuantumScale*GetPixelRed(source_image,p),
920 blur.y1*QuantumScale*GetPixelGreen(source_image,p),
921 blur.x2*QuantumScale*GetPixelRed(source_image,p),
922 blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
923 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
924 (double) y_offset+y,&pixel,exception);
925 SetPixelViaPixelInfo(canvas_image,&pixel,q);
926 p+=GetPixelChannels(source_image);
927 q+=GetPixelChannels(canvas_image);
929 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
930 if (sync == MagickFalse)
933 resample_filter=DestroyResampleFilter(resample_filter);
934 source_view=DestroyCacheView(source_view);
935 canvas_view=DestroyCacheView(canvas_view);
936 source_image=DestroyImage(source_image);
937 source_image=canvas_image;
940 case DisplaceCompositeOp:
941 case DistortCompositeOp:
958 Displace/Distort based on overlay gradient map:
959 X = red_channel; Y = green_channel;
960 compose:args = x_scale[,y_scale[,center.x,center.y]]
962 canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
964 if (canvas_image == (Image *) NULL)
966 source_image=DestroyImage(source_image);
969 SetGeometryInfo(&geometry_info);
971 value=GetImageArtifact(image,"compose:args");
972 if (value != (char *) NULL)
973 flags=ParseGeometry(value,&geometry_info);
974 if ((flags & (WidthValue | HeightValue)) == 0 )
976 if ((flags & AspectValue) == 0)
978 horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
979 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
983 horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
984 vertical_scale=(MagickRealType) (image->rows-1)/2.0;
989 horizontal_scale=geometry_info.rho;
990 vertical_scale=geometry_info.sigma;
991 if ((flags & PercentValue) != 0)
993 if ((flags & AspectValue) == 0)
995 horizontal_scale*=(source_image->columns-1)/200.0;
996 vertical_scale*=(source_image->rows-1)/200.0;
1000 horizontal_scale*=(image->columns-1)/200.0;
1001 vertical_scale*=(image->rows-1)/200.0;
1004 if ((flags & HeightValue) == 0)
1005 vertical_scale=horizontal_scale;
1008 Determine fixed center point for absolute distortion map
1010 Displace offset relative to a fixed absolute point
1011 Select that point according to +X+Y user inputs.
1012 default = center of overlay image
1013 arg flag '!' = locations/percentage relative to background image
1015 center.x=(MagickRealType) x_offset;
1016 center.y=(MagickRealType) y_offset;
1017 if (compose == DistortCompositeOp)
1019 if ((flags & XValue) == 0)
1020 if ((flags & AspectValue) != 0)
1021 center.x=(MagickRealType) ((image->columns-1)/2.0);
1023 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1026 if ((flags & AspectValue) != 0)
1027 center.x=geometry_info.xi;
1029 center.x=(MagickRealType) (x_offset+geometry_info.xi);
1030 if ((flags & YValue) == 0)
1031 if ((flags & AspectValue) != 0)
1032 center.y=(MagickRealType) ((image->rows-1)/2.0);
1034 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1036 if ((flags & AspectValue) != 0)
1037 center.y=geometry_info.psi;
1039 center.y=(MagickRealType) (y_offset+geometry_info.psi);
1042 Shift the pixel offset point as defined by the provided,
1043 displacement/distortion map. -- Like a lens...
1045 GetPixelInfo(image,&pixel);
1046 image_view=AcquireVirtualCacheView(image,exception);
1047 source_view=AcquireVirtualCacheView(source_image,exception);
1048 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1049 for (y=0; y < (ssize_t) source_image->rows; y++)
1054 register const Quantum
1063 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1065 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1067 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1069 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1071 for (x=0; x < (ssize_t) source_image->columns; x++)
1073 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1075 p+=GetPixelChannels(source_image);
1079 Displace the offset.
1081 offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1082 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1083 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1085 offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1086 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1087 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1089 (void) InterpolatePixelInfo(image,image_view,
1090 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1093 Mask with the 'invalid pixel mask' in alpha channel.
1095 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
1096 (QuantumScale*GetPixelAlpha(source_image,p));
1097 SetPixelViaPixelInfo(canvas_image,&pixel,q);
1098 p+=GetPixelChannels(source_image);
1099 q+=GetPixelChannels(canvas_image);
1101 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1102 if (sync == MagickFalse)
1105 canvas_view=DestroyCacheView(canvas_view);
1106 source_view=DestroyCacheView(source_view);
1107 image_view=DestroyCacheView(image_view);
1108 source_image=DestroyImage(source_image);
1109 source_image=canvas_image;
1112 case DissolveCompositeOp:
1115 Geometry arguments to dissolve factors.
1117 value=GetImageArtifact(image,"compose:args");
1118 if (value != (char *) NULL)
1120 flags=ParseGeometry(value,&geometry_info);
1121 source_dissolve=geometry_info.rho/100.0;
1122 canvas_dissolve=1.0;
1123 if ((source_dissolve-MagickEpsilon) < 0.0)
1124 source_dissolve=0.0;
1125 if ((source_dissolve+MagickEpsilon) > 1.0)
1127 canvas_dissolve=2.0-source_dissolve;
1128 source_dissolve=1.0;
1130 if ((flags & SigmaValue) != 0)
1131 canvas_dissolve=geometry_info.sigma/100.0;
1132 if ((canvas_dissolve-MagickEpsilon) < 0.0)
1133 canvas_dissolve=0.0;
1137 case BlendCompositeOp:
1139 value=GetImageArtifact(image,"compose:args");
1140 if (value != (char *) NULL)
1142 flags=ParseGeometry(value,&geometry_info);
1143 source_dissolve=geometry_info.rho/100.0;
1144 canvas_dissolve=1.0-source_dissolve;
1145 if ((flags & SigmaValue) != 0)
1146 canvas_dissolve=geometry_info.sigma/100.0;
1150 case MathematicsCompositeOp:
1153 Just collect the values from "compose:args", setting.
1154 Unused values are set to zero automagically.
1156 Arguments are normally a comma separated list, so this probably should
1157 be changed to some 'general comma list' parser, (with a minimum
1160 SetGeometryInfo(&geometry_info);
1161 value=GetImageArtifact(image,"compose:args");
1162 if (value != (char *) NULL)
1163 (void) ParseGeometry(value,&geometry_info);
1166 case ModulateCompositeOp:
1169 Determine the luma and chroma scale.
1171 value=GetImageArtifact(image,"compose:args");
1172 if (value != (char *) NULL)
1174 flags=ParseGeometry(value,&geometry_info);
1175 percent_luma=geometry_info.rho;
1176 if ((flags & SigmaValue) != 0)
1177 percent_chroma=geometry_info.sigma;
1181 case ThresholdCompositeOp:
1184 Determine the amount and threshold.
1186 value=GetImageArtifact(image,"compose:args");
1187 if (value != (char *) NULL)
1189 flags=ParseGeometry(value,&geometry_info);
1190 amount=geometry_info.rho;
1191 threshold=geometry_info.sigma;
1192 if ((flags & SigmaValue) == 0)
1195 threshold*=QuantumRange;
1206 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1207 source_view=AcquireVirtualCacheView(source_image,exception);
1208 image_view=AcquireAuthenticCacheView(image,exception);
1209 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1210 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1211 magick_threads(source_image,image,image->rows,1)
1213 for (y=0; y < (ssize_t) image->rows; y++)
1230 register const Quantum
1239 if (status == MagickFalse)
1241 if (clip_to_self != MagickFalse)
1245 if ((y-y_offset) >= (ssize_t) source_image->rows)
1249 If pixels is NULL, y is outside overlay region.
1251 pixels=(Quantum *) NULL;
1253 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1255 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1256 source_image->columns,1,exception);
1257 if (p == (const Quantum *) NULL)
1264 p-=x_offset*GetPixelChannels(source_image);
1266 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1267 if (q == (Quantum *) NULL)
1275 GetPixelInfo(image,&canvas_pixel);
1276 GetPixelInfo(source_image,&source_pixel);
1277 for (x=0; x < (ssize_t) image->columns; x++)
1297 if (clip_to_self != MagickFalse)
1301 q+=GetPixelChannels(image);
1304 if ((x-x_offset) >= (ssize_t) source_image->columns)
1307 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1308 ((x-x_offset) >= (ssize_t) source_image->columns))
1311 source[MaxPixelChannels];
1318 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
1320 if (GetPixelWriteMask(image,q) == 0)
1322 q+=GetPixelChannels(image);
1325 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1330 PixelChannel channel=GetPixelChannelChannel(image,i);
1331 PixelTrait traits=GetPixelChannelTraits(image,channel);
1332 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1334 if ((traits == UndefinedPixelTrait) ||
1335 (source_traits == UndefinedPixelTrait))
1339 case AlphaCompositeOp:
1340 case ChangeMaskCompositeOp:
1341 case CopyAlphaCompositeOp:
1342 case DstAtopCompositeOp:
1343 case DstInCompositeOp:
1345 case OutCompositeOp:
1346 case SrcInCompositeOp:
1347 case SrcOutCompositeOp:
1349 if (channel == AlphaPixelChannel)
1350 pixel=(MagickRealType) TransparentAlpha;
1352 pixel=(MagickRealType) q[i];
1355 case ClearCompositeOp:
1356 case CopyCompositeOp:
1357 case ReplaceCompositeOp:
1358 case SrcCompositeOp:
1360 if (channel == AlphaPixelChannel)
1361 pixel=(MagickRealType) TransparentAlpha;
1366 case BlendCompositeOp:
1367 case DissolveCompositeOp:
1369 if (channel == AlphaPixelChannel)
1370 pixel=canvas_dissolve*GetPixelAlpha(source_image,source);
1372 pixel=(MagickRealType) source[channel];
1377 pixel=(MagickRealType) source[channel];
1381 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1382 ClampToQuantum(pixel);
1384 q+=GetPixelChannels(image);
1388 Authentic composite:
1389 Sa: normalized source alpha.
1390 Da: normalized canvas alpha.
1392 Sa=QuantumScale*GetPixelAlpha(source_image,p);
1393 Da=QuantumScale*GetPixelAlpha(image,q);
1396 case BumpmapCompositeOp:
1398 alpha=GetPixelIntensity(source_image,p)*Sa;
1401 case ColorBurnCompositeOp:
1402 case ColorDodgeCompositeOp:
1403 case DarkenCompositeOp:
1404 case DifferenceCompositeOp:
1405 case DivideDstCompositeOp:
1406 case DivideSrcCompositeOp:
1407 case ExclusionCompositeOp:
1408 case HardLightCompositeOp:
1409 case HardMixCompositeOp:
1410 case LinearBurnCompositeOp:
1411 case LinearDodgeCompositeOp:
1412 case LinearLightCompositeOp:
1413 case LightenCompositeOp:
1414 case MathematicsCompositeOp:
1415 case MinusDstCompositeOp:
1416 case MinusSrcCompositeOp:
1417 case ModulusAddCompositeOp:
1418 case ModulusSubtractCompositeOp:
1419 case MultiplyCompositeOp:
1420 case OverlayCompositeOp:
1421 case PegtopLightCompositeOp:
1422 case PinLightCompositeOp:
1423 case ScreenCompositeOp:
1424 case SoftLightCompositeOp:
1425 case VividLightCompositeOp:
1427 alpha=RoundToUnity(Sa+Da-Sa*Da);
1430 case DstAtopCompositeOp:
1431 case DstInCompositeOp:
1433 case SrcInCompositeOp:
1438 case DissolveCompositeOp:
1440 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
1444 case DstOverCompositeOp:
1445 case OverCompositeOp:
1446 case SrcOverCompositeOp:
1451 case DstOutCompositeOp:
1456 case OutCompositeOp:
1457 case SrcOutCompositeOp:
1462 case BlendCompositeOp:
1463 case PlusCompositeOp:
1465 alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
1468 case XorCompositeOp:
1470 alpha=Sa+Da-2.0*Sa*Da;
1479 if (GetPixelWriteMask(image,q) == 0)
1481 p+=GetPixelChannels(source_image);
1482 q+=GetPixelChannels(image);
1487 case ColorizeCompositeOp:
1488 case HueCompositeOp:
1489 case LuminizeCompositeOp:
1490 case ModulateCompositeOp:
1491 case SaturateCompositeOp:
1493 GetPixelInfoPixel(source_image,p,&source_pixel);
1494 GetPixelInfoPixel(image,q,&canvas_pixel);
1500 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1506 PixelChannel channel=GetPixelChannelChannel(image,i);
1507 PixelTrait traits=GetPixelChannelTraits(image,channel);
1508 PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
1509 if (traits == UndefinedPixelTrait)
1511 if ((source_traits == UndefinedPixelTrait) &&
1512 (channel != AlphaPixelChannel))
1514 if (channel == AlphaPixelChannel)
1521 case AlphaCompositeOp:
1523 pixel=QuantumRange*Sa;
1526 case AtopCompositeOp:
1527 case CopyBlackCompositeOp:
1528 case CopyBlueCompositeOp:
1529 case CopyCyanCompositeOp:
1530 case CopyGreenCompositeOp:
1531 case CopyMagentaCompositeOp:
1532 case CopyRedCompositeOp:
1533 case CopyYellowCompositeOp:
1534 case SrcAtopCompositeOp:
1535 case DstCompositeOp:
1538 pixel=QuantumRange*Da;
1541 case ChangeMaskCompositeOp:
1548 pixel=(MagickRealType) TransparentAlpha;
1551 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1552 if (equivalent != MagickFalse)
1553 pixel=(MagickRealType) TransparentAlpha;
1555 pixel=(MagickRealType) OpaqueAlpha;
1558 case ClearCompositeOp:
1560 pixel=(MagickRealType) TransparentAlpha;
1563 case ColorizeCompositeOp:
1564 case HueCompositeOp:
1565 case LuminizeCompositeOp:
1566 case SaturateCompositeOp:
1568 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1570 pixel=QuantumRange*Da;
1573 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1575 pixel=QuantumRange*Sa;
1580 pixel=QuantumRange*Da;
1583 pixel=QuantumRange*Sa;
1586 case CopyAlphaCompositeOp:
1588 if (source_image->alpha_trait == UndefinedPixelTrait)
1589 pixel=GetPixelIntensity(source_image,p);
1591 pixel=QuantumRange*Sa;
1594 case CopyCompositeOp:
1595 case DisplaceCompositeOp:
1596 case DistortCompositeOp:
1597 case DstAtopCompositeOp:
1598 case ReplaceCompositeOp:
1599 case SrcCompositeOp:
1601 pixel=QuantumRange*Sa;
1604 case DarkenIntensityCompositeOp:
1606 pixel=Sa*GetPixelIntensity(source_image,p) <
1607 Da*GetPixelIntensity(image,q) ? Sa : Da;
1610 case LightenIntensityCompositeOp:
1612 pixel=Sa*GetPixelIntensity(source_image,p) >
1613 Da*GetPixelIntensity(image,q) ? Sa : Da;
1616 case ModulateCompositeOp:
1618 pixel=QuantumRange*Da;
1623 pixel=QuantumRange*alpha;
1627 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1628 ClampToQuantum(pixel);
1635 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1636 Dc=(MagickRealType) q[i];
1637 if ((traits & CopyPixelTrait) != 0)
1646 Porter-Duff compositions:
1647 Sca: source normalized color multiplied by alpha.
1648 Dca: normalized canvas color multiplied by alpha.
1650 Sca=QuantumScale*Sa*Sc;
1651 Dca=QuantumScale*Da*Dc;
1654 case DarkenCompositeOp:
1655 case LightenCompositeOp:
1656 case ModulusSubtractCompositeOp:
1658 gamma=PerceptibleReciprocal(1.0-alpha);
1663 gamma=PerceptibleReciprocal(alpha);
1670 case AlphaCompositeOp:
1672 pixel=QuantumRange*Sa;
1675 case AtopCompositeOp:
1676 case SrcAtopCompositeOp:
1678 pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1681 case BlendCompositeOp:
1683 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1686 case BlurCompositeOp:
1687 case CopyCompositeOp:
1688 case ReplaceCompositeOp:
1689 case SrcCompositeOp:
1691 pixel=QuantumRange*Sca;
1694 case DisplaceCompositeOp:
1695 case DistortCompositeOp:
1700 case BumpmapCompositeOp:
1702 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1707 pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1710 case ChangeMaskCompositeOp:
1715 case ClearCompositeOp:
1720 case ColorBurnCompositeOp:
1722 if ((Sca == 0.0) && (Dca == Da))
1724 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1729 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1732 pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-Dca/Da)*Sa/
1733 Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1736 case ColorDodgeCompositeOp:
1738 if ((Sca*Da+Dca*Sa) >= Sa*Da)
1739 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1741 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
1745 case ColorizeCompositeOp:
1747 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1752 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1757 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1759 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1760 &hue,&chroma,&sans);
1761 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1764 case RedPixelChannel: pixel=red; break;
1765 case GreenPixelChannel: pixel=green; break;
1766 case BluePixelChannel: pixel=blue; break;
1767 default: pixel=Dc; break;
1771 case CopyAlphaCompositeOp:
1776 case CopyBlackCompositeOp:
1778 if (channel == BlackPixelChannel)
1779 pixel=(MagickRealType) (QuantumRange-
1780 GetPixelBlack(source_image,p));
1783 case CopyBlueCompositeOp:
1784 case CopyYellowCompositeOp:
1786 if (channel == BluePixelChannel)
1787 pixel=(MagickRealType) GetPixelBlue(source_image,p);
1790 case CopyGreenCompositeOp:
1791 case CopyMagentaCompositeOp:
1793 if (channel == GreenPixelChannel)
1794 pixel=(MagickRealType) GetPixelGreen(source_image,p);
1797 case CopyRedCompositeOp:
1798 case CopyCyanCompositeOp:
1800 if (channel == RedPixelChannel)
1801 pixel=(MagickRealType) GetPixelRed(source_image,p);
1804 case DarkenCompositeOp:
1807 Darken is equivalent to a 'Minimum' method
1808 OR a greyscale version of a binary 'Or'
1809 OR the 'Intersection' of pixel sets.
1811 if ((Sca*Da) < (Dca*Sa))
1813 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1816 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1819 case DarkenIntensityCompositeOp:
1821 pixel=Sa*GetPixelIntensity(source_image,p) <
1822 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1825 case DifferenceCompositeOp:
1827 pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1830 case DissolveCompositeOp:
1832 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1833 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1836 case DivideDstCompositeOp:
1838 if ((fabs((double) Sca) < MagickEpsilon) &&
1839 (fabs((double) Dca) < MagickEpsilon))
1841 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1844 if (fabs((double) Dca) < MagickEpsilon)
1846 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1849 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1852 case DivideSrcCompositeOp:
1854 if ((fabs((double) Dca) < MagickEpsilon) &&
1855 (fabs((double) Sca) < MagickEpsilon))
1857 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1860 if (fabs((double) Sca) < MagickEpsilon)
1862 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1865 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1868 case DstAtopCompositeOp:
1870 pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1873 case DstCompositeOp:
1876 pixel=QuantumRange*Dca;
1879 case DstInCompositeOp:
1881 pixel=QuantumRange*(Dca*Sa);
1884 case DstOutCompositeOp:
1886 pixel=QuantumRange*(Dca*(1.0-Sa));
1889 case DstOverCompositeOp:
1891 pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1894 case ExclusionCompositeOp:
1896 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1900 case HardLightCompositeOp:
1904 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1908 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1912 case HardMixCompositeOp:
1914 pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1917 case HueCompositeOp:
1919 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1924 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1929 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1930 &hue,&chroma,&luma);
1931 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1933 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1936 case RedPixelChannel: pixel=red; break;
1937 case GreenPixelChannel: pixel=green; break;
1938 case BluePixelChannel: pixel=blue; break;
1939 default: pixel=Dc; break;
1944 case SrcInCompositeOp:
1946 pixel=QuantumRange*(Sca*Da);
1949 case LinearBurnCompositeOp:
1952 LinearBurn: as defined by Abode Photoshop, according to
1953 http://www.simplefilter.de/en/basics/mixmods.html is:
1955 f(Sc,Dc) = Sc + Dc - 1
1957 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1960 case LinearDodgeCompositeOp:
1962 pixel=gamma*(Sa*Sc+Da*Dc);
1965 case LinearLightCompositeOp:
1968 LinearLight: as defined by Abode Photoshop, according to
1969 http://www.simplefilter.de/en/basics/mixmods.html is:
1971 f(Sc,Dc) = Dc + 2*Sc - 1
1973 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1976 case LightenCompositeOp:
1978 if ((Sca*Da) > (Dca*Sa))
1980 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1983 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1986 case LightenIntensityCompositeOp:
1989 Lighten is equivalent to a 'Maximum' method
1990 OR a greyscale version of a binary 'And'
1991 OR the 'Union' of pixel sets.
1993 pixel=Sa*GetPixelIntensity(source_image,p) >
1994 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1997 case LuminizeCompositeOp:
1999 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2004 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2009 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2010 &hue,&chroma,&luma);
2011 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2013 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2016 case RedPixelChannel: pixel=red; break;
2017 case GreenPixelChannel: pixel=green; break;
2018 case BluePixelChannel: pixel=blue; break;
2019 default: pixel=Dc; break;
2023 case MathematicsCompositeOp:
2026 'Mathematics' a free form user control mathematical composition
2029 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2031 Where the arguments A,B,C,D are (currently) passed to composite
2032 as a command separated 'geometry' string in "compose:args" image
2035 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2037 Applying the SVG transparency formula (see above), we get...
2039 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2041 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2044 pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
2045 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2046 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2049 case MinusDstCompositeOp:
2051 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2054 case MinusSrcCompositeOp:
2057 Minus source from canvas.
2061 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2064 case ModulateCompositeOp:
2069 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2074 offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2080 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2081 &hue,&chroma,&luma);
2082 luma+=(0.01*percent_luma*offset)/midpoint;
2083 chroma*=0.01*percent_chroma;
2084 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2087 case RedPixelChannel: pixel=red; break;
2088 case GreenPixelChannel: pixel=green; break;
2089 case BluePixelChannel: pixel=blue; break;
2090 default: pixel=Dc; break;
2094 case ModulusAddCompositeOp:
2097 while (pixel > QuantumRange)
2098 pixel-=QuantumRange;
2100 pixel+=QuantumRange;
2101 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2104 case ModulusSubtractCompositeOp:
2107 while (pixel > QuantumRange)
2108 pixel-=QuantumRange;
2110 pixel+=QuantumRange;
2111 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2114 case MultiplyCompositeOp:
2116 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2119 case OutCompositeOp:
2120 case SrcOutCompositeOp:
2122 pixel=QuantumRange*(Sca*(1.0-Da));
2125 case OverCompositeOp:
2126 case SrcOverCompositeOp:
2128 pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
2131 case OverlayCompositeOp:
2135 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-
2139 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2143 case PegtopLightCompositeOp:
2146 PegTop: A Soft-Light alternative: A continuous version of the
2147 Softlight function, producing very similar results.
2149 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2151 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2153 if (fabs((double) Da) < MagickEpsilon)
2155 pixel=QuantumRange*gamma*(Sca);
2158 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2162 case PinLightCompositeOp:
2165 PinLight: A Photoshop 7 composition method
2166 http://www.simplefilter.de/en/basics/mixmods.html
2168 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2170 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2172 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2175 if ((Dca*Sa) > (2.0*Sca*Da))
2177 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2180 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2183 case PlusCompositeOp:
2185 pixel=QuantumRange*(Sca+Dca);
2188 case SaturateCompositeOp:
2190 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2195 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2200 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2201 &hue,&chroma,&luma);
2202 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2203 &sans,&chroma,&sans);
2204 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2207 case RedPixelChannel: pixel=red; break;
2208 case GreenPixelChannel: pixel=green; break;
2209 case BluePixelChannel: pixel=blue; break;
2210 default: pixel=Dc; break;
2214 case ScreenCompositeOp:
2217 Screen: a negated multiply:
2219 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2221 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2224 case SoftLightCompositeOp:
2228 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2229 Sca*(1.0-Da)+Dca*(1.0-Sa));
2232 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2234 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2235 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2239 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2240 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2243 case ThresholdCompositeOp:
2249 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2254 pixel=gamma*(Dc+delta*amount);
2257 case VividLightCompositeOp:
2260 VividLight: A Photoshop 7 composition method. See
2261 http://www.simplefilter.de/en/basics/mixmods.html.
2263 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2265 if ((fabs((double) Sa) < MagickEpsilon) ||
2266 (fabs((double) (Sca-Sa)) < MagickEpsilon))
2268 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2271 if ((2.0*Sca) <= Sa)
2273 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2274 (1.0-Da)+Dca*(1.0-Sa));
2277 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*
2281 case XorCompositeOp:
2283 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2292 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2294 p+=GetPixelChannels(source_image);
2295 channels=GetPixelChannels(source_image);
2296 if (p >= (pixels+channels*source_image->columns))
2298 q+=GetPixelChannels(image);
2300 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2302 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2307 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2308 #pragma omp critical (MagickCore_CompositeImage)
2310 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2312 if (proceed == MagickFalse)
2316 source_view=DestroyCacheView(source_view);
2317 image_view=DestroyCacheView(image_view);
2318 if (canvas_image != (Image * ) NULL)
2319 canvas_image=DestroyImage(canvas_image);
2321 source_image=DestroyImage(source_image);
2326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2330 % T e x t u r e I m a g e %
2334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2336 % TextureImage() repeatedly tiles the texture image across and down the image
2339 % The format of the TextureImage method is:
2341 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2342 % ExceptionInfo *exception)
2344 % A description of each parameter follows:
2346 % o image: the image.
2348 % o texture_image: This image is the texture to layer on the background.
2351 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2352 ExceptionInfo *exception)
2354 #define TextureImageTag "Texture/Image"
2369 assert(image != (Image *) NULL);
2370 if (image->debug != MagickFalse)
2371 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2372 assert(image->signature == MagickCoreSignature);
2373 if (texture == (const Image *) NULL)
2374 return(MagickFalse);
2375 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2376 return(MagickFalse);
2377 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2378 if (texture_image == (const Image *) NULL)
2379 return(MagickFalse);
2380 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2381 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2384 if ((image->compose != CopyCompositeOp) &&
2385 ((image->compose != OverCompositeOp) ||
2386 (image->alpha_trait != UndefinedPixelTrait) ||
2387 (texture_image->alpha_trait != UndefinedPixelTrait)))
2390 Tile texture onto the image background.
2392 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2397 if (status == MagickFalse)
2399 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2404 thread_status=CompositeImage(image,texture_image,image->compose,
2405 MagickTrue,x+texture_image->tile_offset.x,y+
2406 texture_image->tile_offset.y,exception);
2407 if (thread_status == MagickFalse)
2409 status=thread_status;
2413 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2418 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2420 if (proceed == MagickFalse)
2424 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2425 image->rows,image->rows);
2426 texture_image=DestroyImage(texture_image);
2430 Tile texture onto the image background (optimized).
2433 texture_view=AcquireVirtualCacheView(texture_image,exception);
2434 image_view=AcquireAuthenticCacheView(image,exception);
2435 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2436 #pragma omp parallel for schedule(static,4) shared(status) \
2437 magick_threads(texture_image,image,1,1)
2439 for (y=0; y < (ssize_t) image->rows; y++)
2444 register const Quantum
2457 if (status == MagickFalse)
2459 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2460 (y+texture_image->tile_offset.y) % texture_image->rows,
2461 texture_image->columns,1,exception);
2462 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2463 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2468 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2474 width=texture_image->columns;
2475 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2476 width=image->columns-x;
2477 for (j=0; j < (ssize_t) width; j++)
2482 if (GetPixelWriteMask(image,q) == 0)
2484 p+=GetPixelChannels(texture_image);
2485 q+=GetPixelChannels(image);
2488 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2490 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2491 PixelTrait traits=GetPixelChannelTraits(image,channel);
2492 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2494 if ((traits == UndefinedPixelTrait) ||
2495 (texture_traits == UndefinedPixelTrait))
2497 SetPixelChannel(image,channel,p[i],q);
2499 p+=GetPixelChannels(texture_image);
2500 q+=GetPixelChannels(image);
2503 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2504 if (sync == MagickFalse)
2506 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2511 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2513 if (proceed == MagickFalse)
2517 texture_view=DestroyCacheView(texture_view);
2518 image_view=DestroyCacheView(image_view);
2519 texture_image=DestroyImage(texture_image);