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-2013 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/client.h"
49 #include "MagickCore/color.h"
50 #include "MagickCore/color-private.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/colorspace-private.h"
53 #include "MagickCore/composite.h"
54 #include "MagickCore/composite-private.h"
55 #include "MagickCore/constitute.h"
56 #include "MagickCore/draw.h"
57 #include "MagickCore/fx.h"
58 #include "MagickCore/gem.h"
59 #include "MagickCore/geometry.h"
60 #include "MagickCore/image.h"
61 #include "MagickCore/image-private.h"
62 #include "MagickCore/list.h"
63 #include "MagickCore/log.h"
64 #include "MagickCore/monitor.h"
65 #include "MagickCore/monitor-private.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/option.h"
68 #include "MagickCore/pixel-accessor.h"
69 #include "MagickCore/property.h"
70 #include "MagickCore/quantum.h"
71 #include "MagickCore/resample.h"
72 #include "MagickCore/resource_.h"
73 #include "MagickCore/string_.h"
74 #include "MagickCore/thread-private.h"
75 #include "MagickCore/threshold.h"
76 #include "MagickCore/token.h"
77 #include "MagickCore/utility.h"
78 #include "MagickCore/utility-private.h"
79 #include "MagickCore/version.h"
82 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 % C o m p o s i t e I m a g e %
90 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 % CompositeImage() returns the second image composited onto the first
93 % at the specified offset, using the specified composite method.
95 % The format of the CompositeImage method is:
97 % MagickBooleanType CompositeImage(Image *image,
98 % const Image *composite_image,const CompositeOperator compose,
99 % const MagickBooleanType clip_to_self,const ssize_t x_offset,
100 % const ssize_t y_offset,ExceptionInfo *exception)
102 % A description of each parameter follows:
104 % o image: the destination image, modified by he composition
106 % o composite_image: the composite (source) image.
108 % o compose: This operator affects how the composite is applied to
109 % the image. The operators and how they are utilized are listed here
110 % http://www.w3.org/TR/SVG12/#compositing.
112 % o clip_to_self: set to MagickTrue to limit composition to area composed.
114 % o x_offset: the column offset of the composited image.
116 % o y_offset: the row offset of the composited image.
118 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
121 % A string containing extra numerical arguments for specific compose
122 % methods, generally expressed as a 'geometry' or a comma separated list
125 % Compose methods needing such arguments include "BlendCompositeOp" and
126 % "DisplaceCompositeOp".
128 % o exception: return any errors or warnings in this structure.
133 Composition based on the SVG specification:
135 A Composition is defined by...
136 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
137 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
138 Y = 1 for source preserved
139 Z = 1 for destination preserved
141 Conversion to transparency (then optimized)
142 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
143 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
146 Sca = Sc*Sa normalized Source color divided by Source alpha
147 Dca = Dc*Da normalized Dest color divided by Dest alpha
148 Dc' = Dca'/Da' the desired color value for this channel.
150 Da' in in the follow formula as 'gamma' The resulting alpla value.
152 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
153 the following optimizations...
155 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
156 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
158 The above SVG definitions also definate that Mathematical Composition
159 methods should use a 'Over' blending mode for Alpha Channel.
160 It however was not applied for composition modes of 'Plus', 'Minus',
161 the modulus versions of 'Add' and 'Subtract'.
163 Mathematical operator changes to be applied from IM v6.7...
165 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
166 'ModulusAdd' and 'ModulusSubtract' for clarity.
168 2) All mathematical compositions work as per the SVG specification
169 with regard to blending. This now includes 'ModulusAdd' and
172 3) When the special channel flag 'sync' (syncronize channel updates)
173 is turned off (enabled by default) then mathematical compositions are
174 only performed on the channels specified, and are applied
175 independantally of each other. In other words the mathematics is
176 performed as 'pure' mathematical operations, rather than as image
180 static inline MagickRealType MagickMin(const MagickRealType x,
181 const MagickRealType y)
188 static inline MagickRealType MagickMax(const MagickRealType x,
189 const MagickRealType y)
196 static inline MagickRealType ConvertHueToRGB(MagickRealType m1,
197 MagickRealType m2,MagickRealType hue)
204 return(m1+6.0*(m2-m1)*hue);
208 return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
212 static void HCLComposite(const MagickRealType hue,const MagickRealType chroma,
213 const MagickRealType luma,MagickRealType *red,MagickRealType *green,
214 MagickRealType *blue)
227 Convert HCL to RGB colorspace.
229 assert(red != (MagickRealType *) NULL);
230 assert(green != (MagickRealType *) NULL);
231 assert(blue != (MagickRealType *) NULL);
234 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
238 if ((0.0 <= h) && (h < 1.0))
244 if ((1.0 <= h) && (h < 2.0))
250 if ((2.0 <= h) && (h < 3.0))
256 if ((3.0 <= h) && (h < 4.0))
262 if ((4.0 <= h) && (h < 5.0))
268 if ((5.0 <= h) && (h < 6.0))
273 m=luma-(0.298839f*r+0.586811f*g+0.114350f*b);
275 Choose saturation strategy to clip it into the RGB cube; hue and luma are
276 preserved and chroma may be changed.
287 z=(1.0-luma)/(m+c-luma);
290 *red=QuantumRange*(z*r+m);
291 *green=QuantumRange*(z*g+m);
292 *blue=QuantumRange*(z*b+m);
295 static void CompositeHCL(const MagickRealType red,const MagickRealType green,
296 const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
297 MagickRealType *luma)
308 Convert RGB to HCL colorspace.
310 assert(hue != (MagickRealType *) NULL);
311 assert(chroma != (MagickRealType *) NULL);
312 assert(luma != (MagickRealType *) NULL);
316 max=MagickMax(r,MagickMax(g,b));
317 c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
323 h=fmod(6.0+(g-b)/c,6.0);
331 *chroma=QuantumScale*c;
332 *luma=QuantumScale*(0.298839f*r+0.586811f*g+0.114350f*b);
335 static MagickBooleanType CompositeOverImage(Image *image,
336 const Image *composite_image,const MagickBooleanType clip_to_self,
337 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
339 #define CompositeImageTag "Composite/Image"
359 composite_view=AcquireVirtualCacheView(composite_image,exception);
360 image_view=AcquireAuthenticCacheView(image,exception);
361 #if defined(MAGICKCORE_OPENMP_SUPPORT)
362 #pragma omp parallel for schedule(static,4) shared(progress,status) \
363 magick_threads(composite_image,image,image->rows,1)
365 for (y=0; y < (ssize_t) image->rows; y++)
370 register const Quantum
382 if (status == MagickFalse)
384 if (clip_to_self != MagickFalse)
388 if ((y-y_offset) >= (ssize_t) composite_image->rows)
392 If pixels is NULL, y is outside overlay region.
394 pixels=(Quantum *) NULL;
396 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
398 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
399 composite_image->columns,1,exception);
400 if (p == (const Quantum *) NULL)
407 p-=x_offset*GetPixelChannels(composite_image);
409 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
410 if (q == (Quantum *) NULL)
415 for (x=0; x < (ssize_t) image->columns; x++)
430 if (clip_to_self != MagickFalse)
434 q+=GetPixelChannels(image);
437 if ((x-x_offset) >= (ssize_t) composite_image->columns)
440 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
441 ((x-x_offset) >= (ssize_t) composite_image->columns))
444 source[MaxPixelChannels];
449 Dc: destination color.
451 if (GetPixelMask(image,q) != 0)
453 q+=GetPixelChannels(image);
456 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
458 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
467 channel=GetPixelChannelChannel(image,i);
468 traits=GetPixelChannelTraits(image,channel);
469 composite_traits=GetPixelChannelTraits(composite_image,channel);
470 if ((traits == UndefinedPixelTrait) ||
471 (composite_traits == UndefinedPixelTrait))
473 q[i]=source[channel];
475 q+=GetPixelChannels(image);
480 Sa: normalized source alpha.
481 Da: normalized destination alpha.
483 if (GetPixelMask(composite_image,p) != 0)
485 p+=GetPixelChannels(composite_image);
486 channels=GetPixelChannels(composite_image);
487 if (p >= (pixels+channels*composite_image->columns))
489 q+=GetPixelChannels(image);
492 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
493 Da=QuantumScale*GetPixelAlpha(image,q);
494 alpha=Sa*(-Da)+Sa+Da;
495 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
504 channel=GetPixelChannelChannel(image,i);
505 traits=GetPixelChannelTraits(image,channel);
506 composite_traits=GetPixelChannelTraits(composite_image,channel);
507 if ((traits == UndefinedPixelTrait) ||
508 (composite_traits == UndefinedPixelTrait))
510 if ((traits & CopyPixelTrait) != 0)
512 if (channel != AlphaPixelChannel)
517 q[i]=GetPixelChannel(composite_image,channel,p);
523 q[i]=ClampToQuantum(QuantumRange*alpha);
528 Dc: destination color.
530 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
531 Dc=(MagickRealType) q[i];
532 gamma=PerceptibleReciprocal(alpha);
533 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
535 p+=GetPixelChannels(composite_image);
536 channels=GetPixelChannels(composite_image);
537 if (p >= (pixels+channels*composite_image->columns))
539 q+=GetPixelChannels(image);
541 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
543 if (image->progress_monitor != (MagickProgressMonitor) NULL)
548 #if defined(MAGICKCORE_OPENMP_SUPPORT)
549 #pragma omp critical (MagickCore_CompositeImage)
551 proceed=SetImageProgress(image,CompositeImageTag,progress++,
553 if (proceed == MagickFalse)
557 composite_view=DestroyCacheView(composite_view);
558 image_view=DestroyCacheView(image_view);
562 MagickExport MagickBooleanType CompositeImage(Image *image,
563 const Image *composite,const CompositeOperator compose,
564 const MagickBooleanType clip_to_self,const ssize_t x_offset,
565 const ssize_t y_offset,ExceptionInfo *exception)
567 #define CompositeImageTag "Composite/Image"
588 destination_dissolve,
601 assert(image != (Image *) NULL);
602 assert(image->signature == MagickSignature);
603 if (image->debug != MagickFalse)
604 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
605 assert(composite!= (Image *) NULL);
606 assert(composite->signature == MagickSignature);
607 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
609 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
610 if (composite_image == (const Image *) NULL)
612 if (IsGrayColorspace(image->colorspace) != MagickFalse)
613 (void) SetImageColorspace(image,RGBColorspace,exception);
614 (void) SetImageColorspace(composite_image,image->colorspace,exception);
615 if ((image->alpha_trait == BlendPixelTrait) &&
616 (composite_image->alpha_trait != BlendPixelTrait))
617 SetImageAlphaChannel(composite_image,SetAlphaChannel,exception);
618 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
620 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
622 composite_image=DestroyImage(composite_image);
625 destination_image=(Image *) NULL;
627 destination_dissolve=1.0;
629 percent_chroma=100.0;
634 case CopyCompositeOp:
636 if ((x_offset < 0) || (y_offset < 0))
638 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
640 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
643 composite_view=AcquireVirtualCacheView(composite_image,exception);
644 image_view=AcquireAuthenticCacheView(image,exception);
645 #if defined(MAGICKCORE_OPENMP_SUPPORT)
646 #pragma omp parallel for schedule(static,4) shared(status) \
647 magick_threads(composite_image,image,composite_image->rows,1)
649 for (y=0; y < (ssize_t) composite_image->rows; y++)
654 register const Quantum
663 if (status == MagickFalse)
665 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
667 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
668 composite_image->columns,1,exception);
669 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
674 for (x=0; x < (ssize_t) composite_image->columns; x++)
679 if (GetPixelMask(composite_image,p) != 0)
681 p+=GetPixelChannels(composite_image);
682 q+=GetPixelChannels(image);
685 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
694 channel=GetPixelChannelChannel(composite_image,i);
695 composite_traits=GetPixelChannelTraits(composite_image,channel);
696 traits=GetPixelChannelTraits(image,channel);
697 if ((traits == UndefinedPixelTrait) ||
698 (composite_traits == UndefinedPixelTrait))
700 SetPixelChannel(image,channel,p[i],q);
702 p+=GetPixelChannels(composite_image);
703 q+=GetPixelChannels(image);
705 sync=SyncCacheViewAuthenticPixels(image_view,exception);
706 if (sync == MagickFalse)
708 if (image->progress_monitor != (MagickProgressMonitor) NULL)
713 #if defined(MAGICKCORE_OPENMP_SUPPORT)
714 #pragma omp critical (MagickCore_CompositeImage)
716 proceed=SetImageProgress(image,CompositeImageTag,
717 (MagickOffsetType) y,image->rows);
718 if (proceed == MagickFalse)
722 composite_view=DestroyCacheView(composite_view);
723 image_view=DestroyCacheView(image_view);
724 composite_image=DestroyImage(composite_image);
727 case CopyAlphaCompositeOp:
728 case ChangeMaskCompositeOp:
729 case IntensityCompositeOp:
732 Modify destination outside the overlaid region and require an alpha
733 channel to exist, to add transparency.
735 if (image->alpha_trait != BlendPixelTrait)
736 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
739 case BlurCompositeOp:
764 Blur Image by resampling.
766 Blur Image dictated by an overlay gradient map: X = red_channel;
767 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
769 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
771 if (destination_image == (Image *) NULL)
773 composite_image=DestroyImage(composite_image);
777 Gather the maximum blur sigma values from user.
779 SetGeometryInfo(&geometry_info);
781 value=GetImageArtifact(image,"compose:args");
782 if (value != (const char *) NULL)
783 flags=ParseGeometry(value,&geometry_info);
784 if ((flags & WidthValue) == 0)
786 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
787 "InvalidSetting","'%s' '%s'","compose:args",value);
788 composite_image=DestroyImage(composite_image);
789 destination_image=DestroyImage(destination_image);
793 Users input sigma now needs to be converted to the EWA ellipse size.
794 The filter defaults to a sigma of 0.5 so to make this match the
795 users input the ellipse size needs to be doubled.
797 width=height=geometry_info.rho*2.0;
798 if ((flags & HeightValue) != 0 )
799 height=geometry_info.sigma*2.0;
801 Default the unrotated ellipse width and height axis vectors.
807 /* rotate vectors if a rotation angle is given */
808 if ((flags & XValue) != 0 )
813 angle=DegreesToRadians(geometry_info.xi);
814 blur.x1=width*cos(angle);
815 blur.x2=width*sin(angle);
816 blur.y1=(-height*sin(angle));
817 blur.y2=height*cos(angle);
819 /* Otherwise lets set a angle range and calculate in the loop */
822 if ((flags & YValue) != 0 )
824 angle_start=DegreesToRadians(geometry_info.xi);
825 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
828 Set up a gaussian cylindrical filter for EWA Bluring.
830 As the minimum ellipse radius of support*1.0 the EWA algorithm
831 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
832 This means that even 'No Blur' will be still a little blurry!
834 The solution (as well as the problem of preventing any user
835 expert filter settings, is to set our own user settings, then
836 restore them afterwards.
838 resample_filter=AcquireResampleFilter(image,exception);
839 SetResampleFilter(resample_filter,GaussianFilter);
841 /* do the variable blurring of each pixel in image */
842 GetPixelInfo(image,&pixel);
843 composite_view=AcquireVirtualCacheView(composite_image,exception);
844 destination_view=AcquireAuthenticCacheView(destination_image,exception);
845 for (y=0; y < (ssize_t) composite_image->rows; y++)
850 register const Quantum
859 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
861 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
863 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
864 destination_image->columns,1,exception);
865 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
867 for (x=0; x < (ssize_t) composite_image->columns; x++)
869 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
871 p+=GetPixelChannels(composite_image);
874 if (fabs(angle_range) > MagickEpsilon)
879 angle=angle_start+angle_range*QuantumScale*
880 GetPixelBlue(composite_image,p);
881 blur.x1=width*cos(angle);
882 blur.x2=width*sin(angle);
883 blur.y1=(-height*sin(angle));
884 blur.y2=height*cos(angle);
887 if ( x == 10 && y == 60 ) {
888 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
889 blur.x2,blur.y1, blur.y2);
890 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
891 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
893 ScaleResampleFilter(resample_filter,
894 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
895 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
896 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
897 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
898 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
899 (double) y_offset+y,&pixel,exception);
900 SetPixelInfoPixel(destination_image,&pixel,q);
901 p+=GetPixelChannels(composite_image);
902 q+=GetPixelChannels(destination_image);
904 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
905 if (sync == MagickFalse)
908 resample_filter=DestroyResampleFilter(resample_filter);
909 composite_view=DestroyCacheView(composite_view);
910 destination_view=DestroyCacheView(destination_view);
911 composite_image=DestroyImage(composite_image);
912 composite_image=destination_image;
915 case DisplaceCompositeOp:
916 case DistortCompositeOp:
938 Displace/Distort based on overlay gradient map:
939 X = red_channel; Y = green_channel;
940 compose:args = x_scale[,y_scale[,center.x,center.y]]
942 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
944 if (destination_image == (Image *) NULL)
946 composite_image=DestroyImage(composite_image);
949 SetGeometryInfo(&geometry_info);
951 value=GetImageArtifact(composite_image,"compose:args");
952 if (value != (char *) NULL)
953 flags=ParseGeometry(value,&geometry_info);
954 if ((flags & (WidthValue|HeightValue)) == 0 )
956 if ((flags & AspectValue) == 0)
958 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
960 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
964 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
965 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
970 horizontal_scale=geometry_info.rho;
971 vertical_scale=geometry_info.sigma;
972 if ((flags & PercentValue) != 0)
974 if ((flags & AspectValue) == 0)
976 horizontal_scale*=(composite_image->columns-1.0)/200.0;
977 vertical_scale*=(composite_image->rows-1.0)/200.0;
981 horizontal_scale*=(image->columns-1.0)/200.0;
982 vertical_scale*=(image->rows-1.0)/200.0;
985 if ((flags & HeightValue) == 0)
986 vertical_scale=horizontal_scale;
989 Determine fixed center point for absolute distortion map
991 Displace offset relative to a fixed absolute point
992 Select that point according to +X+Y user inputs.
993 default = center of overlay image
994 arg flag '!' = locations/percentage relative to background image
996 center.x=(MagickRealType) x_offset;
997 center.y=(MagickRealType) y_offset;
998 if (compose == DistortCompositeOp)
1000 if ((flags & XValue) == 0)
1001 if ((flags & AspectValue) == 0)
1002 center.x=(MagickRealType) (x_offset+(composite_image->columns-1)/
1005 center.x=(MagickRealType) ((image->columns-1)/2);
1007 if ((flags & AspectValue) == 0)
1008 center.x=(MagickRealType) x_offset+geometry_info.xi;
1010 center.x=geometry_info.xi;
1011 if ((flags & YValue) == 0)
1012 if ((flags & AspectValue) == 0)
1013 center.y=(MagickRealType) (y_offset+(composite_image->rows-1)/
1016 center.y=(MagickRealType) ((image->rows-1)/2);
1018 if ((flags & AspectValue) == 0)
1019 center.y=(MagickRealType) y_offset+geometry_info.psi;
1021 center.y=geometry_info.psi;
1024 Shift the pixel offset point as defined by the provided,
1025 displacement/distortion map. -- Like a lens...
1027 GetPixelInfo(image,&pixel);
1028 image_view=AcquireVirtualCacheView(image,exception);
1029 composite_view=AcquireVirtualCacheView(composite_image,exception);
1030 destination_view=AcquireAuthenticCacheView(destination_image,exception);
1031 for (y=0; y < (ssize_t) composite_image->rows; y++)
1036 register const Quantum
1045 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1047 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1049 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1050 destination_image->columns,1,exception);
1051 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1053 for (x=0; x < (ssize_t) composite_image->columns; x++)
1055 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1057 p+=GetPixelChannels(composite_image);
1061 Displace the offset.
1063 offset.x=(double) (horizontal_scale*(GetPixelRed(composite_image,p)-
1064 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1065 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1067 offset.y=(double) (vertical_scale*(GetPixelGreen(composite_image,p)-
1068 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1069 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1071 (void) InterpolatePixelInfo(image,image_view,
1072 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1075 Mask with the 'invalid pixel mask' in alpha channel.
1077 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1078 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
1079 SetPixelInfoPixel(destination_image,&pixel,q);
1080 p+=GetPixelChannels(composite_image);
1081 q+=GetPixelChannels(destination_image);
1083 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1084 if (sync == MagickFalse)
1087 destination_view=DestroyCacheView(destination_view);
1088 composite_view=DestroyCacheView(composite_view);
1089 image_view=DestroyCacheView(image_view);
1090 composite_image=DestroyImage(composite_image);
1091 composite_image=destination_image;
1094 case DissolveCompositeOp:
1100 Geometry arguments to dissolve factors.
1102 value=GetImageArtifact(composite_image,"compose:args");
1103 if (value != (char *) NULL)
1105 flags=ParseGeometry(value,&geometry_info);
1106 source_dissolve=geometry_info.rho/100.0;
1107 destination_dissolve=1.0;
1108 if ((source_dissolve-MagickEpsilon) < 0.0)
1109 source_dissolve=0.0;
1110 if ((source_dissolve+MagickEpsilon) > 1.0)
1112 destination_dissolve=2.0-source_dissolve;
1113 source_dissolve=1.0;
1115 if ((flags & SigmaValue) != 0)
1116 destination_dissolve=geometry_info.sigma/100.0;
1117 if ((destination_dissolve-MagickEpsilon) < 0.0)
1118 destination_dissolve=0.0;
1119 /* posible speed up? -- from IMv6 update
1120 clip_to_self=MagickFalse;
1121 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1123 destination_dissolve=1.0;
1124 clip_to_self=MagickTrue;
1130 case BlendCompositeOp:
1135 value=GetImageArtifact(composite_image,"compose:args");
1136 if (value != (char *) NULL)
1138 flags=ParseGeometry(value,&geometry_info);
1139 source_dissolve=geometry_info.rho/100.0;
1140 destination_dissolve=1.0-source_dissolve;
1141 if ((flags & SigmaValue) != 0)
1142 destination_dissolve=geometry_info.sigma/100.0;
1146 case MathematicsCompositeOp:
1152 Just collect the values from "compose:args", setting.
1153 Unused values are set to zero automagically.
1155 Arguments are normally a comma separated list, so this probably should
1156 be changed to some 'general comma list' parser, (with a minimum
1159 SetGeometryInfo(&geometry_info);
1160 value=GetImageArtifact(composite_image,"compose:args");
1161 if (value != (char *) NULL)
1162 (void) ParseGeometry(value,&geometry_info);
1165 case ModulateCompositeOp:
1171 Determine the luma and chroma scale.
1173 value=GetImageArtifact(composite_image,"compose:args");
1174 if (value != (char *) NULL)
1176 flags=ParseGeometry(value,&geometry_info);
1177 percent_luma=geometry_info.rho;
1178 if ((flags & SigmaValue) != 0)
1179 percent_chroma=geometry_info.sigma;
1183 case ThresholdCompositeOp:
1189 Determine the amount and threshold.
1191 value=GetImageArtifact(composite_image,"compose:args");
1192 if (value != (char *) NULL)
1194 flags=ParseGeometry(value,&geometry_info);
1195 amount=geometry_info.rho;
1196 threshold=geometry_info.sigma;
1197 if ((flags & SigmaValue) == 0)
1200 threshold*=QuantumRange;
1211 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1212 composite_view=AcquireVirtualCacheView(composite_image,exception);
1213 image_view=AcquireAuthenticCacheView(image,exception);
1214 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1215 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1216 magick_threads(composite_image,image,image->rows,1)
1218 for (y=0; y < (ssize_t) image->rows; y++)
1235 register const Quantum
1244 if (status == MagickFalse)
1246 if (clip_to_self != MagickFalse)
1250 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1254 If pixels is NULL, y is outside overlay region.
1256 pixels=(Quantum *) NULL;
1258 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1260 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1261 composite_image->columns,1,exception);
1262 if (p == (const Quantum *) NULL)
1269 p-=x_offset*GetPixelChannels(composite_image);
1271 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1272 if (q == (Quantum *) NULL)
1280 GetPixelInfo(image,&destination_pixel);
1281 GetPixelInfo(composite_image,&source_pixel);
1282 for (x=0; x < (ssize_t) image->columns; x++)
1302 if (clip_to_self != MagickFalse)
1306 q+=GetPixelChannels(image);
1309 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1312 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1313 ((x-x_offset) >= (ssize_t) composite_image->columns))
1316 source[MaxPixelChannels];
1321 Dc: destination color.
1323 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1325 if (GetPixelMask(image,q) != 0)
1327 q+=GetPixelChannels(image);
1330 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1342 channel=GetPixelChannelChannel(image,i);
1343 traits=GetPixelChannelTraits(image,channel);
1344 composite_traits=GetPixelChannelTraits(composite_image,channel);
1345 if ((traits == UndefinedPixelTrait) ||
1346 (composite_traits == UndefinedPixelTrait))
1350 case AlphaCompositeOp:
1351 case ChangeMaskCompositeOp:
1352 case CopyAlphaCompositeOp:
1353 case DstAtopCompositeOp:
1354 case DstInCompositeOp:
1356 case IntensityCompositeOp:
1357 case OutCompositeOp:
1358 case SrcInCompositeOp:
1359 case SrcOutCompositeOp:
1361 pixel=(MagickRealType) q[i];
1362 if (channel == AlphaPixelChannel)
1363 pixel=(MagickRealType) TransparentAlpha;
1366 case ClearCompositeOp:
1367 case CopyCompositeOp:
1368 case ReplaceCompositeOp:
1369 case SrcCompositeOp:
1371 if (channel == AlphaPixelChannel)
1373 pixel=(MagickRealType) TransparentAlpha;
1379 case BlendCompositeOp:
1380 case DissolveCompositeOp:
1382 if (channel == AlphaPixelChannel)
1384 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1388 pixel=(MagickRealType) source[channel];
1393 pixel=(MagickRealType) source[channel];
1397 q[i]=ClampToQuantum(pixel);
1399 q+=GetPixelChannels(image);
1403 Authentic composite:
1404 Sa: normalized source alpha.
1405 Da: normalized destination alpha.
1407 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1408 Da=QuantumScale*GetPixelAlpha(image,q);
1411 case BumpmapCompositeOp:
1413 alpha=GetPixelIntensity(composite_image,p)*Sa;
1416 case ColorBurnCompositeOp:
1417 case ColorDodgeCompositeOp:
1418 case DifferenceCompositeOp:
1419 case DivideDstCompositeOp:
1420 case DivideSrcCompositeOp:
1421 case ExclusionCompositeOp:
1422 case HardLightCompositeOp:
1423 case LinearBurnCompositeOp:
1424 case LinearDodgeCompositeOp:
1425 case LinearLightCompositeOp:
1426 case MathematicsCompositeOp:
1427 case MinusDstCompositeOp:
1428 case MinusSrcCompositeOp:
1429 case ModulusAddCompositeOp:
1430 case ModulusSubtractCompositeOp:
1431 case MultiplyCompositeOp:
1432 case OverlayCompositeOp:
1433 case PegtopLightCompositeOp:
1434 case PinLightCompositeOp:
1435 case ScreenCompositeOp:
1436 case SoftLightCompositeOp:
1437 case VividLightCompositeOp:
1439 alpha=RoundToUnity(Sa+Da-Sa*Da);
1442 case DarkenCompositeOp:
1443 case DstAtopCompositeOp:
1444 case DstInCompositeOp:
1446 case LightenCompositeOp:
1447 case SrcInCompositeOp:
1452 case DissolveCompositeOp:
1454 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1455 Sa+destination_dissolve*Da;
1458 case DstOverCompositeOp:
1460 alpha=Da*(-Sa)+Da+Sa;
1463 case DstOutCompositeOp:
1468 case OutCompositeOp:
1469 case SrcOutCompositeOp:
1474 case OverCompositeOp:
1475 case SrcOverCompositeOp:
1477 alpha=Sa*(-Da)+Sa+Da;
1480 case BlendCompositeOp:
1481 case PlusCompositeOp:
1483 alpha=RoundToUnity(Sa+Da);
1486 case XorCompositeOp:
1488 alpha=Sa+Da-2.0*Sa*Da;
1497 if (GetPixelMask(image,p) != 0)
1499 p+=GetPixelChannels(composite_image);
1500 q+=GetPixelChannels(image);
1505 case ColorizeCompositeOp:
1506 case HueCompositeOp:
1507 case LuminizeCompositeOp:
1508 case ModulateCompositeOp:
1509 case SaturateCompositeOp:
1511 GetPixelInfoPixel(composite_image,p,&source_pixel);
1512 GetPixelInfoPixel(image,q,&destination_pixel);
1518 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1531 channel=GetPixelChannelChannel(image,i);
1532 traits=GetPixelChannelTraits(image,channel);
1533 composite_traits=GetPixelChannelTraits(composite_image,channel);
1534 if (traits == UndefinedPixelTrait)
1536 if ((compose != IntensityCompositeOp) &&
1537 (composite_traits == UndefinedPixelTrait))
1541 Dc: destination color.
1543 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
1544 Dc=(MagickRealType) q[i];
1545 if ((traits & CopyPixelTrait) != 0)
1547 if (channel != AlphaPixelChannel)
1552 q[i]=ClampToQuantum(Sc);
1560 case AlphaCompositeOp:
1562 pixel=QuantumRange*Sa;
1565 case AtopCompositeOp:
1566 case CopyBlackCompositeOp:
1567 case CopyBlueCompositeOp:
1568 case CopyCyanCompositeOp:
1569 case CopyGreenCompositeOp:
1570 case CopyMagentaCompositeOp:
1571 case CopyRedCompositeOp:
1572 case CopyYellowCompositeOp:
1573 case SrcAtopCompositeOp:
1574 case DstCompositeOp:
1577 pixel=QuantumRange*Da;
1580 case ChangeMaskCompositeOp:
1585 if (Da > ((MagickRealType) QuantumRange/2.0))
1587 pixel=(MagickRealType) TransparentAlpha;
1590 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
1591 if (equivalent != MagickFalse)
1593 pixel=(MagickRealType) TransparentAlpha;
1596 pixel=(MagickRealType) OpaqueAlpha;
1599 case ClearCompositeOp:
1601 pixel=(MagickRealType) TransparentAlpha;
1604 case ColorizeCompositeOp:
1605 case HueCompositeOp:
1606 case LuminizeCompositeOp:
1607 case SaturateCompositeOp:
1609 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1611 pixel=QuantumRange*Da;
1614 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1616 pixel=QuantumRange*Sa;
1621 pixel=QuantumRange*Da;
1624 pixel=QuantumRange*Sa;
1627 case CopyAlphaCompositeOp:
1629 pixel=QuantumRange*Sa;
1630 if (composite_image->alpha_trait != BlendPixelTrait)
1631 pixel=GetPixelIntensity(composite_image,p);
1634 case CopyCompositeOp:
1635 case DisplaceCompositeOp:
1636 case DistortCompositeOp:
1637 case DstAtopCompositeOp:
1638 case ReplaceCompositeOp:
1639 case SrcCompositeOp:
1641 pixel=QuantumRange*Sa;
1644 case DarkenIntensityCompositeOp:
1646 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1647 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
1650 case IntensityCompositeOp:
1652 pixel=GetPixelIntensity(composite_image,p);
1655 case LightenIntensityCompositeOp:
1657 pixel=Sa*GetPixelIntensity(composite_image,p) >
1658 Da*GetPixelIntensity(image,q) ? Sa : Da;
1661 case ModulateCompositeOp:
1663 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1665 pixel=QuantumRange*Da;
1668 pixel=QuantumRange*Da;
1673 pixel=QuantumRange*alpha;
1677 q[i]=ClampToQuantum(pixel);
1681 Porter-Duff compositions:
1682 Sca: source normalized color multiplied by alpha.
1683 Dca: normalized destination color multiplied by alpha.
1685 Sca=QuantumScale*Sa*Sc;
1686 Dca=QuantumScale*Da*Dc;
1689 case DarkenCompositeOp:
1690 case LightenCompositeOp:
1691 case ModulusSubtractCompositeOp:
1699 gamma=PerceptibleReciprocal(alpha);
1703 case AlphaCompositeOp:
1705 pixel=QuantumRange*Sa;
1708 case AtopCompositeOp:
1709 case SrcAtopCompositeOp:
1711 pixel=Sc*Sa+Dc*(1.0-Sa);
1714 case BlendCompositeOp:
1716 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1719 case BlurCompositeOp:
1720 case DisplaceCompositeOp:
1721 case DistortCompositeOp:
1722 case CopyCompositeOp:
1723 case ReplaceCompositeOp:
1724 case SrcCompositeOp:
1729 case BumpmapCompositeOp:
1731 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1736 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1739 case ChangeMaskCompositeOp:
1744 case ClearCompositeOp:
1749 case ColorBurnCompositeOp:
1752 Refer to the March 2009 SVG specification.
1754 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1756 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1759 if (Sca < MagickEpsilon)
1761 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1764 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1765 Sca*(1.0-Da)+Dca*(1.0-Sa));
1768 case ColorDodgeCompositeOp:
1770 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1772 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1775 if (fabs(Sca-Sa) < MagickEpsilon)
1777 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1780 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
1784 case ColorizeCompositeOp:
1786 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1791 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1796 CompositeHCL(destination_pixel.red,destination_pixel.green,
1797 destination_pixel.blue,&sans,&sans,&luma);
1798 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1799 &hue,&chroma,&sans);
1800 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1803 case RedPixelChannel: pixel=red; break;
1804 case GreenPixelChannel: pixel=green; break;
1805 case BluePixelChannel: pixel=blue; break;
1806 default: pixel=Dc; break;
1810 case CopyAlphaCompositeOp:
1811 case IntensityCompositeOp:
1816 case CopyBlackCompositeOp:
1818 if (channel == BlackPixelChannel)
1819 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
1822 case CopyBlueCompositeOp:
1823 case CopyYellowCompositeOp:
1825 if (channel == BluePixelChannel)
1826 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
1829 case CopyGreenCompositeOp:
1830 case CopyMagentaCompositeOp:
1832 if (channel == GreenPixelChannel)
1833 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
1836 case CopyRedCompositeOp:
1837 case CopyCyanCompositeOp:
1839 if (channel == RedPixelChannel)
1840 pixel=(MagickRealType) GetPixelRed(composite_image,p);
1843 case DarkenCompositeOp:
1846 Darken is equivalent to a 'Minimum' method
1847 OR a greyscale version of a binary 'Or'
1848 OR the 'Intersection' of pixel sets.
1852 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1855 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1858 case DarkenIntensityCompositeOp:
1860 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1861 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
1864 case DifferenceCompositeOp:
1866 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1869 case DissolveCompositeOp:
1871 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1872 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1875 case DivideDstCompositeOp:
1877 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1879 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1882 if (fabs(Dca) < MagickEpsilon)
1884 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1887 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1890 case DivideSrcCompositeOp:
1892 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1894 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1897 if (fabs(Sca) < MagickEpsilon)
1899 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1902 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1905 case DstAtopCompositeOp:
1907 pixel=Dc*Da+Sc*(1.0-Da);
1910 case DstCompositeOp:
1916 case DstInCompositeOp:
1918 pixel=gamma*(Sa*Dc*Sa);
1921 case DstOutCompositeOp:
1923 pixel=gamma*(Da*Dc*(1.0-Sa));
1926 case DstOverCompositeOp:
1928 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1931 case ExclusionCompositeOp:
1933 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1937 case HardLightCompositeOp:
1941 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
1945 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1949 case HueCompositeOp:
1951 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1956 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1961 CompositeHCL(destination_pixel.red,destination_pixel.green,
1962 destination_pixel.blue,&hue,&chroma,&luma);
1963 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1965 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1968 case RedPixelChannel: pixel=red; break;
1969 case GreenPixelChannel: pixel=green; break;
1970 case BluePixelChannel: pixel=blue; break;
1971 default: pixel=Dc; break;
1976 case SrcInCompositeOp:
1978 pixel=gamma*(Da*Sc*Da);
1981 case LinearBurnCompositeOp:
1984 LinearBurn: as defined by Abode Photoshop, according to
1985 http://www.simplefilter.de/en/basics/mixmods.html is:
1987 f(Sc,Dc) = Sc + Dc - 1
1989 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1992 case LinearDodgeCompositeOp:
1994 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1997 case LinearLightCompositeOp:
2000 LinearLight: as defined by Abode Photoshop, according to
2001 http://www.simplefilter.de/en/basics/mixmods.html is:
2003 f(Sc,Dc) = Dc + 2*Sc - 1
2005 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
2008 case LightenCompositeOp:
2012 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
2015 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
2018 case LightenIntensityCompositeOp:
2021 Lighten is equivalent to a 'Maximum' method
2022 OR a greyscale version of a binary 'And'
2023 OR the 'Union' of pixel sets.
2025 pixel=Sa*GetPixelIntensity(composite_image,p) >
2026 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2029 case LuminizeCompositeOp:
2031 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
2036 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
2041 CompositeHCL(destination_pixel.red,destination_pixel.green,
2042 destination_pixel.blue,&hue,&chroma,&luma);
2043 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2045 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2048 case RedPixelChannel: pixel=red; break;
2049 case GreenPixelChannel: pixel=green; break;
2050 case BluePixelChannel: pixel=blue; break;
2051 default: pixel=Dc; break;
2055 case MathematicsCompositeOp:
2058 'Mathematics' a free form user control mathematical composition
2061 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2063 Where the arguments A,B,C,D are (currently) passed to composite
2064 as a command separated 'geometry' string in "compose:args" image
2067 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2069 Applying the SVG transparency formula (see above), we get...
2071 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2073 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2076 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2077 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2078 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2081 case MinusDstCompositeOp:
2083 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2086 case MinusSrcCompositeOp:
2089 Minus source from destination.
2093 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2096 case ModulateCompositeOp:
2101 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
2106 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2112 CompositeHCL(destination_pixel.red,destination_pixel.green,
2113 destination_pixel.blue,&hue,&chroma,&luma);
2114 luma+=(0.01*percent_luma*offset)/midpoint;
2115 chroma*=0.01*percent_chroma;
2116 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2119 case RedPixelChannel: pixel=red; break;
2120 case GreenPixelChannel: pixel=green; break;
2121 case BluePixelChannel: pixel=blue; break;
2122 default: pixel=Dc; break;
2126 case ModulusAddCompositeOp:
2129 if (pixel > QuantumRange)
2130 pixel-=(QuantumRange+1.0);
2131 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2134 case ModulusSubtractCompositeOp:
2138 pixel+=(QuantumRange+1.0);
2139 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2142 case MultiplyCompositeOp:
2144 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2147 case OutCompositeOp:
2148 case SrcOutCompositeOp:
2150 pixel=gamma*(Sa*Sc*(1.0-Da));
2153 case OverCompositeOp:
2154 case SrcOverCompositeOp:
2156 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
2159 case OverlayCompositeOp:
2163 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2167 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2171 case PegtopLightCompositeOp:
2174 PegTop: A Soft-Light alternative: A continuous version of the
2175 Softlight function, producing very similar results.
2177 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2179 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2181 if (fabs(Da) < MagickEpsilon)
2183 pixel=QuantumRange*gamma*(Sca);
2186 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2190 case PinLightCompositeOp:
2193 PinLight: A Photoshop 7 composition method
2194 http://www.simplefilter.de/en/basics/mixmods.html
2196 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2198 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2200 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2203 if ((Dca*Sa) > (2.0*Sca*Da))
2205 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2208 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2211 case PlusCompositeOp:
2213 pixel=gamma*(Sa*Sc+Da*Dc);
2216 case SaturateCompositeOp:
2218 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
2223 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
2228 CompositeHCL(destination_pixel.red,destination_pixel.green,
2229 destination_pixel.blue,&hue,&chroma,&luma);
2230 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2231 &sans,&chroma,&sans);
2232 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2235 case RedPixelChannel: pixel=red; break;
2236 case GreenPixelChannel: pixel=green; break;
2237 case BluePixelChannel: pixel=blue; break;
2238 default: pixel=Dc; break;
2242 case ScreenCompositeOp:
2245 Screen: a negated multiply:
2247 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2249 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2252 case SoftLightCompositeOp:
2255 Refer to the March 2009 SVG specification.
2259 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2260 Sca*(1.0-Da)+Dca*(1.0-Sa));
2263 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2265 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2266 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2270 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2271 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2274 case ThresholdCompositeOp:
2280 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2285 pixel=gamma*(Dc+delta*amount);
2288 case VividLightCompositeOp:
2291 VividLight: A Photoshop 7 composition method. See
2292 http://www.simplefilter.de/en/basics/mixmods.html.
2294 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2296 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2298 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2301 if ((2.0*Sca) <= Sa)
2303 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2304 (1.0-Da)+Dca*(1.0-Sa));
2307 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2311 case XorCompositeOp:
2313 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
2322 q[i]=ClampToQuantum(pixel);
2324 p+=GetPixelChannels(composite_image);
2325 channels=GetPixelChannels(composite_image);
2326 if (p >= (pixels+channels*composite_image->columns))
2328 q+=GetPixelChannels(image);
2330 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2332 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2337 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2338 #pragma omp critical (MagickCore_CompositeImage)
2340 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2342 if (proceed == MagickFalse)
2346 composite_view=DestroyCacheView(composite_view);
2347 image_view=DestroyCacheView(image_view);
2348 if (destination_image != (Image * ) NULL)
2349 destination_image=DestroyImage(destination_image);
2351 composite_image=DestroyImage(composite_image);
2356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2360 % T e x t u r e I m a g e %
2364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2366 % TextureImage() repeatedly tiles the texture image across and down the image
2369 % The format of the TextureImage method is:
2371 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2372 % ExceptionInfo *exception)
2374 % A description of each parameter follows:
2376 % o image: the image.
2378 % o texture_image: This image is the texture to layer on the background.
2381 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2382 ExceptionInfo *exception)
2384 #define TextureImageTag "Texture/Image"
2399 assert(image != (Image *) NULL);
2400 if (image->debug != MagickFalse)
2401 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2402 assert(image->signature == MagickSignature);
2403 if (texture == (const Image *) NULL)
2404 return(MagickFalse);
2405 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2406 return(MagickFalse);
2407 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2408 if (texture_image == (const Image *) NULL)
2409 return(MagickFalse);
2410 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2411 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2414 if ((image->compose != CopyCompositeOp) &&
2415 ((image->compose != OverCompositeOp) || (image->alpha_trait == BlendPixelTrait) ||
2416 (texture_image->alpha_trait == BlendPixelTrait)))
2419 Tile texture onto the image background.
2421 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2426 if (status == MagickFalse)
2428 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2433 thread_status=CompositeImage(image,texture_image,image->compose,
2434 MagickFalse,x+texture_image->tile_offset.x,y+
2435 texture_image->tile_offset.y,exception);
2436 if (thread_status == MagickFalse)
2438 status=thread_status;
2442 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2447 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2449 if (proceed == MagickFalse)
2453 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2454 image->rows,image->rows);
2455 texture_image=DestroyImage(texture_image);
2459 Tile texture onto the image background (optimized).
2462 texture_view=AcquireVirtualCacheView(texture_image,exception);
2463 image_view=AcquireAuthenticCacheView(image,exception);
2464 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2465 #pragma omp parallel for schedule(static,4) shared(status) \
2466 magick_threads(texture_image,image,1,1)
2468 for (y=0; y < (ssize_t) image->rows; y++)
2473 register const Quantum
2486 if (status == MagickFalse)
2488 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2489 (y+texture_image->tile_offset.y) % texture_image->rows,
2490 texture_image->columns,1,exception);
2491 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2492 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2497 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2503 width=texture_image->columns;
2504 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2505 width=image->columns-x;
2506 for (j=0; j < (ssize_t) width; j++)
2511 if (GetPixelMask(image,p) != 0)
2513 p+=GetPixelChannels(texture_image);
2514 q+=GetPixelChannels(image);
2517 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2526 channel=GetPixelChannelChannel(texture_image,i);
2527 texture_traits=GetPixelChannelTraits(texture_image,channel);
2528 traits=GetPixelChannelTraits(image,channel);
2529 if ((traits == UndefinedPixelTrait) ||
2530 (texture_traits == UndefinedPixelTrait))
2532 SetPixelChannel(image,channel,p[i],q);
2534 p+=GetPixelChannels(texture_image);
2535 q+=GetPixelChannels(image);
2538 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2539 if (sync == MagickFalse)
2541 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2546 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2548 if (proceed == MagickFalse)
2552 texture_view=DestroyCacheView(texture_view);
2553 image_view=DestroyCacheView(image_view);
2554 texture_image=DestroyImage(texture_image);