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 MagickExport MagickBooleanType CompositeImage(Image *image,
288 const Image *composite,const CompositeOperator compose,
289 const MagickBooleanType clip_to_self,const ssize_t x_offset,
290 const ssize_t y_offset,ExceptionInfo *exception)
292 #define CompositeImageTag "Composite/Image"
330 assert(image != (Image *) NULL);
331 assert(image->signature == MagickCoreSignature);
332 if (image->debug != MagickFalse)
333 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
334 assert(composite != (Image *) NULL);
335 assert(composite->signature == MagickCoreSignature);
336 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
338 source_image=CloneImage(composite,0,0,MagickTrue,exception);
339 if (source_image == (const Image *) NULL)
341 if (IsGrayColorspace(image->colorspace) != MagickFalse)
342 (void) SetImageColorspace(image,sRGBColorspace,exception);
343 (void) SetImageColorspace(source_image,image->colorspace,exception);
345 canvas_image=(Image *) NULL;
348 value=GetImageArtifact(image,"compose:clamp");
349 if (value != (const char *) NULL)
350 clamp=IsStringTrue(value);
351 SetGeometryInfo(&geometry_info);
353 percent_chroma=100.0;
358 case CopyCompositeOp:
360 if ((x_offset < 0) || (y_offset < 0))
362 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
364 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
367 source_view=AcquireVirtualCacheView(source_image,exception);
368 image_view=AcquireAuthenticCacheView(image,exception);
369 #if defined(MAGICKCORE_OPENMP_SUPPORT)
370 #pragma omp parallel for schedule(static,4) shared(status) \
371 magick_threads(source_image,image,source_image->rows,1)
373 for (y=0; y < (ssize_t) source_image->rows; y++)
378 register const Quantum
387 if (status == MagickFalse)
389 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
391 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
392 source_image->columns,1,exception);
393 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
398 for (x=0; x < (ssize_t) source_image->columns; x++)
403 if (GetPixelReadMask(source_image,p) == 0)
405 p+=GetPixelChannels(source_image);
406 q+=GetPixelChannels(image);
409 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
411 PixelChannel channel=GetPixelChannelChannel(image,i);
412 PixelTrait traits=GetPixelChannelTraits(image,channel);
413 PixelTrait source_traits=GetPixelChannelTraits(source_image,
415 if (traits == UndefinedPixelTrait)
417 if (source_traits != UndefinedPixelTrait)
418 SetPixelChannel(image,channel,p[i],q);
419 else if (channel == AlphaPixelChannel)
420 SetPixelChannel(image,channel,OpaqueAlpha,q);
422 p+=GetPixelChannels(source_image);
423 q+=GetPixelChannels(image);
425 sync=SyncCacheViewAuthenticPixels(image_view,exception);
426 if (sync == MagickFalse)
428 if (image->progress_monitor != (MagickProgressMonitor) NULL)
433 #if defined(MAGICKCORE_OPENMP_SUPPORT)
434 #pragma omp critical (MagickCore_CompositeImage)
436 proceed=SetImageProgress(image,CompositeImageTag,
437 (MagickOffsetType) y,image->rows);
438 if (proceed == MagickFalse)
442 source_view=DestroyCacheView(source_view);
443 image_view=DestroyCacheView(image_view);
444 source_image=DestroyImage(source_image);
447 case IntensityCompositeOp:
449 if ((x_offset < 0) || (y_offset < 0))
451 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
453 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
456 source_view=AcquireVirtualCacheView(source_image,exception);
457 image_view=AcquireAuthenticCacheView(image,exception);
458 #if defined(MAGICKCORE_OPENMP_SUPPORT)
459 #pragma omp parallel for schedule(static,4) shared(status) \
460 magick_threads(source_image,image,source_image->rows,1)
462 for (y=0; y < (ssize_t) source_image->rows; y++)
467 register const Quantum
476 if (status == MagickFalse)
478 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
480 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
481 source_image->columns,1,exception);
482 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
487 for (x=0; x < (ssize_t) source_image->columns; x++)
489 if (GetPixelReadMask(source_image,p) == 0)
491 p+=GetPixelChannels(source_image);
492 q+=GetPixelChannels(image);
495 SetPixelAlpha(image,clamp != MagickFalse ?
496 ClampPixel(GetPixelIntensity(source_image,p)) :
497 ClampToQuantum(GetPixelIntensity(source_image,p)),q);
498 p+=GetPixelChannels(source_image);
499 q+=GetPixelChannels(image);
501 sync=SyncCacheViewAuthenticPixels(image_view,exception);
502 if (sync == MagickFalse)
504 if (image->progress_monitor != (MagickProgressMonitor) NULL)
509 #if defined(MAGICKCORE_OPENMP_SUPPORT)
510 #pragma omp critical (MagickCore_CompositeImage)
512 proceed=SetImageProgress(image,CompositeImageTag,
513 (MagickOffsetType) y,image->rows);
514 if (proceed == MagickFalse)
518 source_view=DestroyCacheView(source_view);
519 image_view=DestroyCacheView(image_view);
520 source_image=DestroyImage(source_image);
523 case CopyAlphaCompositeOp:
524 case ChangeMaskCompositeOp:
527 Modify canvas outside the overlaid region and require an alpha
528 channel to exist, to add transparency.
530 if (image->alpha_trait == UndefinedPixelTrait)
531 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
534 case BlurCompositeOp:
555 Blur Image by resampling.
557 Blur Image dictated by an overlay gradient map: X = red_channel;
558 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
560 canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
562 if (canvas_image == (Image *) NULL)
564 source_image=DestroyImage(source_image);
568 Gather the maximum blur sigma values from user.
571 value=GetImageArtifact(image,"compose:args");
572 if (value != (const char *) NULL)
573 flags=ParseGeometry(value,&geometry_info);
574 if ((flags & WidthValue) == 0)
576 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
577 "InvalidSetting","'%s' '%s'","compose:args",value);
578 source_image=DestroyImage(source_image);
579 canvas_image=DestroyImage(canvas_image);
583 Users input sigma now needs to be converted to the EWA ellipse size.
584 The filter defaults to a sigma of 0.5 so to make this match the
585 users input the ellipse size needs to be doubled.
587 width=height=geometry_info.rho*2.0;
588 if ((flags & HeightValue) != 0 )
589 height=geometry_info.sigma*2.0;
591 Default the unrotated ellipse width and height axis vectors.
597 /* rotate vectors if a rotation angle is given */
598 if ((flags & XValue) != 0 )
603 angle=DegreesToRadians(geometry_info.xi);
604 blur.x1=width*cos(angle);
605 blur.x2=width*sin(angle);
606 blur.y1=(-height*sin(angle));
607 blur.y2=height*cos(angle);
609 /* Otherwise lets set a angle range and calculate in the loop */
612 if ((flags & YValue) != 0 )
614 angle_start=DegreesToRadians(geometry_info.xi);
615 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
618 Set up a gaussian cylindrical filter for EWA Bluring.
620 As the minimum ellipse radius of support*1.0 the EWA algorithm
621 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
622 This means that even 'No Blur' will be still a little blurry!
624 The solution (as well as the problem of preventing any user
625 expert filter settings, is to set our own user settings, then
626 restore them afterwards.
628 resample_filter=AcquireResampleFilter(image,exception);
629 SetResampleFilter(resample_filter,GaussianFilter);
631 /* do the variable blurring of each pixel in image */
632 GetPixelInfo(image,&pixel);
633 source_view=AcquireVirtualCacheView(source_image,exception);
634 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
635 for (y=0; y < (ssize_t) source_image->rows; y++)
640 register const Quantum
649 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
651 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
653 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
655 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
657 for (x=0; x < (ssize_t) source_image->columns; x++)
659 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
661 p+=GetPixelChannels(source_image);
664 if (fabs((double) angle_range) > MagickEpsilon)
669 angle=angle_start+angle_range*QuantumScale*
670 GetPixelBlue(source_image,p);
671 blur.x1=width*cos(angle);
672 blur.x2=width*sin(angle);
673 blur.y1=(-height*sin(angle));
674 blur.y2=height*cos(angle);
677 if ( x == 10 && y == 60 ) {
678 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
679 blur.x2,blur.y1, blur.y2);
680 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
681 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
683 ScaleResampleFilter(resample_filter,
684 blur.x1*QuantumScale*GetPixelRed(source_image,p),
685 blur.y1*QuantumScale*GetPixelGreen(source_image,p),
686 blur.x2*QuantumScale*GetPixelRed(source_image,p),
687 blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
688 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
689 (double) y_offset+y,&pixel,exception);
690 SetPixelViaPixelInfo(canvas_image,&pixel,q);
691 p+=GetPixelChannels(source_image);
692 q+=GetPixelChannels(canvas_image);
694 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
695 if (sync == MagickFalse)
698 resample_filter=DestroyResampleFilter(resample_filter);
699 source_view=DestroyCacheView(source_view);
700 canvas_view=DestroyCacheView(canvas_view);
701 source_image=DestroyImage(source_image);
702 source_image=canvas_image;
705 case DisplaceCompositeOp:
706 case DistortCompositeOp:
723 Displace/Distort based on overlay gradient map:
724 X = red_channel; Y = green_channel;
725 compose:args = x_scale[,y_scale[,center.x,center.y]]
727 canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
729 if (canvas_image == (Image *) NULL)
731 source_image=DestroyImage(source_image);
734 SetGeometryInfo(&geometry_info);
736 value=GetImageArtifact(image,"compose:args");
737 if (value != (char *) NULL)
738 flags=ParseGeometry(value,&geometry_info);
739 if ((flags & (WidthValue | HeightValue)) == 0 )
741 if ((flags & AspectValue) == 0)
743 horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
744 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
748 horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
749 vertical_scale=(MagickRealType) (image->rows-1)/2.0;
754 horizontal_scale=geometry_info.rho;
755 vertical_scale=geometry_info.sigma;
756 if ((flags & PercentValue) != 0)
758 if ((flags & AspectValue) == 0)
760 horizontal_scale*=(source_image->columns-1)/200.0;
761 vertical_scale*=(source_image->rows-1)/200.0;
765 horizontal_scale*=(image->columns-1)/200.0;
766 vertical_scale*=(image->rows-1)/200.0;
769 if ((flags & HeightValue) == 0)
770 vertical_scale=horizontal_scale;
773 Determine fixed center point for absolute distortion map
775 Displace offset relative to a fixed absolute point
776 Select that point according to +X+Y user inputs.
777 default = center of overlay image
778 arg flag '!' = locations/percentage relative to background image
780 center.x=(MagickRealType) x_offset;
781 center.y=(MagickRealType) y_offset;
782 if (compose == DistortCompositeOp)
784 if ((flags & XValue) == 0)
785 if ((flags & AspectValue) != 0)
786 center.x=(MagickRealType) ((image->columns-1)/2.0);
788 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
791 if ((flags & AspectValue) != 0)
792 center.x=geometry_info.xi;
794 center.x=(MagickRealType) (x_offset+geometry_info.xi);
795 if ((flags & YValue) == 0)
796 if ((flags & AspectValue) != 0)
797 center.y=(MagickRealType) ((image->rows-1)/2.0);
799 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
801 if ((flags & AspectValue) != 0)
802 center.y=geometry_info.psi;
804 center.y=(MagickRealType) (y_offset+geometry_info.psi);
807 Shift the pixel offset point as defined by the provided,
808 displacement/distortion map. -- Like a lens...
810 GetPixelInfo(image,&pixel);
811 image_view=AcquireVirtualCacheView(image,exception);
812 source_view=AcquireVirtualCacheView(source_image,exception);
813 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
814 for (y=0; y < (ssize_t) source_image->rows; y++)
819 register const Quantum
828 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
830 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
832 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
834 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
836 for (x=0; x < (ssize_t) source_image->columns; x++)
838 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
840 p+=GetPixelChannels(source_image);
846 offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
847 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
848 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
850 offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
851 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
852 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
854 (void) InterpolatePixelInfo(image,image_view,
855 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
858 Mask with the 'invalid pixel mask' in alpha channel.
860 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
861 (QuantumScale*GetPixelAlpha(source_image,p));
862 SetPixelViaPixelInfo(canvas_image,&pixel,q);
863 p+=GetPixelChannels(source_image);
864 q+=GetPixelChannels(canvas_image);
866 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
867 if (sync == MagickFalse)
870 canvas_view=DestroyCacheView(canvas_view);
871 source_view=DestroyCacheView(source_view);
872 image_view=DestroyCacheView(image_view);
873 source_image=DestroyImage(source_image);
874 source_image=canvas_image;
877 case DissolveCompositeOp:
880 Geometry arguments to dissolve factors.
882 value=GetImageArtifact(image,"compose:args");
883 if (value != (char *) NULL)
885 flags=ParseGeometry(value,&geometry_info);
886 source_dissolve=geometry_info.rho/100.0;
888 if ((source_dissolve-MagickEpsilon) < 0.0)
890 if ((source_dissolve+MagickEpsilon) > 1.0)
892 canvas_dissolve=2.0-source_dissolve;
895 if ((flags & SigmaValue) != 0)
896 canvas_dissolve=geometry_info.sigma/100.0;
897 if ((canvas_dissolve-MagickEpsilon) < 0.0)
902 case BlendCompositeOp:
904 value=GetImageArtifact(image,"compose:args");
905 if (value != (char *) NULL)
907 flags=ParseGeometry(value,&geometry_info);
908 source_dissolve=geometry_info.rho/100.0;
909 canvas_dissolve=1.0-source_dissolve;
910 if ((flags & SigmaValue) != 0)
911 canvas_dissolve=geometry_info.sigma/100.0;
915 case MathematicsCompositeOp:
918 Just collect the values from "compose:args", setting.
919 Unused values are set to zero automagically.
921 Arguments are normally a comma separated list, so this probably should
922 be changed to some 'general comma list' parser, (with a minimum
925 SetGeometryInfo(&geometry_info);
926 value=GetImageArtifact(image,"compose:args");
927 if (value != (char *) NULL)
928 (void) ParseGeometry(value,&geometry_info);
931 case ModulateCompositeOp:
934 Determine the luma and chroma scale.
936 value=GetImageArtifact(image,"compose:args");
937 if (value != (char *) NULL)
939 flags=ParseGeometry(value,&geometry_info);
940 percent_luma=geometry_info.rho;
941 if ((flags & SigmaValue) != 0)
942 percent_chroma=geometry_info.sigma;
946 case ThresholdCompositeOp:
949 Determine the amount and threshold.
951 value=GetImageArtifact(image,"compose:args");
952 if (value != (char *) NULL)
954 flags=ParseGeometry(value,&geometry_info);
955 amount=geometry_info.rho;
956 threshold=geometry_info.sigma;
957 if ((flags & SigmaValue) == 0)
960 threshold*=QuantumRange;
971 midpoint=((MagickRealType) QuantumRange+1.0)/2;
972 source_view=AcquireVirtualCacheView(source_image,exception);
973 image_view=AcquireAuthenticCacheView(image,exception);
974 #if defined(MAGICKCORE_OPENMP_SUPPORT)
975 #pragma omp parallel for schedule(static,4) shared(progress,status) \
976 magick_threads(source_image,image,image->rows,1)
978 for (y=0; y < (ssize_t) image->rows; y++)
995 register const Quantum
1004 if (status == MagickFalse)
1006 if (clip_to_self != MagickFalse)
1010 if ((y-y_offset) >= (ssize_t) source_image->rows)
1014 If pixels is NULL, y is outside overlay region.
1016 pixels=(Quantum *) NULL;
1018 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1020 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1021 source_image->columns,1,exception);
1022 if (p == (const Quantum *) NULL)
1029 p-=x_offset*GetPixelChannels(source_image);
1031 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1032 if (q == (Quantum *) NULL)
1040 GetPixelInfo(image,&canvas_pixel);
1041 GetPixelInfo(source_image,&source_pixel);
1042 for (x=0; x < (ssize_t) image->columns; x++)
1062 if (clip_to_self != MagickFalse)
1066 q+=GetPixelChannels(image);
1069 if ((x-x_offset) >= (ssize_t) source_image->columns)
1072 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1073 ((x-x_offset) >= (ssize_t) source_image->columns))
1076 source[MaxPixelChannels];
1083 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
1085 if (GetPixelReadMask(image,q) == 0)
1087 q+=GetPixelChannels(image);
1090 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1095 PixelChannel channel=GetPixelChannelChannel(image,i);
1096 PixelTrait traits=GetPixelChannelTraits(image,channel);
1097 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1099 if ((traits == UndefinedPixelTrait) ||
1100 (source_traits == UndefinedPixelTrait))
1104 case AlphaCompositeOp:
1105 case ChangeMaskCompositeOp:
1106 case CopyAlphaCompositeOp:
1107 case DstAtopCompositeOp:
1108 case DstInCompositeOp:
1110 case OutCompositeOp:
1111 case SrcInCompositeOp:
1112 case SrcOutCompositeOp:
1114 if (channel == AlphaPixelChannel)
1115 pixel=(MagickRealType) TransparentAlpha;
1117 pixel=(MagickRealType) q[i];
1120 case ClearCompositeOp:
1121 case CopyCompositeOp:
1122 case ReplaceCompositeOp:
1123 case SrcCompositeOp:
1125 if (channel == AlphaPixelChannel)
1126 pixel=(MagickRealType) TransparentAlpha;
1131 case BlendCompositeOp:
1132 case DissolveCompositeOp:
1134 if (channel == AlphaPixelChannel)
1135 pixel=canvas_dissolve*GetPixelAlpha(source_image,source);
1137 pixel=(MagickRealType) source[channel];
1142 pixel=(MagickRealType) source[channel];
1146 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1147 ClampToQuantum(pixel);
1149 q+=GetPixelChannels(image);
1153 Authentic composite:
1154 Sa: normalized source alpha.
1155 Da: normalized canvas alpha.
1157 Sa=QuantumScale*GetPixelAlpha(source_image,p);
1158 Da=QuantumScale*GetPixelAlpha(image,q);
1161 case BumpmapCompositeOp:
1163 alpha=GetPixelIntensity(source_image,p)*Sa;
1166 case ColorBurnCompositeOp:
1167 case ColorDodgeCompositeOp:
1168 case DarkenCompositeOp:
1169 case DifferenceCompositeOp:
1170 case DivideDstCompositeOp:
1171 case DivideSrcCompositeOp:
1172 case ExclusionCompositeOp:
1173 case HardLightCompositeOp:
1174 case HardMixCompositeOp:
1175 case LinearBurnCompositeOp:
1176 case LinearDodgeCompositeOp:
1177 case LinearLightCompositeOp:
1178 case LightenCompositeOp:
1179 case MathematicsCompositeOp:
1180 case MinusDstCompositeOp:
1181 case MinusSrcCompositeOp:
1182 case ModulusAddCompositeOp:
1183 case ModulusSubtractCompositeOp:
1184 case MultiplyCompositeOp:
1185 case OverlayCompositeOp:
1186 case PegtopLightCompositeOp:
1187 case PinLightCompositeOp:
1188 case ScreenCompositeOp:
1189 case SoftLightCompositeOp:
1190 case VividLightCompositeOp:
1192 alpha=RoundToUnity(Sa+Da-Sa*Da);
1195 case DstAtopCompositeOp:
1196 case DstInCompositeOp:
1198 case SrcInCompositeOp:
1203 case DissolveCompositeOp:
1205 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
1209 case DstOverCompositeOp:
1210 case OverCompositeOp:
1211 case SrcOverCompositeOp:
1216 case DstOutCompositeOp:
1221 case OutCompositeOp:
1222 case SrcOutCompositeOp:
1227 case BlendCompositeOp:
1228 case PlusCompositeOp:
1230 alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
1233 case XorCompositeOp:
1235 alpha=Sa+Da-2.0*Sa*Da;
1244 if (GetPixelReadMask(image,q) == 0)
1246 p+=GetPixelChannels(source_image);
1247 q+=GetPixelChannels(image);
1252 case ColorizeCompositeOp:
1253 case HueCompositeOp:
1254 case LuminizeCompositeOp:
1255 case ModulateCompositeOp:
1256 case SaturateCompositeOp:
1258 GetPixelInfoPixel(source_image,p,&source_pixel);
1259 GetPixelInfoPixel(image,q,&canvas_pixel);
1265 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1271 PixelChannel channel=GetPixelChannelChannel(image,i);
1272 PixelTrait traits=GetPixelChannelTraits(image,channel);
1273 PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
1274 if (traits == UndefinedPixelTrait)
1276 if ((source_traits == UndefinedPixelTrait) &&
1277 (channel != AlphaPixelChannel))
1279 if (channel == AlphaPixelChannel)
1286 case AlphaCompositeOp:
1288 pixel=QuantumRange*Sa;
1291 case AtopCompositeOp:
1292 case CopyBlackCompositeOp:
1293 case CopyBlueCompositeOp:
1294 case CopyCyanCompositeOp:
1295 case CopyGreenCompositeOp:
1296 case CopyMagentaCompositeOp:
1297 case CopyRedCompositeOp:
1298 case CopyYellowCompositeOp:
1299 case SrcAtopCompositeOp:
1300 case DstCompositeOp:
1303 pixel=QuantumRange*Da;
1306 case ChangeMaskCompositeOp:
1313 pixel=(MagickRealType) TransparentAlpha;
1316 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1317 if (equivalent != MagickFalse)
1318 pixel=(MagickRealType) TransparentAlpha;
1320 pixel=(MagickRealType) OpaqueAlpha;
1323 case ClearCompositeOp:
1325 pixel=(MagickRealType) TransparentAlpha;
1328 case ColorizeCompositeOp:
1329 case HueCompositeOp:
1330 case LuminizeCompositeOp:
1331 case SaturateCompositeOp:
1333 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1335 pixel=QuantumRange*Da;
1338 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1340 pixel=QuantumRange*Sa;
1345 pixel=QuantumRange*Da;
1348 pixel=QuantumRange*Sa;
1351 case CopyAlphaCompositeOp:
1353 if (source_image->alpha_trait == UndefinedPixelTrait)
1354 pixel=GetPixelIntensity(source_image,p);
1356 pixel=QuantumRange*Sa;
1359 case CopyCompositeOp:
1360 case DisplaceCompositeOp:
1361 case DistortCompositeOp:
1362 case DstAtopCompositeOp:
1363 case ReplaceCompositeOp:
1364 case SrcCompositeOp:
1366 pixel=QuantumRange*Sa;
1369 case DarkenIntensityCompositeOp:
1371 pixel=Sa*GetPixelIntensity(source_image,p) <
1372 Da*GetPixelIntensity(image,q) ? Sa : Da;
1375 case LightenIntensityCompositeOp:
1377 pixel=Sa*GetPixelIntensity(source_image,p) >
1378 Da*GetPixelIntensity(image,q) ? Sa : Da;
1381 case ModulateCompositeOp:
1383 pixel=QuantumRange*Da;
1388 pixel=QuantumRange*alpha;
1392 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1393 ClampToQuantum(pixel);
1400 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1401 Dc=(MagickRealType) q[i];
1402 if ((traits & CopyPixelTrait) != 0)
1411 Porter-Duff compositions:
1412 Sca: source normalized color multiplied by alpha.
1413 Dca: normalized canvas color multiplied by alpha.
1415 Sca=QuantumScale*Sa*Sc;
1416 Dca=QuantumScale*Da*Dc;
1419 case DarkenCompositeOp:
1420 case LightenCompositeOp:
1421 case ModulusSubtractCompositeOp:
1423 gamma=PerceptibleReciprocal(1.0-alpha);
1428 gamma=PerceptibleReciprocal(alpha);
1435 case AlphaCompositeOp:
1437 pixel=QuantumRange*Sa;
1440 case AtopCompositeOp:
1441 case SrcAtopCompositeOp:
1443 pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1446 case BlendCompositeOp:
1448 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1451 case BlurCompositeOp:
1452 case CopyCompositeOp:
1453 case ReplaceCompositeOp:
1454 case SrcCompositeOp:
1456 pixel=QuantumRange*Sca;
1459 case DisplaceCompositeOp:
1460 case DistortCompositeOp:
1465 case BumpmapCompositeOp:
1467 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1472 pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1475 case ChangeMaskCompositeOp:
1480 case ClearCompositeOp:
1485 case ColorBurnCompositeOp:
1487 if ((Sca == 0.0) && (Dca == Da))
1489 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1494 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1497 pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-Dca/Da)*Sa/
1498 Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1501 case ColorDodgeCompositeOp:
1503 if ((Sca*Da+Dca*Sa) >= Sa*Da)
1504 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1506 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
1510 case ColorizeCompositeOp:
1512 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1517 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1522 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1524 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1525 &hue,&chroma,&sans);
1526 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1529 case RedPixelChannel: pixel=red; break;
1530 case GreenPixelChannel: pixel=green; break;
1531 case BluePixelChannel: pixel=blue; break;
1532 default: pixel=Dc; break;
1536 case CopyAlphaCompositeOp:
1541 case CopyBlackCompositeOp:
1543 if (channel == BlackPixelChannel)
1544 pixel=(MagickRealType) (QuantumRange-
1545 GetPixelBlack(source_image,p));
1548 case CopyBlueCompositeOp:
1549 case CopyYellowCompositeOp:
1551 if (channel == BluePixelChannel)
1552 pixel=(MagickRealType) GetPixelBlue(source_image,p);
1555 case CopyGreenCompositeOp:
1556 case CopyMagentaCompositeOp:
1558 if (channel == GreenPixelChannel)
1559 pixel=(MagickRealType) GetPixelGreen(source_image,p);
1562 case CopyRedCompositeOp:
1563 case CopyCyanCompositeOp:
1565 if (channel == RedPixelChannel)
1566 pixel=(MagickRealType) GetPixelRed(source_image,p);
1569 case DarkenCompositeOp:
1572 Darken is equivalent to a 'Minimum' method
1573 OR a greyscale version of a binary 'Or'
1574 OR the 'Intersection' of pixel sets.
1576 if ((Sca*Da) < (Dca*Sa))
1578 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1581 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1584 case DarkenIntensityCompositeOp:
1586 pixel=Sa*GetPixelIntensity(source_image,p) <
1587 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1590 case DifferenceCompositeOp:
1592 pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1595 case DissolveCompositeOp:
1597 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1598 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1601 case DivideDstCompositeOp:
1603 if ((fabs((double) Sca) < MagickEpsilon) &&
1604 (fabs((double) Dca) < MagickEpsilon))
1606 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1609 if (fabs((double) Dca) < MagickEpsilon)
1611 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1614 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1617 case DivideSrcCompositeOp:
1619 if ((fabs((double) Dca) < MagickEpsilon) &&
1620 (fabs((double) Sca) < MagickEpsilon))
1622 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1625 if (fabs((double) Sca) < MagickEpsilon)
1627 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1630 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1633 case DstAtopCompositeOp:
1635 pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1638 case DstCompositeOp:
1641 pixel=QuantumRange*Dca;
1644 case DstInCompositeOp:
1646 pixel=QuantumRange*(Dca*Sa);
1649 case DstOutCompositeOp:
1651 pixel=QuantumRange*(Dca*(1.0-Sa));
1654 case DstOverCompositeOp:
1656 pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1659 case ExclusionCompositeOp:
1661 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1665 case HardLightCompositeOp:
1669 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1673 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1677 case HardMixCompositeOp:
1679 pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1682 case HueCompositeOp:
1684 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1689 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1694 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1695 &hue,&chroma,&luma);
1696 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1698 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1701 case RedPixelChannel: pixel=red; break;
1702 case GreenPixelChannel: pixel=green; break;
1703 case BluePixelChannel: pixel=blue; break;
1704 default: pixel=Dc; break;
1709 case SrcInCompositeOp:
1711 pixel=QuantumRange*(Sca*Da);
1714 case LinearBurnCompositeOp:
1717 LinearBurn: as defined by Abode Photoshop, according to
1718 http://www.simplefilter.de/en/basics/mixmods.html is:
1720 f(Sc,Dc) = Sc + Dc - 1
1722 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1725 case LinearDodgeCompositeOp:
1727 pixel=gamma*(Sa*Sc+Da*Dc);
1730 case LinearLightCompositeOp:
1733 LinearLight: as defined by Abode Photoshop, according to
1734 http://www.simplefilter.de/en/basics/mixmods.html is:
1736 f(Sc,Dc) = Dc + 2*Sc - 1
1738 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1741 case LightenCompositeOp:
1743 if ((Sca*Da) > (Dca*Sa))
1745 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1748 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1751 case LightenIntensityCompositeOp:
1754 Lighten is equivalent to a 'Maximum' method
1755 OR a greyscale version of a binary 'And'
1756 OR the 'Union' of pixel sets.
1758 pixel=Sa*GetPixelIntensity(source_image,p) >
1759 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1762 case LuminizeCompositeOp:
1764 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1769 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1774 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1775 &hue,&chroma,&luma);
1776 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1778 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1781 case RedPixelChannel: pixel=red; break;
1782 case GreenPixelChannel: pixel=green; break;
1783 case BluePixelChannel: pixel=blue; break;
1784 default: pixel=Dc; break;
1788 case MathematicsCompositeOp:
1791 'Mathematics' a free form user control mathematical composition
1794 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
1796 Where the arguments A,B,C,D are (currently) passed to composite
1797 as a command separated 'geometry' string in "compose:args" image
1800 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
1802 Applying the SVG transparency formula (see above), we get...
1804 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
1806 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
1809 pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
1810 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
1811 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1814 case MinusDstCompositeOp:
1816 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
1819 case MinusSrcCompositeOp:
1822 Minus source from canvas.
1826 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
1829 case ModulateCompositeOp:
1834 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1839 offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
1845 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1846 &hue,&chroma,&luma);
1847 luma+=(0.01*percent_luma*offset)/midpoint;
1848 chroma*=0.01*percent_chroma;
1849 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1852 case RedPixelChannel: pixel=red; break;
1853 case GreenPixelChannel: pixel=green; break;
1854 case BluePixelChannel: pixel=blue; break;
1855 default: pixel=Dc; break;
1859 case ModulusAddCompositeOp:
1862 while (pixel > QuantumRange)
1863 pixel-=QuantumRange;
1865 pixel+=QuantumRange;
1866 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
1869 case ModulusSubtractCompositeOp:
1872 while (pixel > QuantumRange)
1873 pixel-=QuantumRange;
1875 pixel+=QuantumRange;
1876 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
1879 case MultiplyCompositeOp:
1881 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1884 case OutCompositeOp:
1885 case SrcOutCompositeOp:
1887 pixel=QuantumRange*(Sca*(1.0-Da));
1890 case OverCompositeOp:
1891 case SrcOverCompositeOp:
1893 pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
1896 case OverlayCompositeOp:
1900 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-
1904 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
1908 case PegtopLightCompositeOp:
1911 PegTop: A Soft-Light alternative: A continuous version of the
1912 Softlight function, producing very similar results.
1914 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
1916 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
1918 if (fabs((double) Da) < MagickEpsilon)
1920 pixel=QuantumRange*gamma*(Sca);
1923 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
1927 case PinLightCompositeOp:
1930 PinLight: A Photoshop 7 composition method
1931 http://www.simplefilter.de/en/basics/mixmods.html
1933 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
1935 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
1937 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
1940 if ((Dca*Sa) > (2.0*Sca*Da))
1942 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
1945 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
1948 case PlusCompositeOp:
1950 pixel=QuantumRange*(Sca+Dca);
1953 case SaturateCompositeOp:
1955 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1960 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1965 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1966 &hue,&chroma,&luma);
1967 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1968 &sans,&chroma,&sans);
1969 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1972 case RedPixelChannel: pixel=red; break;
1973 case GreenPixelChannel: pixel=green; break;
1974 case BluePixelChannel: pixel=blue; break;
1975 default: pixel=Dc; break;
1979 case ScreenCompositeOp:
1982 Screen: a negated multiply:
1984 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
1986 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
1989 case SoftLightCompositeOp:
1993 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
1994 Sca*(1.0-Da)+Dca*(1.0-Sa));
1997 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
1999 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2000 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2004 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2005 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2008 case ThresholdCompositeOp:
2014 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2019 pixel=gamma*(Dc+delta*amount);
2022 case VividLightCompositeOp:
2025 VividLight: A Photoshop 7 composition method. See
2026 http://www.simplefilter.de/en/basics/mixmods.html.
2028 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2030 if ((fabs((double) Sa) < MagickEpsilon) ||
2031 (fabs((double) (Sca-Sa)) < MagickEpsilon))
2033 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2036 if ((2.0*Sca) <= Sa)
2038 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2039 (1.0-Da)+Dca*(1.0-Sa));
2042 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*
2046 case XorCompositeOp:
2048 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2057 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2059 p+=GetPixelChannels(source_image);
2060 channels=GetPixelChannels(source_image);
2061 if (p >= (pixels+channels*source_image->columns))
2063 q+=GetPixelChannels(image);
2065 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2067 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2072 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2073 #pragma omp critical (MagickCore_CompositeImage)
2075 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2077 if (proceed == MagickFalse)
2081 source_view=DestroyCacheView(source_view);
2082 image_view=DestroyCacheView(image_view);
2083 if (canvas_image != (Image * ) NULL)
2084 canvas_image=DestroyImage(canvas_image);
2086 source_image=DestroyImage(source_image);
2091 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2095 % T e x t u r e I m a g e %
2099 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2101 % TextureImage() repeatedly tiles the texture image across and down the image
2104 % The format of the TextureImage method is:
2106 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2107 % ExceptionInfo *exception)
2109 % A description of each parameter follows:
2111 % o image: the image.
2113 % o texture_image: This image is the texture to layer on the background.
2116 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2117 ExceptionInfo *exception)
2119 #define TextureImageTag "Texture/Image"
2134 assert(image != (Image *) NULL);
2135 if (image->debug != MagickFalse)
2136 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2137 assert(image->signature == MagickCoreSignature);
2138 if (texture == (const Image *) NULL)
2139 return(MagickFalse);
2140 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2141 return(MagickFalse);
2142 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2143 if (texture_image == (const Image *) NULL)
2144 return(MagickFalse);
2145 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2146 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2149 if ((image->compose != CopyCompositeOp) &&
2150 ((image->compose != OverCompositeOp) ||
2151 (image->alpha_trait != UndefinedPixelTrait) ||
2152 (texture_image->alpha_trait != UndefinedPixelTrait)))
2155 Tile texture onto the image background.
2157 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2162 if (status == MagickFalse)
2164 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2169 thread_status=CompositeImage(image,texture_image,image->compose,
2170 MagickTrue,x+texture_image->tile_offset.x,y+
2171 texture_image->tile_offset.y,exception);
2172 if (thread_status == MagickFalse)
2174 status=thread_status;
2178 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2183 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2185 if (proceed == MagickFalse)
2189 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2190 image->rows,image->rows);
2191 texture_image=DestroyImage(texture_image);
2195 Tile texture onto the image background (optimized).
2198 texture_view=AcquireVirtualCacheView(texture_image,exception);
2199 image_view=AcquireAuthenticCacheView(image,exception);
2200 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2201 #pragma omp parallel for schedule(static,4) shared(status) \
2202 magick_threads(texture_image,image,1,1)
2204 for (y=0; y < (ssize_t) image->rows; y++)
2209 register const Quantum
2222 if (status == MagickFalse)
2224 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2225 (y+texture_image->tile_offset.y) % texture_image->rows,
2226 texture_image->columns,1,exception);
2227 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2228 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2233 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2239 width=texture_image->columns;
2240 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2241 width=image->columns-x;
2242 for (j=0; j < (ssize_t) width; j++)
2247 if (GetPixelReadMask(image,q) == 0)
2249 p+=GetPixelChannels(texture_image);
2250 q+=GetPixelChannels(image);
2253 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2255 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2256 PixelTrait traits=GetPixelChannelTraits(image,channel);
2257 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2259 if ((traits == UndefinedPixelTrait) ||
2260 (texture_traits == UndefinedPixelTrait))
2262 SetPixelChannel(image,channel,p[i],q);
2264 p+=GetPixelChannels(texture_image);
2265 q+=GetPixelChannels(image);
2268 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2269 if (sync == MagickFalse)
2271 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2276 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2278 if (proceed == MagickFalse)
2282 texture_view=DestroyCacheView(texture_view);
2283 image_view=DestroyCacheView(image_view);
2284 texture_image=DestroyImage(texture_image);