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-2012 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 double MagickMin(const double x,const double y)
187 static inline double MagickMax(const double x,const double y)
194 static inline double ConvertHueToRGB(double m1,
195 double m2,double hue)
202 return(m1+6.0*(m2-m1)*hue);
206 return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
210 static void HCLComposite(const double hue,const double chroma,const double luma,
211 double *red,double *green,double *blue)
224 Convert HCL to RGB colorspace.
226 assert(red != (double *) NULL);
227 assert(green != (double *) NULL);
228 assert(blue != (double *) NULL);
231 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
235 if ((0.0 <= h) && (h < 1.0))
241 if ((1.0 <= h) && (h < 2.0))
247 if ((2.0 <= h) && (h < 3.0))
253 if ((3.0 <= h) && (h < 4.0))
259 if ((4.0 <= h) && (h < 5.0))
265 if ((5.0 <= h) && (h < 6.0))
270 m=luma-(0.298839*r+0.586811*g+0.114350*b);
272 Choose saturation strategy to clip it into the RGB cube; hue and luma are
273 preserved and chroma may be changed.
284 z=(1.0-luma)/(m+c-luma);
287 *red=QuantumRange*(z*r+m);
288 *green=QuantumRange*(z*g+m);
289 *blue=QuantumRange*(z*b+m);
292 static void CompositeHCL(const double red,const double green,const double blue,
293 double *hue,double *chroma,double *luma)
304 Convert RGB to HCL colorspace.
306 assert(hue != (double *) NULL);
307 assert(chroma != (double *) NULL);
308 assert(luma != (double *) NULL);
312 max=MagickMax(r,MagickMax(g,b));
313 c=max-(double) MagickMin(r,MagickMin(g,b));
319 h=fmod(6.0+(g-b)/c,6.0);
327 *chroma=QuantumScale*c;
328 *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b);
331 static MagickBooleanType CompositeOverImage(Image *image,
332 const Image *composite_image,const MagickBooleanType clip_to_self,
333 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
335 #define CompositeImageTag "Composite/Image"
355 composite_view=AcquireVirtualCacheView(composite_image,exception);
356 image_view=AcquireAuthenticCacheView(image,exception);
357 #if defined(MAGICKCORE_OPENMP_SUPPORT)
358 #pragma omp parallel for schedule(static,4) shared(progress,status) \
359 dynamic_number_threads(image,image->columns,image->rows,1)
361 for (y=0; y < (ssize_t) image->rows; y++)
366 register const Quantum
378 if (status == MagickFalse)
380 if (clip_to_self != MagickFalse)
384 if ((y-y_offset) >= (ssize_t) composite_image->rows)
388 If pixels is NULL, y is outside overlay region.
390 pixels=(Quantum *) NULL;
392 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
394 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
395 composite_image->columns,1,exception);
396 if (p == (const Quantum *) NULL)
403 p-=x_offset*GetPixelChannels(composite_image);
405 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
406 if (q == (Quantum *) NULL)
411 for (x=0; x < (ssize_t) image->columns; x++)
424 if (clip_to_self != MagickFalse)
428 q+=GetPixelChannels(image);
431 if ((x-x_offset) >= (ssize_t) composite_image->columns)
434 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
435 ((x-x_offset) >= (ssize_t) composite_image->columns))
438 source[MaxPixelChannels];
443 Dc: destination color.
445 if (GetPixelMask(image,q) != 0)
447 q+=GetPixelChannels(image);
450 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
452 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
461 channel=GetPixelChannelChannel(image,i);
462 traits=GetPixelChannelTraits(image,channel);
463 composite_traits=GetPixelChannelTraits(composite_image,channel);
464 if ((traits == UndefinedPixelTrait) ||
465 (composite_traits == UndefinedPixelTrait))
467 q[i]=source[channel];
469 q+=GetPixelChannels(image);
474 Sa: normalized source alpha.
475 Da: normalized destination alpha.
477 if (GetPixelMask(composite_image,p) != 0)
479 p+=GetPixelChannels(composite_image);
480 channels=GetPixelChannels(composite_image);
481 if (p >= (pixels+channels*composite_image->columns))
483 q+=GetPixelChannels(image);
486 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
487 Da=QuantumScale*GetPixelAlpha(image,q);
488 alpha=Sa*(-Da)+Sa+Da;
489 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
498 channel=GetPixelChannelChannel(image,i);
499 traits=GetPixelChannelTraits(image,channel);
500 composite_traits=GetPixelChannelTraits(composite_image,channel);
501 if ((traits == UndefinedPixelTrait) ||
502 (composite_traits == UndefinedPixelTrait))
504 if ((traits & CopyPixelTrait) != 0)
506 if (channel != AlphaPixelChannel)
511 q[i]=GetPixelChannel(composite_image,channel,p);
517 q[i]=ClampToQuantum(QuantumRange*alpha);
522 Dc: destination color.
524 Sc=(double) GetPixelChannel(composite_image,channel,p);
526 gamma=MagickEpsilonReciprocal(alpha);
527 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
529 p+=GetPixelChannels(composite_image);
530 channels=GetPixelChannels(composite_image);
531 if (p >= (pixels+channels*composite_image->columns))
533 q+=GetPixelChannels(image);
535 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
537 if (image->progress_monitor != (MagickProgressMonitor) NULL)
542 #if defined(MAGICKCORE_OPENMP_SUPPORT)
543 #pragma omp critical (MagickCore_CompositeImage)
545 proceed=SetImageProgress(image,CompositeImageTag,progress++,
547 if (proceed == MagickFalse)
551 composite_view=DestroyCacheView(composite_view);
552 image_view=DestroyCacheView(image_view);
556 MagickExport MagickBooleanType CompositeImage(Image *image,
557 const Image *composite,const CompositeOperator compose,
558 const MagickBooleanType clip_to_self,const ssize_t x_offset,
559 const ssize_t y_offset,ExceptionInfo *exception)
561 #define CompositeImageTag "Composite/Image"
582 destination_dissolve,
595 assert(image != (Image *) NULL);
596 assert(image->signature == MagickSignature);
597 if (image->debug != MagickFalse)
598 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
599 assert(composite!= (Image *) NULL);
600 assert(composite->signature == MagickSignature);
601 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
603 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
604 if (composite_image == (const Image *) NULL)
606 (void) SetImageColorspace(composite_image,image->colorspace,exception);
607 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
609 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
611 composite_image=DestroyImage(composite_image);
614 destination_image=(Image *) NULL;
616 destination_dissolve=1.0;
618 percent_chroma=100.0;
623 case CopyCompositeOp:
625 if ((x_offset < 0) || (y_offset < 0))
627 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
629 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
632 composite_view=AcquireVirtualCacheView(composite_image,exception);
633 image_view=AcquireAuthenticCacheView(image,exception);
634 #if defined(MAGICKCORE_OPENMP_SUPPORT)
635 #pragma omp parallel for schedule(static,4) shared(status) \
636 dynamic_number_threads(image,image->columns,image->rows,1)
638 for (y=0; y < (ssize_t) composite_image->rows; y++)
643 register const Quantum
652 if (status == MagickFalse)
654 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
656 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
657 composite_image->columns,1,exception);
658 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
663 for (x=0; x < (ssize_t) composite_image->columns; x++)
668 if (GetPixelMask(composite_image,p) != 0)
670 p+=GetPixelChannels(composite_image);
671 q+=GetPixelChannels(image);
674 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
683 channel=GetPixelChannelChannel(composite_image,i);
684 composite_traits=GetPixelChannelTraits(composite_image,channel);
685 traits=GetPixelChannelTraits(image,channel);
686 if ((traits == UndefinedPixelTrait) ||
687 (composite_traits == UndefinedPixelTrait))
689 SetPixelChannel(image,channel,p[i],q);
691 p+=GetPixelChannels(composite_image);
692 q+=GetPixelChannels(image);
694 sync=SyncCacheViewAuthenticPixels(image_view,exception);
695 if (sync == MagickFalse)
697 if (image->progress_monitor != (MagickProgressMonitor) NULL)
702 #if defined(MAGICKCORE_OPENMP_SUPPORT)
703 #pragma omp critical (MagickCore_CompositeImage)
705 proceed=SetImageProgress(image,CompositeImageTag,
706 (MagickOffsetType) y,image->rows);
707 if (proceed == MagickFalse)
711 composite_view=DestroyCacheView(composite_view);
712 image_view=DestroyCacheView(image_view);
713 composite_image=DestroyImage(composite_image);
716 case CopyAlphaCompositeOp:
717 case ChangeMaskCompositeOp:
718 case IntensityCompositeOp:
721 Modify destination outside the overlaid region and require an alpha
722 channel to exist, to add transparency.
724 if (image->alpha_trait != BlendPixelTrait)
725 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
728 case BlurCompositeOp:
753 Blur Image by resampling.
755 Blur Image dictated by an overlay gradient map: X = red_channel;
756 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
758 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
760 if (destination_image == (Image *) NULL)
762 composite_image=DestroyImage(composite_image);
766 Gather the maximum blur sigma values from user.
768 SetGeometryInfo(&geometry_info);
770 value=GetImageArtifact(composite_image,"compose:args");
771 if (value != (char *) NULL)
772 flags=ParseGeometry(value,&geometry_info);
773 if ((flags & WidthValue) == 0 ) {
774 (void) ThrowMagickException(exception,GetMagickModule(),
775 OptionWarning,"InvalidSetting","'%s' '%s'",
776 "compose:args",value);
777 composite_image=DestroyImage(composite_image);
778 destination_image=DestroyImage(destination_image);
782 Users input sigma now needs to be converted to the EWA ellipse size.
783 The filter defaults to a sigma of 0.5 so to make this match the
784 users input the ellipse size needs to be doubled.
786 width=height=geometry_info.rho*2.0;
787 if ((flags & HeightValue) != 0 )
788 height=geometry_info.sigma*2.0;
790 /* default the unrotated ellipse width and height axis vectors */
795 /* rotate vectors if a rotation angle is given */
796 if ((flags & XValue) != 0 )
801 angle=DegreesToRadians(geometry_info.xi);
802 blur.x1=width*cos(angle);
803 blur.x2=width*sin(angle);
804 blur.y1=(-height*sin(angle));
805 blur.y2=height*cos(angle);
807 /* Otherwise lets set a angle range and calculate in the loop */
810 if ((flags & YValue) != 0 )
812 angle_start=DegreesToRadians(geometry_info.xi);
813 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
816 Set up a gaussian cylindrical filter for EWA Bluring.
818 As the minimum ellipse radius of support*1.0 the EWA algorithm
819 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
820 This means that even 'No Blur' will be still a little blurry!
822 The solution (as well as the problem of preventing any user
823 expert filter settings, is to set our own user settings, then
824 restore them afterwards.
826 resample_filter=AcquireResampleFilter(image,exception);
827 SetResampleFilter(resample_filter,GaussianFilter);
829 /* do the variable blurring of each pixel in image */
830 GetPixelInfo(image,&pixel);
831 composite_view=AcquireVirtualCacheView(composite_image,exception);
832 destination_view=AcquireAuthenticCacheView(destination_image,exception);
833 for (y=0; y < (ssize_t) composite_image->rows; y++)
838 register const Quantum
847 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
849 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
851 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
852 destination_image->columns,1,exception);
853 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
855 for (x=0; x < (ssize_t) composite_image->columns; x++)
857 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
859 p+=GetPixelChannels(composite_image);
862 if (fabs(angle_range) > MagickEpsilon)
867 angle=angle_start+angle_range*QuantumScale*
868 GetPixelBlue(composite_image,p);
869 blur.x1=width*cos(angle);
870 blur.x2=width*sin(angle);
871 blur.y1=(-height*sin(angle));
872 blur.y2=height*cos(angle);
875 if ( x == 10 && y == 60 ) {
876 fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
877 blur.x1, blur.x2, blur.y1, blur.y2);
878 fprintf(stderr, "scaled by=%lf,%lf\n",
879 QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
881 ScaleResampleFilter(resample_filter,
882 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
883 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
884 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
885 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
886 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
887 (double) y_offset+y,&pixel,exception);
888 SetPixelInfoPixel(destination_image,&pixel,q);
889 p+=GetPixelChannels(composite_image);
890 q+=GetPixelChannels(destination_image);
892 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
893 if (sync == MagickFalse)
896 resample_filter=DestroyResampleFilter(resample_filter);
897 composite_view=DestroyCacheView(composite_view);
898 destination_view=DestroyCacheView(destination_view);
899 composite_image=DestroyImage(composite_image);
900 composite_image=destination_image;
903 case DisplaceCompositeOp:
904 case DistortCompositeOp:
926 Displace/Distort based on overlay gradient map:
927 X = red_channel; Y = green_channel;
928 compose:args = x_scale[,y_scale[,center.x,center.y]]
930 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
932 if (destination_image == (Image *) NULL)
934 composite_image=DestroyImage(composite_image);
937 SetGeometryInfo(&geometry_info);
939 value=GetImageArtifact(composite_image,"compose:args");
940 if (value != (char *) NULL)
941 flags=ParseGeometry(value,&geometry_info);
942 if ((flags & (WidthValue|HeightValue)) == 0 )
944 if ((flags & AspectValue) == 0)
946 horizontal_scale=(double) (composite_image->columns-1.0)/
948 vertical_scale=(double) (composite_image->rows-1.0)/2.0;
952 horizontal_scale=(double) (image->columns-1.0)/2.0;
953 vertical_scale=(double) (image->rows-1.0)/2.0;
958 horizontal_scale=geometry_info.rho;
959 vertical_scale=geometry_info.sigma;
960 if ((flags & PercentValue) != 0)
962 if ((flags & AspectValue) == 0)
964 horizontal_scale*=(composite_image->columns-1.0)/200.0;
965 vertical_scale*=(composite_image->rows-1.0)/200.0;
969 horizontal_scale*=(image->columns-1.0)/200.0;
970 vertical_scale*=(image->rows-1.0)/200.0;
973 if ((flags & HeightValue) == 0)
974 vertical_scale=horizontal_scale;
977 Determine fixed center point for absolute distortion map
979 Displace offset relative to a fixed absolute point
980 Select that point according to +X+Y user inputs.
981 default = center of overlay image
982 arg flag '!' = locations/percentage relative to background image
984 center.x=(double) x_offset;
985 center.y=(double) y_offset;
986 if (compose == DistortCompositeOp)
988 if ((flags & XValue) == 0)
989 if ((flags & AspectValue) == 0)
990 center.x=(double) x_offset+(composite_image->columns-1)/
993 center.x=((double) image->columns-1)/2.0;
995 if ((flags & AspectValue) == 0)
996 center.x=(double) x_offset+geometry_info.xi;
998 center.x=geometry_info.xi;
999 if ((flags & YValue) == 0)
1000 if ((flags & AspectValue) == 0)
1001 center.y=(double) y_offset+(composite_image->rows-1)/2.0;
1003 center.y=((double) image->rows-1)/2.0;
1005 if ((flags & AspectValue) == 0)
1006 center.y=(double) y_offset+geometry_info.psi;
1008 center.y=geometry_info.psi;
1011 Shift the pixel offset point as defined by the provided,
1012 displacement/distortion map. -- Like a lens...
1014 GetPixelInfo(image,&pixel);
1015 image_view=AcquireVirtualCacheView(image,exception);
1016 composite_view=AcquireVirtualCacheView(composite_image,exception);
1017 destination_view=AcquireAuthenticCacheView(destination_image,exception);
1018 for (y=0; y < (ssize_t) composite_image->rows; y++)
1023 register const Quantum
1032 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1034 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1036 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1037 destination_image->columns,1,exception);
1038 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1040 for (x=0; x < (ssize_t) composite_image->columns; x++)
1042 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1044 p+=GetPixelChannels(composite_image);
1048 Displace the offset.
1050 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1051 (((double) QuantumRange+1.0)/2.0)))/(((double)
1052 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1054 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1055 (((double) QuantumRange+1.0)/2.0)))/(((double)
1056 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1058 (void) InterpolatePixelInfo(image,image_view,
1059 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1062 Mask with the 'invalid pixel mask' in alpha channel.
1064 pixel.alpha=(double) QuantumRange*(1.0-(1.0-QuantumScale*
1065 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
1066 SetPixelInfoPixel(destination_image,&pixel,q);
1067 p+=GetPixelChannels(composite_image);
1068 q+=GetPixelChannels(destination_image);
1070 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1071 if (sync == MagickFalse)
1074 destination_view=DestroyCacheView(destination_view);
1075 composite_view=DestroyCacheView(composite_view);
1076 image_view=DestroyCacheView(image_view);
1077 composite_image=DestroyImage(composite_image);
1078 composite_image=destination_image;
1081 case DissolveCompositeOp:
1087 Geometry arguments to dissolve factors.
1089 value=GetImageArtifact(composite_image,"compose:args");
1090 if (value != (char *) NULL)
1092 flags=ParseGeometry(value,&geometry_info);
1093 source_dissolve=geometry_info.rho/100.0;
1094 destination_dissolve=1.0;
1095 if ((source_dissolve-MagickEpsilon) < 0.0)
1096 source_dissolve=0.0;
1097 if ((source_dissolve+MagickEpsilon) > 1.0)
1099 destination_dissolve=2.0-source_dissolve;
1100 source_dissolve=1.0;
1102 if ((flags & SigmaValue) != 0)
1103 destination_dissolve=geometry_info.sigma/100.0;
1104 if ((destination_dissolve-MagickEpsilon) < 0.0)
1105 destination_dissolve=0.0;
1106 /* posible speed up? -- from IMv6 update
1107 clip_to_self=MagickFalse;
1108 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1110 destination_dissolve=1.0;
1111 clip_to_self=MagickTrue;
1117 case BlendCompositeOp:
1122 value=GetImageArtifact(composite_image,"compose:args");
1123 if (value != (char *) NULL)
1125 flags=ParseGeometry(value,&geometry_info);
1126 source_dissolve=geometry_info.rho/100.0;
1127 destination_dissolve=1.0-source_dissolve;
1128 if ((flags & SigmaValue) != 0)
1129 destination_dissolve=geometry_info.sigma/100.0;
1133 case MathematicsCompositeOp:
1139 Just collect the values from "compose:args", setting.
1140 Unused values are set to zero automagically.
1142 Arguments are normally a comma separated list, so this probably should
1143 be changed to some 'general comma list' parser, (with a minimum
1146 SetGeometryInfo(&geometry_info);
1147 value=GetImageArtifact(composite_image,"compose:args");
1148 if (value != (char *) NULL)
1149 (void) ParseGeometry(value,&geometry_info);
1152 case ModulateCompositeOp:
1158 Determine the luma and chroma scale.
1160 value=GetImageArtifact(composite_image,"compose:args");
1161 if (value != (char *) NULL)
1163 flags=ParseGeometry(value,&geometry_info);
1164 percent_luma=geometry_info.rho;
1165 if ((flags & SigmaValue) != 0)
1166 percent_chroma=geometry_info.sigma;
1170 case ThresholdCompositeOp:
1176 Determine the amount and threshold.
1178 value=GetImageArtifact(composite_image,"compose:args");
1179 if (value != (char *) NULL)
1181 flags=ParseGeometry(value,&geometry_info);
1182 amount=geometry_info.rho;
1183 threshold=geometry_info.sigma;
1184 if ((flags & SigmaValue) == 0)
1187 threshold*=QuantumRange;
1198 midpoint=((double) QuantumRange+1.0)/2;
1199 composite_view=AcquireVirtualCacheView(composite_image,exception);
1200 image_view=AcquireAuthenticCacheView(image,exception);
1201 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1202 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1203 dynamic_number_threads(image,image->columns,image->rows,1)
1205 for (y=0; y < (ssize_t) image->rows; y++)
1222 register const Quantum
1231 if (status == MagickFalse)
1233 if (clip_to_self != MagickFalse)
1237 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1241 If pixels is NULL, y is outside overlay region.
1243 pixels=(Quantum *) NULL;
1245 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1247 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1248 composite_image->columns,1,exception);
1249 if (p == (const Quantum *) NULL)
1256 p-=x_offset*GetPixelChannels(composite_image);
1258 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1259 if (q == (Quantum *) NULL)
1267 GetPixelInfo(image,&destination_pixel);
1268 GetPixelInfo(composite_image,&source_pixel);
1269 for (x=0; x < (ssize_t) image->columns; x++)
1287 if (clip_to_self != MagickFalse)
1291 q+=GetPixelChannels(image);
1294 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1297 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1298 ((x-x_offset) >= (ssize_t) composite_image->columns))
1301 source[MaxPixelChannels];
1306 Dc: destination color.
1308 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1310 if (GetPixelMask(image,q) != 0)
1312 q+=GetPixelChannels(image);
1315 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1327 channel=GetPixelChannelChannel(image,i);
1328 traits=GetPixelChannelTraits(image,channel);
1329 composite_traits=GetPixelChannelTraits(composite_image,channel);
1330 if ((traits == UndefinedPixelTrait) ||
1331 (composite_traits == UndefinedPixelTrait))
1335 case AlphaCompositeOp:
1336 case ChangeMaskCompositeOp:
1337 case CopyAlphaCompositeOp:
1338 case DstAtopCompositeOp:
1339 case DstInCompositeOp:
1341 case IntensityCompositeOp:
1342 case OutCompositeOp:
1343 case SrcInCompositeOp:
1344 case SrcOutCompositeOp:
1346 pixel=(double) q[i];
1347 if (channel == AlphaPixelChannel)
1348 pixel=(double) TransparentAlpha;
1351 case ClearCompositeOp:
1352 case CopyCompositeOp:
1353 case ReplaceCompositeOp:
1354 case SrcCompositeOp:
1356 if (channel == AlphaPixelChannel)
1358 pixel=(double) TransparentAlpha;
1364 case BlendCompositeOp:
1365 case DissolveCompositeOp:
1367 if (channel == AlphaPixelChannel)
1369 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1373 pixel=(double) source[channel];
1378 pixel=(double) source[channel];
1382 q[i]=ClampToQuantum(pixel);
1384 q+=GetPixelChannels(image);
1388 Authentic composite:
1389 Sa: normalized source alpha.
1390 Da: normalized destination alpha.
1392 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1393 Da=QuantumScale*GetPixelAlpha(image,q);
1396 case BumpmapCompositeOp:
1398 alpha=GetPixelIntensity(composite_image,p)*Sa;
1401 case ColorBurnCompositeOp:
1402 case ColorDodgeCompositeOp:
1403 case DifferenceCompositeOp:
1404 case DivideDstCompositeOp:
1405 case DivideSrcCompositeOp:
1406 case ExclusionCompositeOp:
1407 case HardLightCompositeOp:
1408 case LinearBurnCompositeOp:
1409 case LinearDodgeCompositeOp:
1410 case LinearLightCompositeOp:
1411 case MathematicsCompositeOp:
1412 case MinusDstCompositeOp:
1413 case MinusSrcCompositeOp:
1414 case ModulusAddCompositeOp:
1415 case ModulusSubtractCompositeOp:
1416 case MultiplyCompositeOp:
1417 case OverlayCompositeOp:
1418 case PegtopLightCompositeOp:
1419 case PinLightCompositeOp:
1420 case ScreenCompositeOp:
1421 case SoftLightCompositeOp:
1422 case VividLightCompositeOp:
1424 alpha=RoundToUnity(Sa+Da-Sa*Da);
1427 case DarkenCompositeOp:
1428 case DstAtopCompositeOp:
1429 case DstInCompositeOp:
1431 case LightenCompositeOp:
1432 case SrcInCompositeOp:
1437 case DissolveCompositeOp:
1439 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1440 Sa+destination_dissolve*Da;
1443 case DstOverCompositeOp:
1445 alpha=Da*(-Sa)+Da+Sa;
1448 case DstOutCompositeOp:
1453 case OutCompositeOp:
1454 case SrcOutCompositeOp:
1459 case OverCompositeOp:
1460 case SrcOverCompositeOp:
1462 alpha=Sa*(-Da)+Sa+Da;
1465 case BlendCompositeOp:
1466 case PlusCompositeOp:
1468 alpha=RoundToUnity(Sa+Da);
1471 case XorCompositeOp:
1473 alpha=Sa+Da-2.0*Sa*Da;
1482 if (GetPixelMask(image,p) != 0)
1484 p+=GetPixelChannels(composite_image);
1485 q+=GetPixelChannels(image);
1490 case ColorizeCompositeOp:
1491 case HueCompositeOp:
1492 case LuminizeCompositeOp:
1493 case ModulateCompositeOp:
1494 case SaturateCompositeOp:
1496 GetPixelInfoPixel(composite_image,p,&source_pixel);
1497 GetPixelInfoPixel(image,q,&destination_pixel);
1503 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1518 channel=GetPixelChannelChannel(image,i);
1519 traits=GetPixelChannelTraits(image,channel);
1520 composite_traits=GetPixelChannelTraits(composite_image,channel);
1521 if (traits == UndefinedPixelTrait)
1523 if ((compose != IntensityCompositeOp) &&
1524 (composite_traits == UndefinedPixelTrait))
1528 Dc: destination color.
1530 Sc=(double) GetPixelChannel(composite_image,channel,p);
1532 if ((traits & CopyPixelTrait) != 0)
1534 if (channel != AlphaPixelChannel)
1539 q[i]=ClampToQuantum(Sc);
1547 case AlphaCompositeOp:
1549 pixel=QuantumRange*Sa;
1552 case AtopCompositeOp:
1553 case CopyBlackCompositeOp:
1554 case CopyBlueCompositeOp:
1555 case CopyCyanCompositeOp:
1556 case CopyGreenCompositeOp:
1557 case CopyMagentaCompositeOp:
1558 case CopyRedCompositeOp:
1559 case CopyYellowCompositeOp:
1560 case SrcAtopCompositeOp:
1561 case DstCompositeOp:
1564 pixel=QuantumRange*Da;
1567 case ChangeMaskCompositeOp:
1572 if (Da > ((double) QuantumRange/2.0))
1574 pixel=(double) TransparentAlpha;
1577 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
1578 if (equivalent != MagickFalse)
1580 pixel=(double) TransparentAlpha;
1583 pixel=(double) OpaqueAlpha;
1586 case ClearCompositeOp:
1588 pixel=(double) TransparentAlpha;
1591 case ColorizeCompositeOp:
1592 case HueCompositeOp:
1593 case LuminizeCompositeOp:
1594 case SaturateCompositeOp:
1596 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1598 pixel=QuantumRange*Da;
1601 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1603 pixel=QuantumRange*Sa;
1608 pixel=QuantumRange*Da;
1611 pixel=QuantumRange*Sa;
1614 case CopyAlphaCompositeOp:
1616 pixel=QuantumRange*Sa;
1617 if (composite_image->alpha_trait != BlendPixelTrait)
1618 pixel=GetPixelIntensity(composite_image,p);
1621 case CopyCompositeOp:
1622 case DisplaceCompositeOp:
1623 case DistortCompositeOp:
1624 case DstAtopCompositeOp:
1625 case ReplaceCompositeOp:
1626 case SrcCompositeOp:
1628 pixel=QuantumRange*Sa;
1631 case DarkenIntensityCompositeOp:
1633 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1634 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
1637 case IntensityCompositeOp:
1639 pixel=GetPixelIntensity(composite_image,p);
1642 case LightenIntensityCompositeOp:
1644 pixel=Sa*GetPixelIntensity(composite_image,p) >
1645 Da*GetPixelIntensity(image,q) ? Sa : Da;
1648 case ModulateCompositeOp:
1650 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1652 pixel=QuantumRange*Da;
1655 pixel=QuantumRange*Da;
1660 pixel=QuantumRange*alpha;
1664 q[i]=ClampToQuantum(pixel);
1668 Porter-Duff compositions:
1669 Sca: source normalized color multiplied by alpha.
1670 Dca: normalized destination color multiplied by alpha.
1672 Sca=QuantumScale*Sa*Sc;
1673 Dca=QuantumScale*Da*Dc;
1676 case DarkenCompositeOp:
1677 case LightenCompositeOp:
1678 case ModulusSubtractCompositeOp:
1686 gamma=MagickEpsilonReciprocal(alpha);
1690 case AlphaCompositeOp:
1692 pixel=QuantumRange*Sa;
1695 case AtopCompositeOp:
1696 case SrcAtopCompositeOp:
1698 pixel=Sc*Sa+Dc*(1.0-Sa);
1701 case BlendCompositeOp:
1703 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1706 case BlurCompositeOp:
1707 case DisplaceCompositeOp:
1708 case DistortCompositeOp:
1709 case CopyCompositeOp:
1710 case ReplaceCompositeOp:
1711 case SrcCompositeOp:
1716 case BumpmapCompositeOp:
1718 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1723 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1726 case ChangeMaskCompositeOp:
1731 case ClearCompositeOp:
1736 case ColorBurnCompositeOp:
1739 Refer to the March 2009 SVG specification.
1741 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1743 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1746 if (Sca < MagickEpsilon)
1748 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1751 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1752 Sca*(1.0-Da)+Dca*(1.0-Sa));
1755 case ColorDodgeCompositeOp:
1757 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1759 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1762 if (fabs(Sca-Sa) < MagickEpsilon)
1764 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1767 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
1771 case ColorizeCompositeOp:
1773 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1778 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1783 CompositeHCL(destination_pixel.red,destination_pixel.green,
1784 destination_pixel.blue,&sans,&sans,&luma);
1785 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1786 &hue,&chroma,&sans);
1787 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1790 case RedPixelChannel: pixel=red; break;
1791 case GreenPixelChannel: pixel=green; break;
1792 case BluePixelChannel: pixel=blue; break;
1793 default: pixel=Dc; break;
1797 case CopyAlphaCompositeOp:
1798 case IntensityCompositeOp:
1803 case CopyBlackCompositeOp:
1805 if (channel == BlackPixelChannel)
1806 pixel=(double) GetPixelBlack(composite_image,p);
1809 case CopyBlueCompositeOp:
1810 case CopyYellowCompositeOp:
1812 if (channel == BluePixelChannel)
1813 pixel=(double) GetPixelBlue(composite_image,p);
1816 case CopyGreenCompositeOp:
1817 case CopyMagentaCompositeOp:
1819 if (channel == GreenPixelChannel)
1820 pixel=(double) GetPixelGreen(composite_image,p);
1823 case CopyRedCompositeOp:
1824 case CopyCyanCompositeOp:
1826 if (channel == RedPixelChannel)
1827 pixel=(double) GetPixelRed(composite_image,p);
1830 case DarkenCompositeOp:
1833 Darken is equivalent to a 'Minimum' method
1834 OR a greyscale version of a binary 'Or'
1835 OR the 'Intersection' of pixel sets.
1839 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1842 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1845 case DarkenIntensityCompositeOp:
1847 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1848 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
1851 case DifferenceCompositeOp:
1853 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1856 case DissolveCompositeOp:
1858 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1859 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1862 case DivideDstCompositeOp:
1864 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1866 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1869 if (fabs(Dca) < MagickEpsilon)
1871 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1874 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1877 case DivideSrcCompositeOp:
1879 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1881 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1884 if (fabs(Sca) < MagickEpsilon)
1886 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1889 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1892 case DstAtopCompositeOp:
1894 pixel=Dc*Da+Sc*(1.0-Da);
1897 case DstCompositeOp:
1903 case DstInCompositeOp:
1905 pixel=gamma*(Sa*Dc*Sa);
1908 case DstOutCompositeOp:
1910 pixel=gamma*(Da*Dc*(1.0-Sa));
1913 case DstOverCompositeOp:
1915 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1918 case ExclusionCompositeOp:
1920 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1924 case HardLightCompositeOp:
1928 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
1932 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1936 case HueCompositeOp:
1938 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1943 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1948 CompositeHCL(destination_pixel.red,destination_pixel.green,
1949 destination_pixel.blue,&hue,&chroma,&luma);
1950 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1952 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1955 case RedPixelChannel: pixel=red; break;
1956 case GreenPixelChannel: pixel=green; break;
1957 case BluePixelChannel: pixel=blue; break;
1958 default: pixel=Dc; break;
1963 case SrcInCompositeOp:
1965 pixel=gamma*(Da*Sc*Da);
1968 case LinearBurnCompositeOp:
1971 LinearBurn: as defined by Abode Photoshop, according to
1972 http://www.simplefilter.de/en/basics/mixmods.html is:
1974 f(Sc,Dc) = Sc + Dc - 1
1976 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1979 case LinearDodgeCompositeOp:
1981 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1984 case LinearLightCompositeOp:
1987 LinearLight: as defined by Abode Photoshop, according to
1988 http://www.simplefilter.de/en/basics/mixmods.html is:
1990 f(Sc,Dc) = Dc + 2*Sc - 1
1992 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1995 case LightenCompositeOp:
1999 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
2002 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
2005 case LightenIntensityCompositeOp:
2008 Lighten is equivalent to a 'Maximum' method
2009 OR a greyscale version of a binary 'And'
2010 OR the 'Union' of pixel sets.
2012 pixel=Sa*GetPixelIntensity(composite_image,p) >
2013 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2016 case LuminizeCompositeOp:
2018 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
2023 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
2028 CompositeHCL(destination_pixel.red,destination_pixel.green,
2029 destination_pixel.blue,&hue,&chroma,&luma);
2030 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2032 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2035 case RedPixelChannel: pixel=red; break;
2036 case GreenPixelChannel: pixel=green; break;
2037 case BluePixelChannel: pixel=blue; break;
2038 default: pixel=Dc; break;
2042 case MathematicsCompositeOp:
2045 'Mathematics' a free form user control mathematical composition
2048 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2050 Where the arguments A,B,C,D are (currently) passed to composite
2051 as a command separated 'geometry' string in "compose:args" image
2054 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2056 Applying the SVG transparency formula (see above), we get...
2058 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2060 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2063 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2064 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2065 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2068 case MinusDstCompositeOp:
2070 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2073 case MinusSrcCompositeOp:
2076 Minus source from destination.
2080 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2083 case ModulateCompositeOp:
2088 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
2093 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2099 CompositeHCL(destination_pixel.red,destination_pixel.green,
2100 destination_pixel.blue,&hue,&chroma,&luma);
2101 luma+=(0.01*percent_luma*offset)/midpoint;
2102 chroma*=0.01*percent_chroma;
2103 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2106 case RedPixelChannel: pixel=red; break;
2107 case GreenPixelChannel: pixel=green; break;
2108 case BluePixelChannel: pixel=blue; break;
2109 default: pixel=Dc; break;
2113 case ModulusAddCompositeOp:
2116 if (pixel > QuantumRange)
2117 pixel-=(QuantumRange+1.0);
2118 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2121 case ModulusSubtractCompositeOp:
2125 pixel+=(QuantumRange+1.0);
2126 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2129 case MultiplyCompositeOp:
2131 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2134 case OutCompositeOp:
2135 case SrcOutCompositeOp:
2137 pixel=gamma*(Sa*Sc*(1.0-Da));
2140 case OverCompositeOp:
2141 case SrcOverCompositeOp:
2143 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
2146 case OverlayCompositeOp:
2150 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2154 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2158 case PegtopLightCompositeOp:
2161 PegTop: A Soft-Light alternative: A continuous version of the
2162 Softlight function, producing very similar results.
2164 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2166 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2168 if (fabs(Da) < MagickEpsilon)
2170 pixel=QuantumRange*gamma*(Sca);
2173 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2177 case PinLightCompositeOp:
2180 PinLight: A Photoshop 7 composition method
2181 http://www.simplefilter.de/en/basics/mixmods.html
2183 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2185 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2187 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2190 if ((Dca*Sa) > (2.0*Sca*Da))
2192 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2195 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2198 case PlusCompositeOp:
2200 pixel=gamma*(Sa*Sc+Da*Dc);
2203 case SaturateCompositeOp:
2205 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
2210 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
2215 CompositeHCL(destination_pixel.red,destination_pixel.green,
2216 destination_pixel.blue,&hue,&chroma,&luma);
2217 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2218 &sans,&chroma,&sans);
2219 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2222 case RedPixelChannel: pixel=red; break;
2223 case GreenPixelChannel: pixel=green; break;
2224 case BluePixelChannel: pixel=blue; break;
2225 default: pixel=Dc; break;
2229 case ScreenCompositeOp:
2232 Screen: a negated multiply:
2234 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2236 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2239 case SoftLightCompositeOp:
2242 Refer to the March 2009 SVG specification.
2246 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2247 Sca*(1.0-Da)+Dca*(1.0-Sa));
2250 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2252 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2253 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2257 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2258 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2261 case ThresholdCompositeOp:
2267 if ((double) fabs((double) (2.0*delta)) < threshold)
2272 pixel=gamma*(Dc+delta*amount);
2275 case VividLightCompositeOp:
2278 VividLight: A Photoshop 7 composition method. See
2279 http://www.simplefilter.de/en/basics/mixmods.html.
2281 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2283 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2285 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2288 if ((2.0*Sca) <= Sa)
2290 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2291 (1.0-Da)+Dca*(1.0-Sa));
2294 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2298 case XorCompositeOp:
2300 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
2309 q[i]=ClampToQuantum(pixel);
2311 p+=GetPixelChannels(composite_image);
2312 channels=GetPixelChannels(composite_image);
2313 if (p >= (pixels+channels*composite_image->columns))
2315 q+=GetPixelChannels(image);
2317 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2319 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2324 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2325 #pragma omp critical (MagickCore_CompositeImage)
2327 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2329 if (proceed == MagickFalse)
2333 composite_view=DestroyCacheView(composite_view);
2334 image_view=DestroyCacheView(image_view);
2335 if (destination_image != (Image * ) NULL)
2336 destination_image=DestroyImage(destination_image);
2338 composite_image=DestroyImage(composite_image);
2339 if (status != MagickFalse)
2340 (void) ClampImage(image,exception);
2345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2349 % T e x t u r e I m a g e %
2353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2355 % TextureImage() repeatedly tiles the texture image across and down the image
2358 % The format of the TextureImage method is:
2360 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2361 % ExceptionInfo *exception)
2363 % A description of each parameter follows:
2365 % o image: the image.
2367 % o texture_image: This image is the texture to layer on the background.
2370 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2371 ExceptionInfo *exception)
2373 #define TextureImageTag "Texture/Image"
2388 assert(image != (Image *) NULL);
2389 if (image->debug != MagickFalse)
2390 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2391 assert(image->signature == MagickSignature);
2392 if (texture == (const Image *) NULL)
2393 return(MagickFalse);
2394 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2395 return(MagickFalse);
2396 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2397 if (texture_image == (const Image *) NULL)
2398 return(MagickFalse);
2399 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2400 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2403 if ((image->compose != CopyCompositeOp) &&
2404 ((image->compose != OverCompositeOp) || (image->alpha_trait == BlendPixelTrait) ||
2405 (texture_image->alpha_trait == BlendPixelTrait)))
2408 Tile texture onto the image background.
2410 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2411 #pragma omp parallel for schedule(static) shared(status) \
2412 dynamic_number_threads(image,image->columns,image->rows,1)
2414 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2419 if (status == MagickFalse)
2421 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2426 thread_status=CompositeImage(image,texture_image,image->compose,
2427 MagickFalse,x+texture_image->tile_offset.x,y+
2428 texture_image->tile_offset.y,exception);
2429 if (thread_status == MagickFalse)
2431 status=thread_status;
2435 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2440 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2441 #pragma omp critical (MagickCore_TextureImage)
2443 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2445 if (proceed == MagickFalse)
2449 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2450 image->rows,image->rows);
2451 texture_image=DestroyImage(texture_image);
2455 Tile texture onto the image background (optimized).
2458 texture_view=AcquireVirtualCacheView(texture_image,exception);
2459 image_view=AcquireAuthenticCacheView(image,exception);
2460 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2461 #pragma omp parallel for schedule(static) shared(status) \
2462 dynamic_number_threads(image,image->columns,image->rows,1)
2464 for (y=0; y < (ssize_t) image->rows; y++)
2469 register const Quantum
2482 if (status == MagickFalse)
2484 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2485 (y+texture_image->tile_offset.y) % texture_image->rows,
2486 texture_image->columns,1,exception);
2487 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2488 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2493 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2499 width=texture_image->columns;
2500 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2501 width=image->columns-x;
2502 for (j=0; j < (ssize_t) width; j++)
2507 if (GetPixelMask(image,p) != 0)
2509 p+=GetPixelChannels(texture_image);
2510 q+=GetPixelChannels(image);
2513 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2522 channel=GetPixelChannelChannel(texture_image,i);
2523 texture_traits=GetPixelChannelTraits(texture_image,channel);
2524 traits=GetPixelChannelTraits(image,channel);
2525 if ((traits == UndefinedPixelTrait) ||
2526 (texture_traits == UndefinedPixelTrait))
2528 SetPixelChannel(image,channel,p[i],q);
2530 p+=GetPixelChannels(texture_image);
2531 q+=GetPixelChannels(image);
2534 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2535 if (sync == MagickFalse)
2537 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2542 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2543 #pragma omp critical (MagickCore_TextureImage)
2545 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2547 if (proceed == MagickFalse)
2551 texture_view=DestroyCacheView(texture_view);
2552 image_view=DestroyCacheView(image_view);
2553 texture_image=DestroyImage(texture_image);