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-2016 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-private.h"
47 #include "MagickCore/cache-view.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/color-private.h"
52 #include "MagickCore/colorspace.h"
53 #include "MagickCore/colorspace-private.h"
54 #include "MagickCore/composite.h"
55 #include "MagickCore/composite-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/draw.h"
58 #include "MagickCore/fx.h"
59 #include "MagickCore/gem.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/image.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/monitor.h"
66 #include "MagickCore/monitor-private.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/option.h"
69 #include "MagickCore/pixel-accessor.h"
70 #include "MagickCore/property.h"
71 #include "MagickCore/quantum.h"
72 #include "MagickCore/resample.h"
73 #include "MagickCore/resource_.h"
74 #include "MagickCore/string_.h"
75 #include "MagickCore/thread-private.h"
76 #include "MagickCore/threshold.h"
77 #include "MagickCore/token.h"
78 #include "MagickCore/utility.h"
79 #include "MagickCore/utility-private.h"
80 #include "MagickCore/version.h"
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 % C o m p o s i t e I m a g e %
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93 % CompositeImage() returns the second image composited onto the first
94 % at the specified offset, using the specified composite method.
96 % The format of the CompositeImage method is:
98 % MagickBooleanType CompositeImage(Image *image,
99 % const Image *source_image,const CompositeOperator compose,
100 % const MagickBooleanType clip_to_self,const ssize_t x_offset,
101 % const ssize_t y_offset,ExceptionInfo *exception)
103 % A description of each parameter follows:
105 % o image: the canvas image, modified by he composition
107 % o source_image: the source image.
109 % o compose: This operator affects how the composite is applied to
110 % the image. The operators and how they are utilized are listed here
111 % http://www.w3.org/TR/SVG12/#compositing.
113 % o clip_to_self: set to MagickTrue to limit composition to area composed.
115 % o x_offset: the column offset of the composited image.
117 % o y_offset: the row offset of the composited image.
119 % Extra Controls from Image meta-data in 'image' (artifacts)
122 % A string containing extra numerical arguments for specific compose
123 % methods, generally expressed as a 'geometry' or a comma separated list
126 % Compose methods needing such arguments include "BlendCompositeOp" and
127 % "DisplaceCompositeOp".
129 % o exception: return any errors or warnings in this structure.
134 Composition based on the SVG specification:
136 A Composition is defined by...
137 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
138 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
139 Y = 1 for source preserved
140 Z = 1 for canvas preserved
142 Conversion to transparency (then optimized)
143 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
144 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
147 Sca = Sc*Sa normalized Source color divided by Source alpha
148 Dca = Dc*Da normalized Dest color divided by Dest alpha
149 Dc' = Dca'/Da' the desired color value for this channel.
151 Da' in in the follow formula as 'gamma' The resulting alpla value.
153 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
154 the following optimizations...
156 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
157 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
159 The above SVG definitions also definate that Mathematical Composition
160 methods should use a 'Over' blending mode for Alpha Channel.
161 It however was not applied for composition modes of 'Plus', 'Minus',
162 the modulus versions of 'Add' and 'Subtract'.
164 Mathematical operator changes to be applied from IM v6.7...
166 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
167 'ModulusAdd' and 'ModulusSubtract' for clarity.
169 2) All mathematical compositions work as per the SVG specification
170 with regard to blending. This now includes 'ModulusAdd' and
173 3) When the special channel flag 'sync' (syncronize channel updates)
174 is turned off (enabled by default) then mathematical compositions are
175 only performed on the channels specified, and are applied
176 independantally of each other. In other words the mathematics is
177 performed as 'pure' mathematical operations, rather than as image
181 static void HCLComposite(const MagickRealType hue,const MagickRealType chroma,
182 const MagickRealType luma,MagickRealType *red,MagickRealType *green,
183 MagickRealType *blue)
195 Convert HCL to RGB colorspace.
197 assert(red != (MagickRealType *) NULL);
198 assert(green != (MagickRealType *) NULL);
199 assert(blue != (MagickRealType *) NULL);
202 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
206 if ((0.0 <= h) && (h < 1.0))
212 if ((1.0 <= h) && (h < 2.0))
218 if ((2.0 <= h) && (h < 3.0))
224 if ((3.0 <= h) && (h < 4.0))
230 if ((4.0 <= h) && (h < 5.0))
236 if ((5.0 <= h) && (h < 6.0))
241 m=luma-(0.298839*r+0.586811*g+0.114350*b);
242 *red=QuantumRange*(r+m);
243 *green=QuantumRange*(g+m);
244 *blue=QuantumRange*(b+m);
247 static void CompositeHCL(const MagickRealType red,const MagickRealType green,
248 const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
249 MagickRealType *luma)
260 Convert RGB to HCL colorspace.
262 assert(hue != (MagickRealType *) NULL);
263 assert(chroma != (MagickRealType *) NULL);
264 assert(luma != (MagickRealType *) NULL);
268 max=MagickMax(r,MagickMax(g,b));
269 c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
275 h=fmod((g-b)/c+6.0,6.0);
283 *chroma=QuantumScale*c;
284 *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b);
287 static MagickBooleanType CompositeOverImage(Image *image,
288 const Image *source_image,const MagickBooleanType clip_to_self,
289 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
291 #define CompositeImageTag "Composite/Image"
316 value=GetImageArtifact(image,"compose:clamp");
317 if (value != (const char *) NULL)
318 clamp=IsStringTrue(value);
319 source_view=AcquireVirtualCacheView(source_image,exception);
320 image_view=AcquireAuthenticCacheView(image,exception);
321 #if defined(MAGICKCORE_OPENMP_SUPPORT)
322 #pragma omp parallel for schedule(static,4) shared(progress,status) \
323 magick_threads(source_image,image,image->rows,1)
325 for (y=0; y < (ssize_t) image->rows; y++)
330 register const Quantum
342 if (status == MagickFalse)
344 if (clip_to_self != MagickFalse)
348 if ((y-y_offset) >= (ssize_t) source_image->rows)
352 If pixels is NULL, y is outside overlay region.
354 pixels=(Quantum *) NULL;
356 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
358 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
359 source_image->columns,1,exception);
360 if (p == (const Quantum *) NULL)
367 p-=x_offset*GetPixelChannels(source_image);
369 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
370 if (q == (Quantum *) NULL)
375 for (x=0; x < (ssize_t) image->columns; x++)
389 if (clip_to_self != MagickFalse)
393 q+=GetPixelChannels(image);
396 if ((x-x_offset) >= (ssize_t) source_image->columns)
399 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
400 ((x-x_offset) >= (ssize_t) source_image->columns))
403 source[MaxPixelChannels];
410 if (GetPixelReadMask(image,q) == 0)
412 q+=GetPixelChannels(image);
415 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,
417 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
419 PixelChannel channel=GetPixelChannelChannel(image,i);
420 PixelTrait traits=GetPixelChannelTraits(image,channel);
421 PixelTrait source_traits=GetPixelChannelTraits(source_image,
423 if ((traits == UndefinedPixelTrait) ||
424 (source_traits == UndefinedPixelTrait))
426 q[i]=source[channel];
428 q+=GetPixelChannels(image);
433 Sa: normalized source alpha.
434 Da: normalized canvas alpha.
436 if (GetPixelReadMask(source_image,p) == 0)
438 p+=GetPixelChannels(source_image);
439 channels=GetPixelChannels(source_image);
440 if (p >= (pixels+channels*source_image->columns))
442 q+=GetPixelChannels(image);
445 Sa=QuantumScale*GetPixelAlpha(source_image,p);
446 Da=QuantumScale*GetPixelAlpha(image,q);
448 gamma=PerceptibleReciprocal(gamma);
449 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
451 PixelChannel channel=GetPixelChannelChannel(image,i);
452 PixelTrait traits=GetPixelChannelTraits(image,channel);
453 PixelTrait source_traits=GetPixelChannelTraits(source_image,
455 if ((traits == UndefinedPixelTrait) ||
456 (source_traits == UndefinedPixelTrait))
458 if ((traits & CopyPixelTrait) != 0)
463 q[i]=GetPixelChannel(source_image,channel,p);
466 if (channel == AlphaPixelChannel)
471 q[i]=clamp != MagickFalse ?
472 ClampPixel(QuantumRange*(Sa+Da-Sa*Da)) :
473 ClampToQuantum(QuantumRange*(Sa+Da-Sa*Da));
480 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
481 Dc=(MagickRealType) q[i];
482 Sca=QuantumScale*Sa*Sc;
483 Dca=QuantumScale*Da*Dc;
484 q[i]=clamp != MagickFalse ?
485 ClampPixel(gamma*QuantumRange*(Sca+Dca*(1.0-Sa))) :
486 ClampToQuantum(gamma*QuantumRange*(Sca+Dca*(1.0-Sa)));
488 p+=GetPixelChannels(source_image);
489 channels=GetPixelChannels(source_image);
490 if (p >= (pixels+channels*source_image->columns))
492 q+=GetPixelChannels(image);
494 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
496 if (image->progress_monitor != (MagickProgressMonitor) NULL)
501 #if defined(MAGICKCORE_OPENMP_SUPPORT)
502 #pragma omp critical (MagickCore_CompositeImage)
504 proceed=SetImageProgress(image,CompositeImageTag,progress++,
506 if (proceed == MagickFalse)
510 source_view=DestroyCacheView(source_view);
511 image_view=DestroyCacheView(image_view);
515 MagickExport MagickBooleanType CompositeImage(Image *image,
516 const Image *composite,const CompositeOperator compose,
517 const MagickBooleanType clip_to_self,const ssize_t x_offset,
518 const ssize_t y_offset,ExceptionInfo *exception)
520 #define CompositeImageTag "Composite/Image"
558 assert(image != (Image *) NULL);
559 assert(image->signature == MagickCoreSignature);
560 if (image->debug != MagickFalse)
561 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
562 assert(composite!= (Image *) NULL);
563 assert(composite->signature == MagickCoreSignature);
564 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
566 source_image=CloneImage(composite,0,0,MagickTrue,exception);
567 if (source_image == (const Image *) NULL)
569 if (IsGrayColorspace(image->colorspace) != MagickFalse)
570 (void) SetImageColorspace(image,sRGBColorspace,exception);
571 (void) SetImageColorspace(source_image,image->colorspace,exception);
572 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
574 status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
576 source_image=DestroyImage(source_image);
580 canvas_image=(Image *) NULL;
583 value=GetImageArtifact(image,"compose:clamp");
584 if (value != (const char *) NULL)
585 clamp=IsStringTrue(value);
586 SetGeometryInfo(&geometry_info);
588 percent_chroma=100.0;
593 case CopyCompositeOp:
595 if ((x_offset < 0) || (y_offset < 0))
597 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
599 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
602 source_view=AcquireVirtualCacheView(source_image,exception);
603 image_view=AcquireAuthenticCacheView(image,exception);
604 #if defined(MAGICKCORE_OPENMP_SUPPORT)
605 #pragma omp parallel for schedule(static,4) shared(status) \
606 magick_threads(source_image,image,source_image->rows,1)
608 for (y=0; y < (ssize_t) source_image->rows; y++)
613 register const Quantum
622 if (status == MagickFalse)
624 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
626 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
627 source_image->columns,1,exception);
628 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
633 for (x=0; x < (ssize_t) source_image->columns; x++)
638 if (GetPixelReadMask(source_image,p) == 0)
640 p+=GetPixelChannels(source_image);
641 q+=GetPixelChannels(image);
644 for (i=0; i < (ssize_t) GetPixelChannels(source_image); i++)
646 PixelChannel channel=GetPixelChannelChannel(source_image,i);
647 PixelTrait source_traits=GetPixelChannelTraits(source_image,
649 PixelTrait traits=GetPixelChannelTraits(image,channel);
650 if ((traits == UndefinedPixelTrait) ||
651 (source_traits == UndefinedPixelTrait))
653 SetPixelChannel(image,channel,p[i],q);
655 p+=GetPixelChannels(source_image);
656 q+=GetPixelChannels(image);
658 sync=SyncCacheViewAuthenticPixels(image_view,exception);
659 if (sync == MagickFalse)
661 if (image->progress_monitor != (MagickProgressMonitor) NULL)
666 #if defined(MAGICKCORE_OPENMP_SUPPORT)
667 #pragma omp critical (MagickCore_CompositeImage)
669 proceed=SetImageProgress(image,CompositeImageTag,
670 (MagickOffsetType) y,image->rows);
671 if (proceed == MagickFalse)
675 source_view=DestroyCacheView(source_view);
676 image_view=DestroyCacheView(image_view);
677 source_image=DestroyImage(source_image);
680 case IntensityCompositeOp:
682 if ((x_offset < 0) || (y_offset < 0))
684 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
686 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
689 source_view=AcquireVirtualCacheView(source_image,exception);
690 image_view=AcquireAuthenticCacheView(image,exception);
691 #if defined(MAGICKCORE_OPENMP_SUPPORT)
692 #pragma omp parallel for schedule(static,4) shared(status) \
693 magick_threads(source_image,image,source_image->rows,1)
695 for (y=0; y < (ssize_t) source_image->rows; y++)
700 register const Quantum
709 if (status == MagickFalse)
711 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
713 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
714 source_image->columns,1,exception);
715 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
720 for (x=0; x < (ssize_t) source_image->columns; x++)
722 if (GetPixelReadMask(source_image,p) == 0)
724 p+=GetPixelChannels(source_image);
725 q+=GetPixelChannels(image);
728 SetPixelAlpha(image,clamp != MagickFalse ?
729 ClampPixel(GetPixelIntensity(source_image,p)) :
730 ClampToQuantum(GetPixelIntensity(source_image,p)),q);
731 p+=GetPixelChannels(source_image);
732 q+=GetPixelChannels(image);
734 sync=SyncCacheViewAuthenticPixels(image_view,exception);
735 if (sync == MagickFalse)
737 if (image->progress_monitor != (MagickProgressMonitor) NULL)
742 #if defined(MAGICKCORE_OPENMP_SUPPORT)
743 #pragma omp critical (MagickCore_CompositeImage)
745 proceed=SetImageProgress(image,CompositeImageTag,
746 (MagickOffsetType) y,image->rows);
747 if (proceed == MagickFalse)
751 source_view=DestroyCacheView(source_view);
752 image_view=DestroyCacheView(image_view);
753 source_image=DestroyImage(source_image);
756 case CopyAlphaCompositeOp:
757 case ChangeMaskCompositeOp:
760 Modify canvas outside the overlaid region and require an alpha
761 channel to exist, to add transparency.
763 if (image->alpha_trait == UndefinedPixelTrait)
764 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
767 case BlurCompositeOp:
788 Blur Image by resampling.
790 Blur Image dictated by an overlay gradient map: X = red_channel;
791 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
793 canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
795 if (canvas_image == (Image *) NULL)
797 source_image=DestroyImage(source_image);
801 Gather the maximum blur sigma values from user.
804 value=GetImageArtifact(image,"compose:args");
805 if (value != (const char *) NULL)
806 flags=ParseGeometry(value,&geometry_info);
807 if ((flags & WidthValue) == 0)
809 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
810 "InvalidSetting","'%s' '%s'","compose:args",value);
811 source_image=DestroyImage(source_image);
812 canvas_image=DestroyImage(canvas_image);
816 Users input sigma now needs to be converted to the EWA ellipse size.
817 The filter defaults to a sigma of 0.5 so to make this match the
818 users input the ellipse size needs to be doubled.
820 width=height=geometry_info.rho*2.0;
821 if ((flags & HeightValue) != 0 )
822 height=geometry_info.sigma*2.0;
824 Default the unrotated ellipse width and height axis vectors.
830 /* rotate vectors if a rotation angle is given */
831 if ((flags & XValue) != 0 )
836 angle=DegreesToRadians(geometry_info.xi);
837 blur.x1=width*cos(angle);
838 blur.x2=width*sin(angle);
839 blur.y1=(-height*sin(angle));
840 blur.y2=height*cos(angle);
842 /* Otherwise lets set a angle range and calculate in the loop */
845 if ((flags & YValue) != 0 )
847 angle_start=DegreesToRadians(geometry_info.xi);
848 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
851 Set up a gaussian cylindrical filter for EWA Bluring.
853 As the minimum ellipse radius of support*1.0 the EWA algorithm
854 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
855 This means that even 'No Blur' will be still a little blurry!
857 The solution (as well as the problem of preventing any user
858 expert filter settings, is to set our own user settings, then
859 restore them afterwards.
861 resample_filter=AcquireResampleFilter(image,exception);
862 SetResampleFilter(resample_filter,GaussianFilter);
864 /* do the variable blurring of each pixel in image */
865 GetPixelInfo(image,&pixel);
866 source_view=AcquireVirtualCacheView(source_image,exception);
867 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
868 for (y=0; y < (ssize_t) source_image->rows; y++)
873 register const Quantum
882 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
884 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
886 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
888 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
890 for (x=0; x < (ssize_t) source_image->columns; x++)
892 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
894 p+=GetPixelChannels(source_image);
897 if (fabs((double) angle_range) > MagickEpsilon)
902 angle=angle_start+angle_range*QuantumScale*
903 GetPixelBlue(source_image,p);
904 blur.x1=width*cos(angle);
905 blur.x2=width*sin(angle);
906 blur.y1=(-height*sin(angle));
907 blur.y2=height*cos(angle);
910 if ( x == 10 && y == 60 ) {
911 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
912 blur.x2,blur.y1, blur.y2);
913 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
914 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
916 ScaleResampleFilter(resample_filter,
917 blur.x1*QuantumScale*GetPixelRed(source_image,p),
918 blur.y1*QuantumScale*GetPixelGreen(source_image,p),
919 blur.x2*QuantumScale*GetPixelRed(source_image,p),
920 blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
921 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
922 (double) y_offset+y,&pixel,exception);
923 SetPixelViaPixelInfo(canvas_image,&pixel,q);
924 p+=GetPixelChannels(source_image);
925 q+=GetPixelChannels(canvas_image);
927 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
928 if (sync == MagickFalse)
931 resample_filter=DestroyResampleFilter(resample_filter);
932 source_view=DestroyCacheView(source_view);
933 canvas_view=DestroyCacheView(canvas_view);
934 source_image=DestroyImage(source_image);
935 source_image=canvas_image;
938 case DisplaceCompositeOp:
939 case DistortCompositeOp:
956 Displace/Distort based on overlay gradient map:
957 X = red_channel; Y = green_channel;
958 compose:args = x_scale[,y_scale[,center.x,center.y]]
960 canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
962 if (canvas_image == (Image *) NULL)
964 source_image=DestroyImage(source_image);
967 SetGeometryInfo(&geometry_info);
969 value=GetImageArtifact(image,"compose:args");
970 if (value != (char *) NULL)
971 flags=ParseGeometry(value,&geometry_info);
972 if ((flags & (WidthValue | HeightValue)) == 0 )
974 if ((flags & AspectValue) == 0)
976 horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
977 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
981 horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
982 vertical_scale=(MagickRealType) (image->rows-1)/2.0;
987 horizontal_scale=geometry_info.rho;
988 vertical_scale=geometry_info.sigma;
989 if ((flags & PercentValue) != 0)
991 if ((flags & AspectValue) == 0)
993 horizontal_scale*=(source_image->columns-1)/200.0;
994 vertical_scale*=(source_image->rows-1)/200.0;
998 horizontal_scale*=(image->columns-1)/200.0;
999 vertical_scale*=(image->rows-1)/200.0;
1002 if ((flags & HeightValue) == 0)
1003 vertical_scale=horizontal_scale;
1006 Determine fixed center point for absolute distortion map
1008 Displace offset relative to a fixed absolute point
1009 Select that point according to +X+Y user inputs.
1010 default = center of overlay image
1011 arg flag '!' = locations/percentage relative to background image
1013 center.x=(MagickRealType) x_offset;
1014 center.y=(MagickRealType) y_offset;
1015 if (compose == DistortCompositeOp)
1017 if ((flags & XValue) == 0)
1018 if ((flags & AspectValue) != 0)
1019 center.x=(MagickRealType) ((image->columns-1)/2.0);
1021 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1024 if ((flags & AspectValue) != 0)
1025 center.x=geometry_info.xi;
1027 center.x=(MagickRealType) (x_offset+geometry_info.xi);
1028 if ((flags & YValue) == 0)
1029 if ((flags & AspectValue) != 0)
1030 center.y=(MagickRealType) ((image->rows-1)/2.0);
1032 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1034 if ((flags & AspectValue) != 0)
1035 center.y=geometry_info.psi;
1037 center.y=(MagickRealType) (y_offset+geometry_info.psi);
1040 Shift the pixel offset point as defined by the provided,
1041 displacement/distortion map. -- Like a lens...
1043 GetPixelInfo(image,&pixel);
1044 image_view=AcquireVirtualCacheView(image,exception);
1045 source_view=AcquireVirtualCacheView(source_image,exception);
1046 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1047 for (y=0; y < (ssize_t) source_image->rows; y++)
1052 register const Quantum
1061 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1063 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1065 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1067 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1069 for (x=0; x < (ssize_t) source_image->columns; x++)
1071 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1073 p+=GetPixelChannels(source_image);
1077 Displace the offset.
1079 offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1080 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1081 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1083 offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1084 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1085 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1087 (void) InterpolatePixelInfo(image,image_view,
1088 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1091 Mask with the 'invalid pixel mask' in alpha channel.
1093 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
1094 (QuantumScale*GetPixelAlpha(source_image,p));
1095 SetPixelViaPixelInfo(canvas_image,&pixel,q);
1096 p+=GetPixelChannels(source_image);
1097 q+=GetPixelChannels(canvas_image);
1099 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1100 if (sync == MagickFalse)
1103 canvas_view=DestroyCacheView(canvas_view);
1104 source_view=DestroyCacheView(source_view);
1105 image_view=DestroyCacheView(image_view);
1106 source_image=DestroyImage(source_image);
1107 source_image=canvas_image;
1110 case DissolveCompositeOp:
1113 Geometry arguments to dissolve factors.
1115 value=GetImageArtifact(image,"compose:args");
1116 if (value != (char *) NULL)
1118 flags=ParseGeometry(value,&geometry_info);
1119 source_dissolve=geometry_info.rho/100.0;
1120 canvas_dissolve=1.0;
1121 if ((source_dissolve-MagickEpsilon) < 0.0)
1122 source_dissolve=0.0;
1123 if ((source_dissolve+MagickEpsilon) > 1.0)
1125 canvas_dissolve=2.0-source_dissolve;
1126 source_dissolve=1.0;
1128 if ((flags & SigmaValue) != 0)
1129 canvas_dissolve=geometry_info.sigma/100.0;
1130 if ((canvas_dissolve-MagickEpsilon) < 0.0)
1131 canvas_dissolve=0.0;
1135 case BlendCompositeOp:
1137 value=GetImageArtifact(image,"compose:args");
1138 if (value != (char *) NULL)
1140 flags=ParseGeometry(value,&geometry_info);
1141 source_dissolve=geometry_info.rho/100.0;
1142 canvas_dissolve=1.0-source_dissolve;
1143 if ((flags & SigmaValue) != 0)
1144 canvas_dissolve=geometry_info.sigma/100.0;
1148 case MathematicsCompositeOp:
1151 Just collect the values from "compose:args", setting.
1152 Unused values are set to zero automagically.
1154 Arguments are normally a comma separated list, so this probably should
1155 be changed to some 'general comma list' parser, (with a minimum
1158 SetGeometryInfo(&geometry_info);
1159 value=GetImageArtifact(image,"compose:args");
1160 if (value != (char *) NULL)
1161 (void) ParseGeometry(value,&geometry_info);
1164 case ModulateCompositeOp:
1167 Determine the luma and chroma scale.
1169 value=GetImageArtifact(image,"compose:args");
1170 if (value != (char *) NULL)
1172 flags=ParseGeometry(value,&geometry_info);
1173 percent_luma=geometry_info.rho;
1174 if ((flags & SigmaValue) != 0)
1175 percent_chroma=geometry_info.sigma;
1179 case ThresholdCompositeOp:
1182 Determine the amount and threshold.
1184 value=GetImageArtifact(image,"compose:args");
1185 if (value != (char *) NULL)
1187 flags=ParseGeometry(value,&geometry_info);
1188 amount=geometry_info.rho;
1189 threshold=geometry_info.sigma;
1190 if ((flags & SigmaValue) == 0)
1193 threshold*=QuantumRange;
1204 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1205 source_view=AcquireVirtualCacheView(source_image,exception);
1206 image_view=AcquireAuthenticCacheView(image,exception);
1207 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1208 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1209 magick_threads(source_image,image,image->rows,1)
1211 for (y=0; y < (ssize_t) image->rows; y++)
1228 register const Quantum
1237 if (status == MagickFalse)
1239 if (clip_to_self != MagickFalse)
1243 if ((y-y_offset) >= (ssize_t) source_image->rows)
1247 If pixels is NULL, y is outside overlay region.
1249 pixels=(Quantum *) NULL;
1251 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1253 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1254 source_image->columns,1,exception);
1255 if (p == (const Quantum *) NULL)
1262 p-=x_offset*GetPixelChannels(source_image);
1264 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1265 if (q == (Quantum *) NULL)
1273 GetPixelInfo(image,&canvas_pixel);
1274 GetPixelInfo(source_image,&source_pixel);
1275 for (x=0; x < (ssize_t) image->columns; x++)
1295 if (clip_to_self != MagickFalse)
1299 q+=GetPixelChannels(image);
1302 if ((x-x_offset) >= (ssize_t) source_image->columns)
1305 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1306 ((x-x_offset) >= (ssize_t) source_image->columns))
1309 source[MaxPixelChannels];
1316 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
1318 if (GetPixelReadMask(image,q) == 0)
1320 q+=GetPixelChannels(image);
1323 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1328 PixelChannel channel=GetPixelChannelChannel(image,i);
1329 PixelTrait traits=GetPixelChannelTraits(image,channel);
1330 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1332 if ((traits == UndefinedPixelTrait) ||
1333 (source_traits == UndefinedPixelTrait))
1337 case AlphaCompositeOp:
1338 case ChangeMaskCompositeOp:
1339 case CopyAlphaCompositeOp:
1340 case DstAtopCompositeOp:
1341 case DstInCompositeOp:
1343 case OutCompositeOp:
1344 case SrcInCompositeOp:
1345 case SrcOutCompositeOp:
1347 if (channel == AlphaPixelChannel)
1348 pixel=(MagickRealType) TransparentAlpha;
1350 pixel=(MagickRealType) q[i];
1353 case ClearCompositeOp:
1354 case CopyCompositeOp:
1355 case ReplaceCompositeOp:
1356 case SrcCompositeOp:
1358 if (channel == AlphaPixelChannel)
1359 pixel=(MagickRealType) TransparentAlpha;
1364 case BlendCompositeOp:
1365 case DissolveCompositeOp:
1367 if (channel == AlphaPixelChannel)
1368 pixel=canvas_dissolve*GetPixelAlpha(source_image,source);
1370 pixel=(MagickRealType) source[channel];
1375 pixel=(MagickRealType) source[channel];
1379 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1380 ClampToQuantum(pixel);
1382 q+=GetPixelChannels(image);
1386 Authentic composite:
1387 Sa: normalized source alpha.
1388 Da: normalized canvas alpha.
1390 Sa=QuantumScale*GetPixelAlpha(source_image,p);
1391 Da=QuantumScale*GetPixelAlpha(image,q);
1394 case BumpmapCompositeOp:
1396 alpha=GetPixelIntensity(source_image,p)*Sa;
1399 case ColorBurnCompositeOp:
1400 case ColorDodgeCompositeOp:
1401 case DarkenCompositeOp:
1402 case DifferenceCompositeOp:
1403 case DivideDstCompositeOp:
1404 case DivideSrcCompositeOp:
1405 case ExclusionCompositeOp:
1406 case HardLightCompositeOp:
1407 case HardMixCompositeOp:
1408 case LinearBurnCompositeOp:
1409 case LinearDodgeCompositeOp:
1410 case LinearLightCompositeOp:
1411 case LightenCompositeOp:
1412 case MathematicsCompositeOp:
1413 case MinusDstCompositeOp:
1414 case MinusSrcCompositeOp:
1415 case ModulusAddCompositeOp:
1416 case ModulusSubtractCompositeOp:
1417 case MultiplyCompositeOp:
1418 case OverlayCompositeOp:
1419 case PegtopLightCompositeOp:
1420 case PinLightCompositeOp:
1421 case ScreenCompositeOp:
1422 case SoftLightCompositeOp:
1423 case VividLightCompositeOp:
1425 alpha=RoundToUnity(Sa+Da-Sa*Da);
1428 case DstAtopCompositeOp:
1429 case DstInCompositeOp:
1431 case SrcInCompositeOp:
1436 case DissolveCompositeOp:
1438 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
1442 case DstOverCompositeOp:
1443 case OverCompositeOp:
1444 case SrcOverCompositeOp:
1449 case DstOutCompositeOp:
1454 case OutCompositeOp:
1455 case SrcOutCompositeOp:
1460 case BlendCompositeOp:
1461 case PlusCompositeOp:
1463 alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
1466 case XorCompositeOp:
1468 alpha=Sa+Da-2.0*Sa*Da;
1477 if (GetPixelReadMask(image,q) == 0)
1479 p+=GetPixelChannels(source_image);
1480 q+=GetPixelChannels(image);
1485 case ColorizeCompositeOp:
1486 case HueCompositeOp:
1487 case LuminizeCompositeOp:
1488 case ModulateCompositeOp:
1489 case SaturateCompositeOp:
1491 GetPixelInfoPixel(source_image,p,&source_pixel);
1492 GetPixelInfoPixel(image,q,&canvas_pixel);
1498 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1504 PixelChannel channel=GetPixelChannelChannel(image,i);
1505 PixelTrait traits=GetPixelChannelTraits(image,channel);
1506 PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
1507 if (traits == UndefinedPixelTrait)
1509 if ((source_traits == UndefinedPixelTrait) &&
1510 (((compose != CopyAlphaCompositeOp) &&
1511 (compose != ChangeMaskCompositeOp)) ||
1512 (channel != AlphaPixelChannel)))
1518 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1519 Dc=(MagickRealType) q[i];
1520 if ((traits & CopyPixelTrait) != 0)
1528 if (channel == AlphaPixelChannel)
1535 case AlphaCompositeOp:
1537 pixel=QuantumRange*Sa;
1540 case AtopCompositeOp:
1541 case CopyBlackCompositeOp:
1542 case CopyBlueCompositeOp:
1543 case CopyCyanCompositeOp:
1544 case CopyGreenCompositeOp:
1545 case CopyMagentaCompositeOp:
1546 case CopyRedCompositeOp:
1547 case CopyYellowCompositeOp:
1548 case SrcAtopCompositeOp:
1549 case DstCompositeOp:
1552 pixel=QuantumRange*Da;
1555 case ChangeMaskCompositeOp:
1562 pixel=(MagickRealType) TransparentAlpha;
1565 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1566 if (equivalent != MagickFalse)
1567 pixel=(MagickRealType) TransparentAlpha;
1569 pixel=(MagickRealType) OpaqueAlpha;
1572 case ClearCompositeOp:
1574 pixel=(MagickRealType) TransparentAlpha;
1577 case ColorizeCompositeOp:
1578 case HueCompositeOp:
1579 case LuminizeCompositeOp:
1580 case SaturateCompositeOp:
1582 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1584 pixel=QuantumRange*Da;
1587 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1589 pixel=QuantumRange*Sa;
1594 pixel=QuantumRange*Da;
1597 pixel=QuantumRange*Sa;
1600 case CopyAlphaCompositeOp:
1602 if (source_image->alpha_trait == UndefinedPixelTrait)
1603 pixel=GetPixelIntensity(source_image,p);
1605 pixel=QuantumRange*Sa;
1608 case CopyCompositeOp:
1609 case DisplaceCompositeOp:
1610 case DistortCompositeOp:
1611 case DstAtopCompositeOp:
1612 case ReplaceCompositeOp:
1613 case SrcCompositeOp:
1615 pixel=QuantumRange*Sa;
1618 case DarkenIntensityCompositeOp:
1620 pixel=Sa*GetPixelIntensity(source_image,p) <
1621 Da*GetPixelIntensity(image,q) ? Sa : Da;
1624 case LightenIntensityCompositeOp:
1626 pixel=Sa*GetPixelIntensity(source_image,p) >
1627 Da*GetPixelIntensity(image,q) ? Sa : Da;
1630 case ModulateCompositeOp:
1632 pixel=QuantumRange*Da;
1637 pixel=QuantumRange*alpha;
1641 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1642 ClampToQuantum(pixel);
1646 Porter-Duff compositions:
1647 Sca: source normalized color multiplied by alpha.
1648 Dca: normalized canvas color multiplied by alpha.
1650 Sca=QuantumScale*Sa*Sc;
1651 Dca=QuantumScale*Da*Dc;
1654 case DarkenCompositeOp:
1655 case LightenCompositeOp:
1656 case ModulusSubtractCompositeOp:
1658 gamma=PerceptibleReciprocal(1.0-alpha);
1663 gamma=PerceptibleReciprocal(alpha);
1670 case AlphaCompositeOp:
1672 pixel=QuantumRange*Sa;
1675 case AtopCompositeOp:
1676 case SrcAtopCompositeOp:
1678 pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1681 case BlendCompositeOp:
1683 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1686 case BlurCompositeOp:
1687 case CopyCompositeOp:
1688 case ReplaceCompositeOp:
1689 case SrcCompositeOp:
1691 pixel=QuantumRange*Sca;
1694 case DisplaceCompositeOp:
1695 case DistortCompositeOp:
1700 case BumpmapCompositeOp:
1702 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1707 pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1710 case ChangeMaskCompositeOp:
1715 case ClearCompositeOp:
1720 case ColorBurnCompositeOp:
1722 if ((Sca == 0.0) && (Dca == Da))
1724 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1729 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1732 pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-Dca/Da)*Sa/
1733 Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1736 case ColorDodgeCompositeOp:
1738 if ((Sca*Da+Dca*Sa) >= Sa*Da)
1739 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1741 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
1745 case ColorizeCompositeOp:
1747 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1752 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1757 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1759 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1760 &hue,&chroma,&sans);
1761 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1764 case RedPixelChannel: pixel=red; break;
1765 case GreenPixelChannel: pixel=green; break;
1766 case BluePixelChannel: pixel=blue; break;
1767 default: pixel=Dc; break;
1771 case CopyAlphaCompositeOp:
1776 case CopyBlackCompositeOp:
1778 if (channel == BlackPixelChannel)
1779 pixel=(MagickRealType) (QuantumRange-
1780 GetPixelBlack(source_image,p));
1783 case CopyBlueCompositeOp:
1784 case CopyYellowCompositeOp:
1786 if (channel == BluePixelChannel)
1787 pixel=(MagickRealType) GetPixelBlue(source_image,p);
1790 case CopyGreenCompositeOp:
1791 case CopyMagentaCompositeOp:
1793 if (channel == GreenPixelChannel)
1794 pixel=(MagickRealType) GetPixelGreen(source_image,p);
1797 case CopyRedCompositeOp:
1798 case CopyCyanCompositeOp:
1800 if (channel == RedPixelChannel)
1801 pixel=(MagickRealType) GetPixelRed(source_image,p);
1804 case DarkenCompositeOp:
1807 Darken is equivalent to a 'Minimum' method
1808 OR a greyscale version of a binary 'Or'
1809 OR the 'Intersection' of pixel sets.
1811 if ((Sca*Da) < (Dca*Sa))
1813 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1816 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1819 case DarkenIntensityCompositeOp:
1821 pixel=Sa*GetPixelIntensity(source_image,p) <
1822 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1825 case DifferenceCompositeOp:
1827 pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1830 case DissolveCompositeOp:
1832 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1833 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1836 case DivideDstCompositeOp:
1838 if ((fabs((double) Sca) < MagickEpsilon) &&
1839 (fabs((double) Dca) < MagickEpsilon))
1841 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1844 if (fabs((double) Dca) < MagickEpsilon)
1846 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1849 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1852 case DivideSrcCompositeOp:
1854 if ((fabs((double) Dca) < MagickEpsilon) &&
1855 (fabs((double) Sca) < MagickEpsilon))
1857 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1860 if (fabs((double) Sca) < MagickEpsilon)
1862 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1865 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1868 case DstAtopCompositeOp:
1870 pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1873 case DstCompositeOp:
1876 pixel=QuantumRange*Dca;
1879 case DstInCompositeOp:
1881 pixel=QuantumRange*(Dca*Sa);
1884 case DstOutCompositeOp:
1886 pixel=QuantumRange*(Dca*(1.0-Sa));
1889 case DstOverCompositeOp:
1891 pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1894 case ExclusionCompositeOp:
1896 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1900 case HardLightCompositeOp:
1904 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1908 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1912 case HardMixCompositeOp:
1914 pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1917 case HueCompositeOp:
1919 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1924 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1929 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1930 &hue,&chroma,&luma);
1931 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1933 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1936 case RedPixelChannel: pixel=red; break;
1937 case GreenPixelChannel: pixel=green; break;
1938 case BluePixelChannel: pixel=blue; break;
1939 default: pixel=Dc; break;
1944 case SrcInCompositeOp:
1946 pixel=QuantumRange*(Sca*Da);
1949 case LinearBurnCompositeOp:
1952 LinearBurn: as defined by Abode Photoshop, according to
1953 http://www.simplefilter.de/en/basics/mixmods.html is:
1955 f(Sc,Dc) = Sc + Dc - 1
1957 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1960 case LinearDodgeCompositeOp:
1962 pixel=gamma*(Sa*Sc+Da*Dc);
1965 case LinearLightCompositeOp:
1968 LinearLight: as defined by Abode Photoshop, according to
1969 http://www.simplefilter.de/en/basics/mixmods.html is:
1971 f(Sc,Dc) = Dc + 2*Sc - 1
1973 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1976 case LightenCompositeOp:
1978 if ((Sca*Da) > (Dca*Sa))
1980 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1983 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1986 case LightenIntensityCompositeOp:
1989 Lighten is equivalent to a 'Maximum' method
1990 OR a greyscale version of a binary 'And'
1991 OR the 'Union' of pixel sets.
1993 pixel=Sa*GetPixelIntensity(source_image,p) >
1994 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1997 case LuminizeCompositeOp:
1999 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2004 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2009 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2010 &hue,&chroma,&luma);
2011 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2013 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2016 case RedPixelChannel: pixel=red; break;
2017 case GreenPixelChannel: pixel=green; break;
2018 case BluePixelChannel: pixel=blue; break;
2019 default: pixel=Dc; break;
2023 case MathematicsCompositeOp:
2026 'Mathematics' a free form user control mathematical composition
2029 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2031 Where the arguments A,B,C,D are (currently) passed to composite
2032 as a command separated 'geometry' string in "compose:args" image
2035 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2037 Applying the SVG transparency formula (see above), we get...
2039 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2041 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2044 pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
2045 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2046 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2049 case MinusDstCompositeOp:
2051 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2054 case MinusSrcCompositeOp:
2057 Minus source from canvas.
2061 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2064 case ModulateCompositeOp:
2069 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2074 offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2080 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2081 &hue,&chroma,&luma);
2082 luma+=(0.01*percent_luma*offset)/midpoint;
2083 chroma*=0.01*percent_chroma;
2084 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2087 case RedPixelChannel: pixel=red; break;
2088 case GreenPixelChannel: pixel=green; break;
2089 case BluePixelChannel: pixel=blue; break;
2090 default: pixel=Dc; break;
2094 case ModulusAddCompositeOp:
2097 while (pixel > QuantumRange)
2098 pixel-=QuantumRange;
2100 pixel+=QuantumRange;
2101 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2104 case ModulusSubtractCompositeOp:
2107 while (pixel > QuantumRange)
2108 pixel-=QuantumRange;
2110 pixel+=QuantumRange;
2111 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2114 case MultiplyCompositeOp:
2116 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2119 case OutCompositeOp:
2120 case SrcOutCompositeOp:
2122 pixel=QuantumRange*(Sca*(1.0-Da));
2125 case OverCompositeOp:
2126 case SrcOverCompositeOp:
2128 pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
2131 case OverlayCompositeOp:
2135 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-
2139 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2143 case PegtopLightCompositeOp:
2146 PegTop: A Soft-Light alternative: A continuous version of the
2147 Softlight function, producing very similar results.
2149 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2151 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2153 if (fabs((double) Da) < MagickEpsilon)
2155 pixel=QuantumRange*gamma*(Sca);
2158 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2162 case PinLightCompositeOp:
2165 PinLight: A Photoshop 7 composition method
2166 http://www.simplefilter.de/en/basics/mixmods.html
2168 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2170 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2172 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2175 if ((Dca*Sa) > (2.0*Sca*Da))
2177 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2180 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2183 case PlusCompositeOp:
2185 pixel=QuantumRange*(Sca+Dca);
2188 case SaturateCompositeOp:
2190 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2195 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2200 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2201 &hue,&chroma,&luma);
2202 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2203 &sans,&chroma,&sans);
2204 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2207 case RedPixelChannel: pixel=red; break;
2208 case GreenPixelChannel: pixel=green; break;
2209 case BluePixelChannel: pixel=blue; break;
2210 default: pixel=Dc; break;
2214 case ScreenCompositeOp:
2217 Screen: a negated multiply:
2219 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2221 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2224 case SoftLightCompositeOp:
2228 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2229 Sca*(1.0-Da)+Dca*(1.0-Sa));
2232 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2234 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2235 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2239 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2240 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2243 case ThresholdCompositeOp:
2249 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2254 pixel=gamma*(Dc+delta*amount);
2257 case VividLightCompositeOp:
2260 VividLight: A Photoshop 7 composition method. See
2261 http://www.simplefilter.de/en/basics/mixmods.html.
2263 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2265 if ((fabs((double) Sa) < MagickEpsilon) ||
2266 (fabs((double) (Sca-Sa)) < MagickEpsilon))
2268 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2271 if ((2.0*Sca) <= Sa)
2273 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2274 (1.0-Da)+Dca*(1.0-Sa));
2277 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*
2281 case XorCompositeOp:
2283 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2292 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2294 p+=GetPixelChannels(source_image);
2295 channels=GetPixelChannels(source_image);
2296 if (p >= (pixels+channels*source_image->columns))
2298 q+=GetPixelChannels(image);
2300 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2302 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2307 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2308 #pragma omp critical (MagickCore_CompositeImage)
2310 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2312 if (proceed == MagickFalse)
2316 source_view=DestroyCacheView(source_view);
2317 image_view=DestroyCacheView(image_view);
2318 if (canvas_image != (Image * ) NULL)
2319 canvas_image=DestroyImage(canvas_image);
2321 source_image=DestroyImage(source_image);
2326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2330 % T e x t u r e I m a g e %
2334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2336 % TextureImage() repeatedly tiles the texture image across and down the image
2339 % The format of the TextureImage method is:
2341 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2342 % ExceptionInfo *exception)
2344 % A description of each parameter follows:
2346 % o image: the image.
2348 % o texture_image: This image is the texture to layer on the background.
2351 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2352 ExceptionInfo *exception)
2354 #define TextureImageTag "Texture/Image"
2369 assert(image != (Image *) NULL);
2370 if (image->debug != MagickFalse)
2371 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2372 assert(image->signature == MagickCoreSignature);
2373 if (texture == (const Image *) NULL)
2374 return(MagickFalse);
2375 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2376 return(MagickFalse);
2377 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2378 if (texture_image == (const Image *) NULL)
2379 return(MagickFalse);
2380 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2381 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2384 if ((image->compose != CopyCompositeOp) &&
2385 ((image->compose != OverCompositeOp) ||
2386 (image->alpha_trait != UndefinedPixelTrait) ||
2387 (texture_image->alpha_trait != UndefinedPixelTrait)))
2390 Tile texture onto the image background.
2392 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2397 if (status == MagickFalse)
2399 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2404 thread_status=CompositeImage(image,texture_image,image->compose,
2405 MagickTrue,x+texture_image->tile_offset.x,y+
2406 texture_image->tile_offset.y,exception);
2407 if (thread_status == MagickFalse)
2409 status=thread_status;
2413 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2418 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2420 if (proceed == MagickFalse)
2424 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2425 image->rows,image->rows);
2426 texture_image=DestroyImage(texture_image);
2430 Tile texture onto the image background (optimized).
2433 texture_view=AcquireVirtualCacheView(texture_image,exception);
2434 image_view=AcquireAuthenticCacheView(image,exception);
2435 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2436 #pragma omp parallel for schedule(static,4) shared(status) \
2437 magick_threads(texture_image,image,1,1)
2439 for (y=0; y < (ssize_t) image->rows; y++)
2444 register const Quantum
2457 if (status == MagickFalse)
2459 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2460 (y+texture_image->tile_offset.y) % texture_image->rows,
2461 texture_image->columns,1,exception);
2462 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2463 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2468 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2474 width=texture_image->columns;
2475 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2476 width=image->columns-x;
2477 for (j=0; j < (ssize_t) width; j++)
2482 if (GetPixelReadMask(image,q) == 0)
2484 p+=GetPixelChannels(texture_image);
2485 q+=GetPixelChannels(image);
2488 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2490 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2491 PixelTrait traits=GetPixelChannelTraits(image,channel);
2492 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2494 if ((traits == UndefinedPixelTrait) ||
2495 (texture_traits == UndefinedPixelTrait))
2497 SetPixelChannel(image,channel,p[i],q);
2499 p+=GetPixelChannels(texture_image);
2500 q+=GetPixelChannels(image);
2503 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2504 if (sync == MagickFalse)
2506 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2511 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2513 if (proceed == MagickFalse)
2517 texture_view=DestroyCacheView(texture_view);
2518 image_view=DestroyCacheView(image_view);
2519 texture_image=DestroyImage(texture_image);