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/token.h"
76 #include "MagickCore/utility.h"
77 #include "MagickCore/utility-private.h"
78 #include "MagickCore/version.h"
81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85 % C o m p o s i t e I m a g e %
89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 % CompositeImage() returns the second image composited onto the first
92 % at the specified offset, using the specified composite method.
94 % The format of the CompositeImage method is:
96 % MagickBooleanType CompositeImage(Image *image,
97 % const Image *composite_image,const CompositeOperator compose,
98 % const MagickBooleanType clip_to_self,const ssize_t x_offset,
99 % const ssize_t y_offset,ExceptionInfo *exception)
101 % A description of each parameter follows:
103 % o image: the destination image, modified by he composition
105 % o composite_image: the composite (source) image.
107 % o compose: This operator affects how the composite is applied to
108 % the image. The operators and how they are utilized are listed here
109 % http://www.w3.org/TR/SVG12/#compositing.
111 % o clip_to_self: set to MagickTrue to limit composition to area composed.
113 % o x_offset: the column offset of the composited image.
115 % o y_offset: the row offset of the composited image.
117 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
120 % A string containing extra numerical arguments for specific compose
121 % methods, generally expressed as a 'geometry' or a comma separated list
124 % Compose methods needing such arguments include "BlendCompositeOp" and
125 % "DisplaceCompositeOp".
127 % o exception: return any errors or warnings in this structure.
132 Composition based on the SVG specification:
134 A Composition is defined by...
135 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
136 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
137 Y = 1 for source preserved
138 Z = 1 for destination preserved
140 Conversion to transparency (then optimized)
141 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
142 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
145 Sca = Sc*Sa normalized Source color divided by Source alpha
146 Dca = Dc*Da normalized Dest color divided by Dest alpha
147 Dc' = Dca'/Da' the desired color value for this channel.
149 Da' in in the follow formula as 'gamma' The resulting alpla value.
151 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
152 the following optimizations...
154 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
155 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
157 The above SVG definitions also definate that Mathematical Composition
158 methods should use a 'Over' blending mode for Alpha Channel.
159 It however was not applied for composition modes of 'Plus', 'Minus',
160 the modulus versions of 'Add' and 'Subtract'.
162 Mathematical operator changes to be applied from IM v6.7...
164 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
165 'ModulusAdd' and 'ModulusSubtract' for clarity.
167 2) All mathematical compositions work as per the SVG specification
168 with regard to blending. This now includes 'ModulusAdd' and
171 3) When the special channel flag 'sync' (syncronize channel updates)
172 is turned off (enabled by default) then mathematical compositions are
173 only performed on the channels specified, and are applied
174 independantally of each other. In other words the mathematics is
175 performed as 'pure' mathematical operations, rather than as image
178 static void CompositeHSB(const double red,const double green,
179 const double blue,double *hue,double *saturation,double *brightness)
187 Convert RGB to HSB colorspace.
189 assert(hue != (double *) NULL);
190 assert(saturation != (double *) NULL);
191 assert(brightness != (double *) NULL);
192 max=(red > green ? red : green);
195 min=(red < green ? red : green);
200 *brightness=(double) (QuantumScale*max);
201 if (fabs((double) max) < MagickEpsilon)
203 *saturation=(double) (1.0-min/max);
204 delta=(MagickRealType) max-min;
205 if (fabs(delta) < MagickEpsilon)
207 if (fabs((double) red-max) < MagickEpsilon)
208 *hue=(double) ((green-blue)/delta);
210 if (fabs((double) green-max) < MagickEpsilon)
211 *hue=(double) (2.0+(blue-red)/delta);
213 if (fabs((double) blue-max) < MagickEpsilon)
214 *hue=(double) (4.0+(red-green)/delta);
220 static void HSBComposite(const double hue,const double saturation,
221 const double brightness,double *red,double *green,double *blue)
231 Convert HSB to RGB colorspace.
233 assert(red != (double *) NULL);
234 assert(green != (double *) NULL);
235 assert(blue != (double *) NULL);
236 if (saturation == 0.0)
238 *red=(double) QuantumRange*brightness;
243 h=6.0*(hue-floor(hue));
244 f=h-floor((double) h);
245 p=brightness*(1.0-saturation);
246 q=brightness*(1.0-saturation*f);
247 t=brightness*(1.0-saturation*(1.0-f));
253 *red=(double) QuantumRange*brightness;
254 *green=(double) QuantumRange*t;
255 *blue=(double) QuantumRange*p;
260 *red=(double) QuantumRange*q;
261 *green=(double) QuantumRange*brightness;
262 *blue=(double) QuantumRange*p;
267 *red=(double) QuantumRange*p;
268 *green=(double) QuantumRange*brightness;
269 *blue=(double) QuantumRange*t;
274 *red=(double) QuantumRange*p;
275 *green=(double) QuantumRange*q;
276 *blue=(double) QuantumRange*brightness;
281 *red=(double) QuantumRange*t;
282 *green=(double) QuantumRange*p;
283 *blue=(double) QuantumRange*brightness;
288 *red=(double) QuantumRange*brightness;
289 *green=(double) QuantumRange*p;
290 *blue=(double) QuantumRange*q;
296 static inline double MagickMin(const double x,const double y)
303 static inline double MagickMax(const double x,const double y)
310 static MagickBooleanType CompositeOverImage(Image *image,
311 const Image *composite_image,const MagickBooleanType clip_to_self,
312 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
314 #define CompositeImageTag "Composite/Image"
334 composite_view=AcquireVirtualCacheView(composite_image,exception);
335 image_view=AcquireAuthenticCacheView(image,exception);
336 #if defined(MAGICKCORE_OPENMP_SUPPORT)
337 #pragma omp parallel for schedule(static,4) shared(progress,status) \
338 dynamic_number_threads(image,image->columns,image->rows,1)
340 for (y=0; y < (ssize_t) image->rows; y++)
345 register const Quantum
357 if (status == MagickFalse)
359 if (clip_to_self != MagickFalse)
363 if ((y-y_offset) >= (ssize_t) composite_image->rows)
367 If pixels is NULL, y is outside overlay region.
369 pixels=(Quantum *) NULL;
371 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
373 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
374 composite_image->columns,1,exception);
375 if (p == (const Quantum *) NULL)
382 p-=x_offset*GetPixelChannels(composite_image);
384 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
385 if (q == (Quantum *) NULL)
390 for (x=0; x < (ssize_t) image->columns; x++)
403 if (clip_to_self != MagickFalse)
407 q+=GetPixelChannels(image);
410 if ((x-x_offset) >= (ssize_t) composite_image->columns)
413 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
414 ((x-x_offset) >= (ssize_t) composite_image->columns))
417 source[MaxPixelChannels];
422 Dc: destination color.
424 if (GetPixelMask(image,q) != 0)
426 q+=GetPixelChannels(image);
429 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
431 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
440 channel=GetPixelChannelMapChannel(image,i);
441 traits=GetPixelChannelMapTraits(image,channel);
442 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
443 if ((traits == UndefinedPixelTrait) ||
444 (composite_traits == UndefinedPixelTrait))
446 q[i]=source[channel];
448 q+=GetPixelChannels(image);
453 Sa: normalized source alpha.
454 Da: normalized destination alpha.
456 if (GetPixelMask(composite_image,p) != 0)
458 p+=GetPixelChannels(composite_image);
459 channels=GetPixelChannels(composite_image);
460 if (p >= (pixels+channels*composite_image->columns))
462 q+=GetPixelChannels(image);
465 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
466 Da=QuantumScale*GetPixelAlpha(image,q);
467 alpha=Sa*(-Da)+Sa+Da;
468 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
477 channel=GetPixelChannelMapChannel(image,i);
478 traits=GetPixelChannelMapTraits(image,channel);
479 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
480 if ((traits == UndefinedPixelTrait) ||
481 (composite_traits == UndefinedPixelTrait))
483 if ((traits & CopyPixelTrait) != 0)
485 if (channel != AlphaPixelChannel)
490 q[i]=GetPixelChannel(composite_image,channel,p);
496 q[i]=ClampToQuantum(QuantumRange*alpha);
501 Dc: destination color.
503 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
504 Dc=(MagickRealType) q[i];
505 gamma=MagickEpsilonReciprocal(alpha);
506 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
508 p+=GetPixelChannels(composite_image);
509 channels=GetPixelChannels(composite_image);
510 if (p >= (pixels+channels*composite_image->columns))
512 q+=GetPixelChannels(image);
514 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
516 if (image->progress_monitor != (MagickProgressMonitor) NULL)
521 #if defined(MAGICKCORE_OPENMP_SUPPORT)
522 #pragma omp critical (MagickCore_CompositeImage)
524 proceed=SetImageProgress(image,CompositeImageTag,progress++,
526 if (proceed == MagickFalse)
530 composite_view=DestroyCacheView(composite_view);
531 image_view=DestroyCacheView(image_view);
535 MagickExport MagickBooleanType CompositeImage(Image *image,
536 const Image *composite_image,const CompositeOperator compose,
537 const MagickBooleanType clip_to_self,const ssize_t x_offset,
538 const ssize_t y_offset,ExceptionInfo *exception)
540 #define CompositeImageTag "Composite/Image"
560 destination_dissolve,
573 assert(image != (Image *) NULL);
574 assert(image->signature == MagickSignature);
575 if (image->debug != MagickFalse)
576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
577 assert(composite_image != (Image *) NULL);
578 assert(composite_image->signature == MagickSignature);
579 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
581 if (IsGrayColorspace(image->colorspace) != MagickFalse)
583 if (IsGrayColorspace(composite_image->colorspace) != MagickFalse)
584 (void) SetImageColorspace(image,RGBColorspace,exception);
586 (void) TransformImageColorspace(image,composite_image->colorspace,
589 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
591 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
595 destination_image=(Image *) NULL;
597 destination_dissolve=1.0;
598 percent_brightness=100.0;
599 percent_saturation=100.0;
604 case CopyCompositeOp:
606 if ((x_offset < 0) || (y_offset < 0))
608 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
610 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
613 composite_view=AcquireVirtualCacheView(composite_image,exception);
614 image_view=AcquireAuthenticCacheView(image,exception);
615 #if defined(MAGICKCORE_OPENMP_SUPPORT)
616 #pragma omp parallel for schedule(static,4) shared(status) \
617 dynamic_number_threads(image,image->columns,image->rows,1)
619 for (y=0; y < (ssize_t) composite_image->rows; y++)
624 register const Quantum
633 if (status == MagickFalse)
635 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
637 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
638 composite_image->columns,1,exception);
639 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
644 for (x=0; x < (ssize_t) composite_image->columns; x++)
649 if (GetPixelMask(composite_image,p) != 0)
651 p+=GetPixelChannels(composite_image);
652 q+=GetPixelChannels(image);
655 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
664 channel=GetPixelChannelMapChannel(composite_image,i);
665 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
666 traits=GetPixelChannelMapTraits(image,channel);
667 if ((traits == UndefinedPixelTrait) ||
668 (composite_traits == UndefinedPixelTrait))
670 SetPixelChannel(image,channel,p[i],q);
672 p+=GetPixelChannels(composite_image);
673 q+=GetPixelChannels(image);
675 sync=SyncCacheViewAuthenticPixels(image_view,exception);
676 if (sync == MagickFalse)
678 if (image->progress_monitor != (MagickProgressMonitor) NULL)
683 #if defined(MAGICKCORE_OPENMP_SUPPORT)
684 #pragma omp critical (MagickCore_CompositeImage)
686 proceed=SetImageProgress(image,CompositeImageTag,
687 (MagickOffsetType) y,image->rows);
688 if (proceed == MagickFalse)
692 composite_view=DestroyCacheView(composite_view);
693 image_view=DestroyCacheView(image_view);
696 case CopyAlphaCompositeOp:
697 case ChangeMaskCompositeOp:
698 case IntensityCompositeOp:
701 Modify destination outside the overlaid region and require an alpha
702 channel to exist, to add transparency.
704 if (image->matte == MagickFalse)
705 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
708 case BlurCompositeOp:
733 Blur Image by resampling.
735 Blur Image dictated by an overlay gradient map: X = red_channel;
736 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
738 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
740 if (destination_image == (Image *) NULL)
743 Gather the maximum blur sigma values from user.
745 SetGeometryInfo(&geometry_info);
747 value=GetImageArtifact(composite_image,"compose:args");
748 if (value != (char *) NULL)
749 flags=ParseGeometry(value,&geometry_info);
750 if ((flags & WidthValue) == 0 ) {
751 (void) ThrowMagickException(exception,GetMagickModule(),
752 OptionWarning,"InvalidSetting","'%s' '%s'",
753 "compose:args",value);
754 destination_image=DestroyImage(destination_image);
758 Users input sigma now needs to be converted to the EWA ellipse size.
759 The filter defaults to a sigma of 0.5 so to make this match the
760 users input the ellipse size needs to be doubled.
762 width=height=geometry_info.rho*2.0;
763 if ((flags & HeightValue) != 0 )
764 height=geometry_info.sigma*2.0;
766 /* default the unrotated ellipse width and height axis vectors */
771 /* rotate vectors if a rotation angle is given */
772 if ((flags & XValue) != 0 )
777 angle=DegreesToRadians(geometry_info.xi);
778 blur.x1=width*cos(angle);
779 blur.x2=width*sin(angle);
780 blur.y1=(-height*sin(angle));
781 blur.y2=height*cos(angle);
783 /* Otherwise lets set a angle range and calculate in the loop */
786 if ((flags & YValue) != 0 )
788 angle_start=DegreesToRadians(geometry_info.xi);
789 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
792 Set up a gaussian cylindrical filter for EWA Bluring.
794 As the minimum ellipse radius of support*1.0 the EWA algorithm
795 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
796 This means that even 'No Blur' will be still a little blurry!
798 The solution (as well as the problem of preventing any user
799 expert filter settings, is to set our own user settings, then
800 restore them afterwards.
802 resample_filter=AcquireResampleFilter(image,exception);
803 SetResampleFilter(resample_filter,GaussianFilter);
805 /* do the variable blurring of each pixel in image */
806 GetPixelInfo(image,&pixel);
807 composite_view=AcquireVirtualCacheView(composite_image,exception);
808 destination_view=AcquireAuthenticCacheView(destination_image,exception);
809 for (y=0; y < (ssize_t) composite_image->rows; y++)
814 register const Quantum
823 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
825 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
827 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
828 destination_image->columns,1,exception);
829 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
831 for (x=0; x < (ssize_t) composite_image->columns; x++)
833 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
835 p+=GetPixelChannels(composite_image);
838 if (fabs(angle_range) > MagickEpsilon)
843 angle=angle_start+angle_range*QuantumScale*
844 GetPixelBlue(composite_image,p);
845 blur.x1=width*cos(angle);
846 blur.x2=width*sin(angle);
847 blur.y1=(-height*sin(angle));
848 blur.y2=height*cos(angle);
851 if ( x == 10 && y == 60 ) {
852 fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
853 blur.x1, blur.x2, blur.y1, blur.y2);
854 fprintf(stderr, "scaled by=%lf,%lf\n",
855 QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
857 ScaleResampleFilter(resample_filter,
858 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
859 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
860 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
861 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
862 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
863 (double) y_offset+y,&pixel,exception);
864 SetPixelInfoPixel(destination_image,&pixel,q);
865 p+=GetPixelChannels(composite_image);
866 q+=GetPixelChannels(destination_image);
868 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
869 if (sync == MagickFalse)
872 resample_filter=DestroyResampleFilter(resample_filter);
873 composite_view=DestroyCacheView(composite_view);
874 destination_view=DestroyCacheView(destination_view);
875 composite_image=destination_image;
878 case DisplaceCompositeOp:
879 case DistortCompositeOp:
901 Displace/Distort based on overlay gradient map:
902 X = red_channel; Y = green_channel;
903 compose:args = x_scale[,y_scale[,center.x,center.y]]
905 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
907 if (destination_image == (Image *) NULL)
909 SetGeometryInfo(&geometry_info);
911 value=GetImageArtifact(composite_image,"compose:args");
912 if (value != (char *) NULL)
913 flags=ParseGeometry(value,&geometry_info);
914 if ((flags & (WidthValue|HeightValue)) == 0 )
916 if ((flags & AspectValue) == 0)
918 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
920 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
924 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
925 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
930 horizontal_scale=geometry_info.rho;
931 vertical_scale=geometry_info.sigma;
932 if ((flags & PercentValue) != 0)
934 if ((flags & AspectValue) == 0)
936 horizontal_scale*=(composite_image->columns-1.0)/200.0;
937 vertical_scale*=(composite_image->rows-1.0)/200.0;
941 horizontal_scale*=(image->columns-1.0)/200.0;
942 vertical_scale*=(image->rows-1.0)/200.0;
945 if ((flags & HeightValue) == 0)
946 vertical_scale=horizontal_scale;
949 Determine fixed center point for absolute distortion map
951 Displace offset relative to a fixed absolute point
952 Select that point according to +X+Y user inputs.
953 default = center of overlay image
954 arg flag '!' = locations/percentage relative to background image
956 center.x=(MagickRealType) x_offset;
957 center.y=(MagickRealType) y_offset;
958 if (compose == DistortCompositeOp)
960 if ((flags & XValue) == 0)
961 if ((flags & AspectValue) == 0)
962 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
965 center.x=((MagickRealType) image->columns-1)/2.0;
967 if ((flags & AspectValue) == 0)
968 center.x=(MagickRealType) x_offset+geometry_info.xi;
970 center.x=geometry_info.xi;
971 if ((flags & YValue) == 0)
972 if ((flags & AspectValue) == 0)
973 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
975 center.y=((MagickRealType) image->rows-1)/2.0;
977 if ((flags & AspectValue) == 0)
978 center.y=(MagickRealType) y_offset+geometry_info.psi;
980 center.y=geometry_info.psi;
983 Shift the pixel offset point as defined by the provided,
984 displacement/distortion map. -- Like a lens...
986 GetPixelInfo(image,&pixel);
987 image_view=AcquireVirtualCacheView(image,exception);
988 composite_view=AcquireVirtualCacheView(composite_image,exception);
989 destination_view=AcquireAuthenticCacheView(destination_image,exception);
990 for (y=0; y < (ssize_t) composite_image->rows; y++)
995 register const Quantum
1004 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1006 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1008 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
1009 destination_image->columns,1,exception);
1010 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1012 for (x=0; x < (ssize_t) composite_image->columns; x++)
1014 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1016 p+=GetPixelChannels(composite_image);
1020 Displace the offset.
1022 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1023 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1024 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1026 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1027 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1028 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1030 (void) InterpolatePixelInfo(image,image_view,
1031 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1034 Mask with the 'invalid pixel mask' in alpha channel.
1036 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1037 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
1038 SetPixelInfoPixel(destination_image,&pixel,q);
1039 p+=GetPixelChannels(composite_image);
1040 q+=GetPixelChannels(destination_image);
1042 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1043 if (sync == MagickFalse)
1046 destination_view=DestroyCacheView(destination_view);
1047 composite_view=DestroyCacheView(composite_view);
1048 image_view=DestroyCacheView(image_view);
1049 composite_image=destination_image;
1052 case DissolveCompositeOp:
1058 Geometry arguments to dissolve factors.
1060 value=GetImageArtifact(composite_image,"compose:args");
1061 if (value != (char *) NULL)
1063 flags=ParseGeometry(value,&geometry_info);
1064 source_dissolve=geometry_info.rho/100.0;
1065 destination_dissolve=1.0;
1066 if ((source_dissolve-MagickEpsilon) < 0.0)
1067 source_dissolve=0.0;
1068 if ((source_dissolve+MagickEpsilon) > 1.0)
1070 destination_dissolve=2.0-source_dissolve;
1071 source_dissolve=1.0;
1073 if ((flags & SigmaValue) != 0)
1074 destination_dissolve=geometry_info.sigma/100.0;
1075 if ((destination_dissolve-MagickEpsilon) < 0.0)
1076 destination_dissolve=0.0;
1077 /* posible speed up? -- from IMv6 update
1078 clip_to_self=MagickFalse;
1079 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1081 destination_dissolve=1.0;
1082 clip_to_self=MagickTrue;
1088 case BlendCompositeOp:
1093 value=GetImageArtifact(composite_image,"compose:args");
1094 if (value != (char *) NULL)
1096 flags=ParseGeometry(value,&geometry_info);
1097 source_dissolve=geometry_info.rho/100.0;
1098 destination_dissolve=1.0-source_dissolve;
1099 if ((flags & SigmaValue) != 0)
1100 destination_dissolve=geometry_info.sigma/100.0;
1104 case MathematicsCompositeOp:
1110 Just collect the values from "compose:args", setting.
1111 Unused values are set to zero automagically.
1113 Arguments are normally a comma separated list, so this probably should
1114 be changed to some 'general comma list' parser, (with a minimum
1117 SetGeometryInfo(&geometry_info);
1118 value=GetImageArtifact(composite_image,"compose:args");
1119 if (value != (char *) NULL)
1120 (void) ParseGeometry(value,&geometry_info);
1123 case ModulateCompositeOp:
1129 Determine the brightness and saturation scale.
1131 value=GetImageArtifact(composite_image,"compose:args");
1132 if (value != (char *) NULL)
1134 flags=ParseGeometry(value,&geometry_info);
1135 percent_brightness=geometry_info.rho;
1136 if ((flags & SigmaValue) != 0)
1137 percent_saturation=geometry_info.sigma;
1141 case ThresholdCompositeOp:
1147 Determine the amount and threshold.
1149 value=GetImageArtifact(composite_image,"compose:args");
1150 if (value != (char *) NULL)
1152 flags=ParseGeometry(value,&geometry_info);
1153 amount=geometry_info.rho;
1154 threshold=geometry_info.sigma;
1155 if ((flags & SigmaValue) == 0)
1158 threshold*=QuantumRange;
1169 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1170 composite_view=AcquireVirtualCacheView(composite_image,exception);
1171 image_view=AcquireAuthenticCacheView(image,exception);
1172 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1173 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1174 dynamic_number_threads(image,image->columns,image->rows,1)
1176 for (y=0; y < (ssize_t) image->rows; y++)
1193 register const Quantum
1202 if (status == MagickFalse)
1204 if (clip_to_self != MagickFalse)
1208 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1212 If pixels is NULL, y is outside overlay region.
1214 pixels=(Quantum *) NULL;
1216 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1218 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1219 composite_image->columns,1,exception);
1220 if (p == (const Quantum *) NULL)
1227 p-=x_offset*GetPixelChannels(composite_image);
1229 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1230 if (q == (Quantum *) NULL)
1238 GetPixelInfo(image,&destination_pixel);
1239 GetPixelInfo(composite_image,&source_pixel);
1240 for (x=0; x < (ssize_t) image->columns; x++)
1258 if (clip_to_self != MagickFalse)
1262 q+=GetPixelChannels(image);
1265 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1268 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1269 ((x-x_offset) >= (ssize_t) composite_image->columns))
1272 source[MaxPixelChannels];
1277 Dc: destination color.
1279 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1281 if (GetPixelMask(image,q) != 0)
1283 q+=GetPixelChannels(image);
1286 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1298 channel=GetPixelChannelMapChannel(image,i);
1299 traits=GetPixelChannelMapTraits(image,channel);
1300 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
1301 if ((traits == UndefinedPixelTrait) ||
1302 (composite_traits == UndefinedPixelTrait))
1306 case AlphaCompositeOp:
1307 case ChangeMaskCompositeOp:
1308 case CopyAlphaCompositeOp:
1309 case DstAtopCompositeOp:
1310 case DstInCompositeOp:
1312 case IntensityCompositeOp:
1313 case OutCompositeOp:
1314 case SrcInCompositeOp:
1315 case SrcOutCompositeOp:
1317 pixel=(MagickRealType) q[i];
1318 if (channel == AlphaPixelChannel)
1319 pixel=(MagickRealType) TransparentAlpha;
1322 case ClearCompositeOp:
1323 case CopyCompositeOp:
1324 case ReplaceCompositeOp:
1325 case SrcCompositeOp:
1327 if (channel == AlphaPixelChannel)
1329 pixel=(MagickRealType) TransparentAlpha;
1335 case BlendCompositeOp:
1336 case DissolveCompositeOp:
1338 if (channel == AlphaPixelChannel)
1340 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1344 pixel=(MagickRealType) source[channel];
1349 pixel=(MagickRealType) source[channel];
1353 q[i]=ClampToQuantum(pixel);
1355 q+=GetPixelChannels(image);
1359 Authentic composite:
1360 Sa: normalized source alpha.
1361 Da: normalized destination alpha.
1363 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1364 Da=QuantumScale*GetPixelAlpha(image,q);
1367 case BumpmapCompositeOp:
1369 alpha=GetPixelIntensity(composite_image,p)*Sa;
1372 case ColorBurnCompositeOp:
1373 case ColorDodgeCompositeOp:
1374 case DifferenceCompositeOp:
1375 case DivideDstCompositeOp:
1376 case DivideSrcCompositeOp:
1377 case ExclusionCompositeOp:
1378 case HardLightCompositeOp:
1379 case LinearBurnCompositeOp:
1380 case LinearDodgeCompositeOp:
1381 case LinearLightCompositeOp:
1382 case MathematicsCompositeOp:
1383 case MinusDstCompositeOp:
1384 case MinusSrcCompositeOp:
1385 case ModulusAddCompositeOp:
1386 case ModulusSubtractCompositeOp:
1387 case MultiplyCompositeOp:
1388 case OverlayCompositeOp:
1389 case PegtopLightCompositeOp:
1390 case PinLightCompositeOp:
1391 case ScreenCompositeOp:
1392 case SoftLightCompositeOp:
1393 case VividLightCompositeOp:
1395 alpha=RoundToUnity(Sa+Da-Sa*Da);
1398 case DarkenCompositeOp:
1399 case DstAtopCompositeOp:
1400 case DstInCompositeOp:
1402 case LightenCompositeOp:
1403 case SrcInCompositeOp:
1408 case DissolveCompositeOp:
1410 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1411 Sa+destination_dissolve*Da;
1414 case DstOverCompositeOp:
1416 alpha=Da*(-Sa)+Da+Sa;
1419 case DstOutCompositeOp:
1424 case OutCompositeOp:
1425 case SrcOutCompositeOp:
1430 case OverCompositeOp:
1431 case SrcOverCompositeOp:
1433 alpha=Sa*(-Da)+Sa+Da;
1436 case BlendCompositeOp:
1437 case PlusCompositeOp:
1439 alpha=RoundToUnity(Sa+Da);
1442 case XorCompositeOp:
1444 alpha=Sa+Da-2.0*Sa*Da;
1453 if (GetPixelMask(image,p) != 0)
1455 p+=GetPixelChannels(composite_image);
1456 q+=GetPixelChannels(image);
1461 case ColorizeCompositeOp:
1462 case HueCompositeOp:
1463 case LuminizeCompositeOp:
1464 case ModulateCompositeOp:
1465 case SaturateCompositeOp:
1467 GetPixelInfoPixel(composite_image,p,&source_pixel);
1468 GetPixelInfoPixel(image,q,&destination_pixel);
1474 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1489 channel=GetPixelChannelMapChannel(image,i);
1490 traits=GetPixelChannelMapTraits(image,channel);
1491 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
1492 if (traits == UndefinedPixelTrait)
1494 if ((compose != IntensityCompositeOp) &&
1495 (composite_traits == UndefinedPixelTrait))
1499 Dc: destination color.
1501 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
1502 Dc=(MagickRealType) q[i];
1503 if ((traits & CopyPixelTrait) != 0)
1505 if (channel != AlphaPixelChannel)
1510 q[i]=ClampToQuantum(Sc);
1518 case AlphaCompositeOp:
1520 pixel=QuantumRange*Sa;
1523 case AtopCompositeOp:
1524 case CopyBlackCompositeOp:
1525 case CopyBlueCompositeOp:
1526 case CopyCyanCompositeOp:
1527 case CopyGreenCompositeOp:
1528 case CopyMagentaCompositeOp:
1529 case CopyRedCompositeOp:
1530 case CopyYellowCompositeOp:
1531 case SrcAtopCompositeOp:
1532 case DstCompositeOp:
1535 pixel=QuantumRange*Da;
1538 case ChangeMaskCompositeOp:
1543 if (Da > ((MagickRealType) QuantumRange/2.0))
1545 pixel=(MagickRealType) TransparentAlpha;
1548 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
1549 if (equivalent != MagickFalse)
1551 pixel=(MagickRealType) TransparentAlpha;
1554 pixel=(MagickRealType) OpaqueAlpha;
1557 case ClearCompositeOp:
1559 pixel=(MagickRealType) TransparentAlpha;
1562 case ColorizeCompositeOp:
1563 case HueCompositeOp:
1564 case LuminizeCompositeOp:
1565 case SaturateCompositeOp:
1567 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1569 pixel=QuantumRange*Da;
1572 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1574 pixel=QuantumRange*Sa;
1579 pixel=QuantumRange*Da;
1582 pixel=QuantumRange*Sa;
1585 case CopyAlphaCompositeOp:
1587 pixel=QuantumRange*Sa;
1588 if (composite_image->matte == MagickFalse)
1589 pixel=GetPixelIntensity(composite_image,p);
1592 case CopyCompositeOp:
1593 case DisplaceCompositeOp:
1594 case DistortCompositeOp:
1595 case DstAtopCompositeOp:
1596 case ReplaceCompositeOp:
1597 case SrcCompositeOp:
1599 pixel=QuantumRange*Sa;
1602 case DarkenIntensityCompositeOp:
1604 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1605 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
1608 case IntensityCompositeOp:
1610 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
1613 case LightenIntensityCompositeOp:
1615 pixel=Sa*GetPixelIntensity(composite_image,p) >
1616 Da*GetPixelIntensity(image,q) ? Sa : Da;
1619 case ModulateCompositeOp:
1621 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1623 pixel=QuantumRange*Da;
1626 pixel=QuantumRange*Da;
1631 pixel=QuantumRange*alpha;
1635 q[i]=ClampToQuantum(pixel);
1639 Porter-Duff compositions:
1640 Sca: source normalized color multiplied by alpha.
1641 Dca: normalized destination color multiplied by alpha.
1643 Sca=QuantumScale*Sa*Sc;
1644 Dca=QuantumScale*Da*Dc;
1647 case DarkenCompositeOp:
1648 case LightenCompositeOp:
1649 case ModulusSubtractCompositeOp:
1657 gamma=MagickEpsilonReciprocal(alpha);
1661 case AlphaCompositeOp:
1663 pixel=QuantumRange*Sa;
1666 case AtopCompositeOp:
1667 case SrcAtopCompositeOp:
1669 pixel=Sc*Sa+Dc*(1.0-Sa);
1672 case BlendCompositeOp:
1674 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1677 case BlurCompositeOp:
1678 case DisplaceCompositeOp:
1679 case DistortCompositeOp:
1680 case CopyCompositeOp:
1681 case ReplaceCompositeOp:
1682 case SrcCompositeOp:
1687 case BumpmapCompositeOp:
1689 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1694 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1697 case ChangeMaskCompositeOp:
1702 case ClearCompositeOp:
1707 case ColorBurnCompositeOp:
1710 Refer to the March 2009 SVG specification.
1712 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1714 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1717 if (Sca < MagickEpsilon)
1719 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1722 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1723 Sca*(1.0-Da)+Dca*(1.0-Sa));
1726 case ColorDodgeCompositeOp:
1728 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1730 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1733 if (fabs(Sca-Sa) < MagickEpsilon)
1735 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1738 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
1742 case ColorizeCompositeOp:
1744 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1749 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1754 CompositeHSB(destination_pixel.red,destination_pixel.green,
1755 destination_pixel.blue,&sans,&sans,&brightness);
1756 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
1757 &hue,&saturation,&sans);
1758 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1761 case RedPixelChannel: pixel=red; break;
1762 case GreenPixelChannel: pixel=green; break;
1763 case BluePixelChannel: pixel=blue; break;
1764 default: pixel=Dc; break;
1768 case CopyAlphaCompositeOp:
1769 case IntensityCompositeOp:
1774 case CopyBlackCompositeOp:
1776 if (channel == BlackPixelChannel)
1777 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
1780 case CopyBlueCompositeOp:
1781 case CopyYellowCompositeOp:
1783 if (channel == BluePixelChannel)
1784 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
1787 case CopyGreenCompositeOp:
1788 case CopyMagentaCompositeOp:
1790 if (channel == GreenPixelChannel)
1791 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
1794 case CopyRedCompositeOp:
1795 case CopyCyanCompositeOp:
1797 if (channel == RedPixelChannel)
1798 pixel=(MagickRealType) GetPixelRed(composite_image,p);
1801 case DarkenCompositeOp:
1804 Darken is equivalent to a 'Minimum' method
1805 OR a greyscale version of a binary 'Or'
1806 OR the 'Intersection' of pixel sets.
1810 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1813 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1816 case DarkenIntensityCompositeOp:
1818 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1819 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
1822 case DifferenceCompositeOp:
1824 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1827 case DissolveCompositeOp:
1829 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1830 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1833 case DivideDstCompositeOp:
1835 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1837 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1840 if (fabs(Dca) < MagickEpsilon)
1842 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1845 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1848 case DivideSrcCompositeOp:
1850 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1852 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1855 if (fabs(Sca) < MagickEpsilon)
1857 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1860 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1863 case DstAtopCompositeOp:
1865 pixel=Dc*Da+Sc*(1.0-Da);
1868 case DstCompositeOp:
1874 case DstInCompositeOp:
1876 pixel=gamma*(Sa*Dc*Sa);
1879 case DstOutCompositeOp:
1881 pixel=gamma*(Da*Dc*(1.0-Sa));
1884 case DstOverCompositeOp:
1886 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1889 case ExclusionCompositeOp:
1891 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1895 case HardLightCompositeOp:
1899 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
1903 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1907 case HueCompositeOp:
1909 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1914 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1919 CompositeHSB(destination_pixel.red,destination_pixel.green,
1920 destination_pixel.blue,&hue,&saturation,&brightness);
1921 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
1923 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1926 case RedPixelChannel: pixel=red; break;
1927 case GreenPixelChannel: pixel=green; break;
1928 case BluePixelChannel: pixel=blue; break;
1929 default: pixel=Dc; break;
1934 case SrcInCompositeOp:
1936 pixel=gamma*(Da*Sc*Da);
1939 case LinearBurnCompositeOp:
1942 LinearBurn: as defined by Abode Photoshop, according to
1943 http://www.simplefilter.de/en/basics/mixmods.html is:
1945 f(Sc,Dc) = Sc + Dc - 1
1947 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1950 case LinearDodgeCompositeOp:
1952 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1955 case LinearLightCompositeOp:
1958 LinearLight: as defined by Abode Photoshop, according to
1959 http://www.simplefilter.de/en/basics/mixmods.html is:
1961 f(Sc,Dc) = Dc + 2*Sc - 1
1963 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1966 case LightenCompositeOp:
1970 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1973 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1976 case LightenIntensityCompositeOp:
1979 Lighten is equivalent to a 'Maximum' method
1980 OR a greyscale version of a binary 'And'
1981 OR the 'Union' of pixel sets.
1983 pixel=Sa*GetPixelIntensity(composite_image,p) >
1984 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1987 case LuminizeCompositeOp:
1989 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1994 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1999 CompositeHSB(destination_pixel.red,destination_pixel.green,
2000 destination_pixel.blue,&hue,&saturation,&brightness);
2001 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
2002 &sans,&sans,&brightness);
2003 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2006 case RedPixelChannel: pixel=red; break;
2007 case GreenPixelChannel: pixel=green; break;
2008 case BluePixelChannel: pixel=blue; break;
2009 default: pixel=Dc; break;
2013 case MathematicsCompositeOp:
2016 'Mathematics' a free form user control mathematical composition
2019 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2021 Where the arguments A,B,C,D are (currently) passed to composite
2022 as a command separated 'geometry' string in "compose:args" image
2025 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2027 Applying the SVG transparency formula (see above), we get...
2029 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2031 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2034 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2035 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2036 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2039 case MinusDstCompositeOp:
2041 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2044 case MinusSrcCompositeOp:
2047 Minus source from destination.
2051 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2054 case ModulateCompositeOp:
2059 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
2064 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2070 CompositeHSB(destination_pixel.red,destination_pixel.green,
2071 destination_pixel.blue,&hue,&saturation,&brightness);
2072 brightness+=(0.01*percent_brightness*offset)/midpoint;
2073 saturation*=0.01*percent_saturation;
2074 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2077 case RedPixelChannel: pixel=red; break;
2078 case GreenPixelChannel: pixel=green; break;
2079 case BluePixelChannel: pixel=blue; break;
2080 default: pixel=Dc; break;
2084 case ModulusAddCompositeOp:
2087 if (pixel > QuantumRange)
2088 pixel-=(QuantumRange+1.0);
2089 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2092 case ModulusSubtractCompositeOp:
2096 pixel+=(QuantumRange+1.0);
2097 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2100 case MultiplyCompositeOp:
2102 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2105 case OutCompositeOp:
2106 case SrcOutCompositeOp:
2108 pixel=gamma*(Sa*Sc*(1.0-Da));
2111 case OverCompositeOp:
2112 case SrcOverCompositeOp:
2114 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
2117 case OverlayCompositeOp:
2121 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2125 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2129 case PegtopLightCompositeOp:
2132 PegTop: A Soft-Light alternative: A continuous version of the
2133 Softlight function, producing very similar results.
2135 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2137 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2139 if (fabs(Da) < MagickEpsilon)
2141 pixel=QuantumRange*gamma*(Sca);
2144 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2148 case PinLightCompositeOp:
2151 PinLight: A Photoshop 7 composition method
2152 http://www.simplefilter.de/en/basics/mixmods.html
2154 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2156 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2158 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2161 if ((Dca*Sa) > (2.0*Sca*Da))
2163 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2166 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2169 case PlusCompositeOp:
2171 pixel=gamma*(Sa*Sc+Da*Dc);
2174 case SaturateCompositeOp:
2176 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
2181 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
2186 CompositeHSB(destination_pixel.red,destination_pixel.green,
2187 destination_pixel.blue,&hue,&saturation,&brightness);
2188 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
2189 &sans,&saturation,&sans);
2190 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2193 case RedPixelChannel: pixel=red; break;
2194 case GreenPixelChannel: pixel=green; break;
2195 case BluePixelChannel: pixel=blue; break;
2196 default: pixel=Dc; break;
2200 case ScreenCompositeOp:
2203 Screen: a negated multiply:
2205 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2207 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2210 case SoftLightCompositeOp:
2213 Refer to the March 2009 SVG specification.
2217 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2218 Sca*(1.0-Da)+Dca*(1.0-Sa));
2221 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2223 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2224 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2228 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2229 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2232 case ThresholdCompositeOp:
2238 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2243 pixel=gamma*(Dc+delta*amount);
2246 case VividLightCompositeOp:
2249 VividLight: A Photoshop 7 composition method. See
2250 http://www.simplefilter.de/en/basics/mixmods.html.
2252 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2254 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2256 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2259 if ((2.0*Sca) <= Sa)
2261 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2262 (1.0-Da)+Dca*(1.0-Sa));
2265 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2269 case XorCompositeOp:
2271 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
2280 q[i]=ClampToQuantum(pixel);
2282 p+=GetPixelChannels(composite_image);
2283 channels=GetPixelChannels(composite_image);
2284 if (p >= (pixels+channels*composite_image->columns))
2286 q+=GetPixelChannels(image);
2288 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2290 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2295 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2296 #pragma omp critical (MagickCore_CompositeImage)
2298 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2300 if (proceed == MagickFalse)
2304 composite_view=DestroyCacheView(composite_view);
2305 image_view=DestroyCacheView(image_view);
2306 if (destination_image != (Image * ) NULL)
2307 destination_image=DestroyImage(destination_image);
2312 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2316 % T e x t u r e I m a g e %
2320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2322 % TextureImage() repeatedly tiles the texture image across and down the image
2325 % The format of the TextureImage method is:
2327 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2328 % ExceptionInfo *exception)
2330 % A description of each parameter follows:
2332 % o image: the image.
2334 % o texture_image: This image is the texture to layer on the background.
2337 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2338 ExceptionInfo *exception)
2340 #define TextureImageTag "Texture/Image"
2355 assert(image != (Image *) NULL);
2356 if (image->debug != MagickFalse)
2357 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2358 assert(image->signature == MagickSignature);
2359 if (texture == (const Image *) NULL)
2360 return(MagickFalse);
2361 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2362 return(MagickFalse);
2363 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2364 if (texture_image == (const Image *) NULL)
2365 return(MagickFalse);
2366 if (IsGrayColorspace(texture_image->colorspace) != MagickFalse)
2367 (void) TransformImageColorspace(texture_image,RGBColorspace,exception);
2368 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2371 if ((image->compose != CopyCompositeOp) &&
2372 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2373 (texture_image->matte != MagickFalse)))
2376 Tile texture onto the image background.
2378 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2379 #pragma omp parallel for schedule(static) shared(status) \
2380 dynamic_number_threads(image,image->columns,image->rows,1)
2382 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2387 if (status == MagickFalse)
2389 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2394 thread_status=CompositeImage(image,texture_image,image->compose,
2395 MagickFalse,x+texture_image->tile_offset.x,y+
2396 texture_image->tile_offset.y,exception);
2397 if (thread_status == MagickFalse)
2399 status=thread_status;
2403 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2408 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2409 #pragma omp critical (MagickCore_TextureImage)
2411 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2413 if (proceed == MagickFalse)
2417 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2418 image->rows,image->rows);
2419 texture_image=DestroyImage(texture_image);
2423 Tile texture onto the image background (optimized).
2426 texture_view=AcquireVirtualCacheView(texture_image,exception);
2427 image_view=AcquireAuthenticCacheView(image,exception);
2428 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2429 #pragma omp parallel for schedule(static) shared(status) \
2430 dynamic_number_threads(image,image->columns,image->rows,1)
2432 for (y=0; y < (ssize_t) image->rows; y++)
2437 register const Quantum
2450 if (status == MagickFalse)
2452 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2453 (y+texture_image->tile_offset.y) % texture_image->rows,
2454 texture_image->columns,1,exception);
2455 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2456 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2461 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2467 width=texture_image->columns;
2468 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2469 width=image->columns-x;
2470 for (j=0; j < (ssize_t) width; j++)
2475 if (GetPixelMask(image,p) != 0)
2477 p+=GetPixelChannels(texture_image);
2478 q+=GetPixelChannels(image);
2481 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2490 channel=GetPixelChannelMapChannel(texture_image,i);
2491 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2492 traits=GetPixelChannelMapTraits(image,channel);
2493 if ((traits == UndefinedPixelTrait) ||
2494 (texture_traits == UndefinedPixelTrait))
2496 SetPixelChannel(image,channel,p[i],q);
2498 p+=GetPixelChannels(texture_image);
2499 q+=GetPixelChannels(image);
2502 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2503 if (sync == MagickFalse)
2505 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2510 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2511 #pragma omp critical (MagickCore_TextureImage)
2513 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2515 if (proceed == MagickFalse)
2519 texture_view=DestroyCacheView(texture_view);
2520 image_view=DestroyCacheView(image_view);
2521 texture_image=DestroyImage(texture_image);