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-2015 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"
311 source_view=AcquireVirtualCacheView(source_image,exception);
312 image_view=AcquireAuthenticCacheView(image,exception);
313 #if defined(MAGICKCORE_OPENMP_SUPPORT)
314 #pragma omp parallel for schedule(static,4) shared(progress,status) \
315 magick_threads(source_image,image,image->rows,1)
317 for (y=0; y < (ssize_t) image->rows; y++)
322 register const Quantum
334 if (status == MagickFalse)
336 if (clip_to_self != MagickFalse)
340 if ((y-y_offset) >= (ssize_t) source_image->rows)
344 If pixels is NULL, y is outside overlay region.
346 pixels=(Quantum *) NULL;
348 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
350 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
351 source_image->columns,1,exception);
352 if (p == (const Quantum *) NULL)
359 p-=x_offset*GetPixelChannels(source_image);
361 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
362 if (q == (Quantum *) NULL)
367 for (x=0; x < (ssize_t) image->columns; x++)
380 if (clip_to_self != MagickFalse)
384 q+=GetPixelChannels(image);
387 if ((x-x_offset) >= (ssize_t) source_image->columns)
390 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
391 ((x-x_offset) >= (ssize_t) source_image->columns))
394 source[MaxPixelChannels];
401 if (GetPixelReadMask(image,q) == 0)
403 q+=GetPixelChannels(image);
406 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,
408 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
410 PixelChannel channel=GetPixelChannelChannel(image,i);
411 PixelTrait traits=GetPixelChannelTraits(image,channel);
412 PixelTrait source_traits=GetPixelChannelTraits(source_image,
414 if ((traits == UndefinedPixelTrait) ||
415 (source_traits == UndefinedPixelTrait))
417 q[i]=source[channel];
419 q+=GetPixelChannels(image);
424 Sa: normalized source alpha.
425 Da: normalized canvas alpha.
427 if (GetPixelReadMask(source_image,p) == 0)
429 p+=GetPixelChannels(source_image);
430 channels=GetPixelChannels(source_image);
431 if (p >= (pixels+channels*source_image->columns))
433 q+=GetPixelChannels(image);
436 Sa=QuantumScale*GetPixelAlpha(source_image,p);
437 Da=QuantumScale*GetPixelAlpha(image,q);
438 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
440 PixelChannel channel=GetPixelChannelChannel(image,i);
441 PixelTrait traits=GetPixelChannelTraits(image,channel);
442 PixelTrait source_traits=GetPixelChannelTraits(source_image,
444 if ((traits == UndefinedPixelTrait) ||
445 (source_traits == UndefinedPixelTrait))
447 if ((traits & CopyPixelTrait) != 0)
452 q[i]=GetPixelChannel(source_image,channel,p);
455 if (channel == AlphaPixelChannel)
460 q[i]=ClampToQuantum(QuantumRange*(Sa+Da-Sa*Da));
467 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
468 Dc=(MagickRealType) q[i];
469 Sca=QuantumScale*Sa*Sc;
470 Dca=QuantumScale*Da*Dc;
471 q[i]=ClampToQuantum(QuantumRange*(Sca+Dca*(1.0-Sa)));
473 p+=GetPixelChannels(source_image);
474 channels=GetPixelChannels(source_image);
475 if (p >= (pixels+channels*source_image->columns))
477 q+=GetPixelChannels(image);
479 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
481 if (image->progress_monitor != (MagickProgressMonitor) NULL)
486 #if defined(MAGICKCORE_OPENMP_SUPPORT)
487 #pragma omp critical (MagickCore_CompositeImage)
489 proceed=SetImageProgress(image,CompositeImageTag,progress++,
491 if (proceed == MagickFalse)
495 source_view=DestroyCacheView(source_view);
496 image_view=DestroyCacheView(image_view);
500 MagickExport MagickBooleanType CompositeImage(Image *image,
501 const Image *composite,const CompositeOperator compose,
502 const MagickBooleanType clip_to_self,const ssize_t x_offset,
503 const ssize_t y_offset,ExceptionInfo *exception)
505 #define CompositeImageTag "Composite/Image"
539 assert(image != (Image *) NULL);
540 assert(image->signature == MagickSignature);
541 if (image->debug != MagickFalse)
542 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
543 assert(composite!= (Image *) NULL);
544 assert(composite->signature == MagickSignature);
545 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
547 source_image=CloneImage(composite,0,0,MagickTrue,exception);
548 if (source_image == (const Image *) NULL)
550 if (IsGrayColorspace(image->colorspace) != MagickFalse)
551 (void) SetImageColorspace(image,sRGBColorspace,exception);
552 (void) SetImageColorspace(source_image,image->colorspace,exception);
553 if ((image->alpha_trait != UndefinedPixelTrait) &&
554 (source_image->alpha_trait == UndefinedPixelTrait))
555 (void) SetImageAlphaChannel(source_image,SetAlphaChannel,exception);
557 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
559 status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
561 source_image=DestroyImage(source_image);
564 canvas_image=(Image *) NULL;
568 percent_chroma=100.0;
573 case CopyCompositeOp:
575 if ((x_offset < 0) || (y_offset < 0))
577 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
579 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
582 source_view=AcquireVirtualCacheView(source_image,exception);
583 image_view=AcquireAuthenticCacheView(image,exception);
584 #if defined(MAGICKCORE_OPENMP_SUPPORT)
585 #pragma omp parallel for schedule(static,4) shared(status) \
586 magick_threads(source_image,image,source_image->rows,1)
588 for (y=0; y < (ssize_t) source_image->rows; y++)
593 register const Quantum
602 if (status == MagickFalse)
604 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,
606 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
607 source_image->columns,1,exception);
608 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
613 for (x=0; x < (ssize_t) source_image->columns; x++)
618 if (GetPixelReadMask(source_image,p) == 0)
620 p+=GetPixelChannels(source_image);
621 q+=GetPixelChannels(image);
624 for (i=0; i < (ssize_t) GetPixelChannels(source_image); i++)
626 PixelChannel channel=GetPixelChannelChannel(source_image,i);
627 PixelTrait source_traits=GetPixelChannelTraits(source_image,
629 PixelTrait traits=GetPixelChannelTraits(image,channel);
630 if ((traits == UndefinedPixelTrait) ||
631 (source_traits == UndefinedPixelTrait))
633 SetPixelChannel(image,channel,p[i],q);
635 p+=GetPixelChannels(source_image);
636 q+=GetPixelChannels(image);
638 sync=SyncCacheViewAuthenticPixels(image_view,exception);
639 if (sync == MagickFalse)
641 if (image->progress_monitor != (MagickProgressMonitor) NULL)
646 #if defined(MAGICKCORE_OPENMP_SUPPORT)
647 #pragma omp critical (MagickCore_CompositeImage)
649 proceed=SetImageProgress(image,CompositeImageTag,
650 (MagickOffsetType) y,image->rows);
651 if (proceed == MagickFalse)
655 source_view=DestroyCacheView(source_view);
656 image_view=DestroyCacheView(image_view);
657 source_image=DestroyImage(source_image);
660 case IntensityCompositeOp:
662 if ((x_offset < 0) || (y_offset < 0))
664 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
666 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
669 source_view=AcquireVirtualCacheView(source_image,exception);
670 image_view=AcquireAuthenticCacheView(image,exception);
671 #if defined(MAGICKCORE_OPENMP_SUPPORT)
672 #pragma omp parallel for schedule(static,4) shared(status) \
673 magick_threads(source_image,image,source_image->rows,1)
675 for (y=0; y < (ssize_t) source_image->rows; y++)
680 register const Quantum
689 if (status == MagickFalse)
691 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,
693 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
694 source_image->columns,1,exception);
695 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
700 for (x=0; x < (ssize_t) source_image->columns; x++)
702 if (GetPixelReadMask(source_image,p) == 0)
704 p+=GetPixelChannels(source_image);
705 q+=GetPixelChannels(image);
708 SetPixelAlpha(image,ClampToQuantum(GetPixelIntensity(source_image,
710 p+=GetPixelChannels(source_image);
711 q+=GetPixelChannels(image);
713 sync=SyncCacheViewAuthenticPixels(image_view,exception);
714 if (sync == MagickFalse)
716 if (image->progress_monitor != (MagickProgressMonitor) NULL)
721 #if defined(MAGICKCORE_OPENMP_SUPPORT)
722 #pragma omp critical (MagickCore_CompositeImage)
724 proceed=SetImageProgress(image,CompositeImageTag,
725 (MagickOffsetType) y,image->rows);
726 if (proceed == MagickFalse)
730 source_view=DestroyCacheView(source_view);
731 image_view=DestroyCacheView(image_view);
732 source_image=DestroyImage(source_image);
735 case CopyAlphaCompositeOp:
736 case ChangeMaskCompositeOp:
739 Modify canvas outside the overlaid region and require an alpha
740 channel to exist, to add transparency.
742 if (image->alpha_trait == UndefinedPixelTrait)
743 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
746 case BlurCompositeOp:
771 Blur Image by resampling.
773 Blur Image dictated by an overlay gradient map: X = red_channel;
774 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
776 canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
778 if (canvas_image == (Image *) NULL)
780 source_image=DestroyImage(source_image);
784 Gather the maximum blur sigma values from user.
786 SetGeometryInfo(&geometry_info);
788 value=GetImageArtifact(image,"compose:args");
789 if (value != (const char *) NULL)
790 flags=ParseGeometry(value,&geometry_info);
791 if ((flags & WidthValue) == 0)
793 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
794 "InvalidSetting","'%s' '%s'","compose:args",value);
795 source_image=DestroyImage(source_image);
796 canvas_image=DestroyImage(canvas_image);
800 Users input sigma now needs to be converted to the EWA ellipse size.
801 The filter defaults to a sigma of 0.5 so to make this match the
802 users input the ellipse size needs to be doubled.
804 width=height=geometry_info.rho*2.0;
805 if ((flags & HeightValue) != 0 )
806 height=geometry_info.sigma*2.0;
808 Default the unrotated ellipse width and height axis vectors.
814 /* rotate vectors if a rotation angle is given */
815 if ((flags & XValue) != 0 )
820 angle=DegreesToRadians(geometry_info.xi);
821 blur.x1=width*cos(angle);
822 blur.x2=width*sin(angle);
823 blur.y1=(-height*sin(angle));
824 blur.y2=height*cos(angle);
826 /* Otherwise lets set a angle range and calculate in the loop */
829 if ((flags & YValue) != 0 )
831 angle_start=DegreesToRadians(geometry_info.xi);
832 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
835 Set up a gaussian cylindrical filter for EWA Bluring.
837 As the minimum ellipse radius of support*1.0 the EWA algorithm
838 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
839 This means that even 'No Blur' will be still a little blurry!
841 The solution (as well as the problem of preventing any user
842 expert filter settings, is to set our own user settings, then
843 restore them afterwards.
845 resample_filter=AcquireResampleFilter(image,exception);
846 SetResampleFilter(resample_filter,GaussianFilter);
848 /* do the variable blurring of each pixel in image */
849 GetPixelInfo(image,&pixel);
850 source_view=AcquireVirtualCacheView(source_image,exception);
851 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
852 for (y=0; y < (ssize_t) source_image->rows; y++)
857 register const Quantum
866 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
868 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,
870 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,
871 canvas_image->columns,1,exception);
872 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
874 for (x=0; x < (ssize_t) source_image->columns; x++)
876 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
878 p+=GetPixelChannels(source_image);
881 if (fabs((double) angle_range) > MagickEpsilon)
886 angle=angle_start+angle_range*QuantumScale*
887 GetPixelBlue(source_image,p);
888 blur.x1=width*cos(angle);
889 blur.x2=width*sin(angle);
890 blur.y1=(-height*sin(angle));
891 blur.y2=height*cos(angle);
894 if ( x == 10 && y == 60 ) {
895 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
896 blur.x2,blur.y1, blur.y2);
897 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
898 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
900 ScaleResampleFilter(resample_filter,
901 blur.x1*QuantumScale*GetPixelRed(source_image,p),
902 blur.y1*QuantumScale*GetPixelGreen(source_image,p),
903 blur.x2*QuantumScale*GetPixelRed(source_image,p),
904 blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
905 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
906 (double) y_offset+y,&pixel,exception);
907 SetPixelViaPixelInfo(canvas_image,&pixel,q);
908 p+=GetPixelChannels(source_image);
909 q+=GetPixelChannels(canvas_image);
911 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
912 if (sync == MagickFalse)
915 resample_filter=DestroyResampleFilter(resample_filter);
916 source_view=DestroyCacheView(source_view);
917 canvas_view=DestroyCacheView(canvas_view);
918 source_image=DestroyImage(source_image);
919 source_image=canvas_image;
922 case DisplaceCompositeOp:
923 case DistortCompositeOp:
945 Displace/Distort based on overlay gradient map:
946 X = red_channel; Y = green_channel;
947 compose:args = x_scale[,y_scale[,center.x,center.y]]
949 canvas_image=CloneImage(image,image->columns,image->rows,MagickTrue,
951 if (canvas_image == (Image *) NULL)
953 source_image=DestroyImage(source_image);
956 SetGeometryInfo(&geometry_info);
958 value=GetImageArtifact(image,"compose:args");
959 if (value != (char *) NULL)
960 flags=ParseGeometry(value,&geometry_info);
961 if ((flags & (WidthValue|HeightValue)) == 0 )
963 if ((flags & AspectValue) == 0)
965 horizontal_scale=(MagickRealType) (source_image->columns-1.0)/
967 vertical_scale=(MagickRealType) (source_image->rows-1.0)/2.0;
971 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
972 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
977 horizontal_scale=geometry_info.rho;
978 vertical_scale=geometry_info.sigma;
979 if ((flags & PercentValue) != 0)
981 if ((flags & AspectValue) == 0)
983 horizontal_scale*=(source_image->columns-1.0)/200.0;
984 vertical_scale*=(source_image->rows-1.0)/200.0;
988 horizontal_scale*=(image->columns-1.0)/200.0;
989 vertical_scale*=(image->rows-1.0)/200.0;
992 if ((flags & HeightValue) == 0)
993 vertical_scale=horizontal_scale;
996 Determine fixed center point for absolute distortion map
998 Displace offset relative to a fixed absolute point
999 Select that point according to +X+Y user inputs.
1000 default = center of overlay image
1001 arg flag '!' = locations/percentage relative to background image
1003 center.x=(MagickRealType) x_offset;
1004 center.y=(MagickRealType) y_offset;
1005 if (compose == DistortCompositeOp)
1007 if ((flags & XValue) == 0)
1008 if ((flags & AspectValue) == 0)
1009 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1012 center.x=(MagickRealType) ((image->columns-1)/2);
1014 if ((flags & AspectValue) == 0)
1015 center.x=(MagickRealType) x_offset+geometry_info.xi;
1017 center.x=geometry_info.xi;
1018 if ((flags & YValue) == 0)
1019 if ((flags & AspectValue) == 0)
1020 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/
1023 center.y=(MagickRealType) ((image->rows-1)/2);
1025 if ((flags & AspectValue) == 0)
1026 center.y=(MagickRealType) y_offset+geometry_info.psi;
1028 center.y=geometry_info.psi;
1031 Shift the pixel offset point as defined by the provided,
1032 displacement/distortion map. -- Like a lens...
1034 GetPixelInfo(image,&pixel);
1035 image_view=AcquireVirtualCacheView(image,exception);
1036 source_view=AcquireVirtualCacheView(source_image,exception);
1037 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1038 for (y=0; y < (ssize_t) source_image->rows; y++)
1043 register const Quantum
1052 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1054 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,
1056 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,
1057 canvas_image->columns,1,exception);
1058 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1060 for (x=0; x < (ssize_t) source_image->columns; x++)
1062 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1064 p+=GetPixelChannels(source_image);
1068 Displace the offset.
1070 offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1071 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1072 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1074 offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1075 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1076 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1078 (void) InterpolatePixelInfo(image,image_view,
1079 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1082 Mask with the 'invalid pixel mask' in alpha channel.
1084 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1085 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(source_image,p)));
1086 SetPixelViaPixelInfo(canvas_image,&pixel,q);
1087 p+=GetPixelChannels(source_image);
1088 q+=GetPixelChannels(canvas_image);
1090 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1091 if (sync == MagickFalse)
1094 canvas_view=DestroyCacheView(canvas_view);
1095 source_view=DestroyCacheView(source_view);
1096 image_view=DestroyCacheView(image_view);
1097 source_image=DestroyImage(source_image);
1098 source_image=canvas_image;
1101 case DissolveCompositeOp:
1107 Geometry arguments to dissolve factors.
1109 value=GetImageArtifact(image,"compose:args");
1110 if (value != (char *) NULL)
1112 flags=ParseGeometry(value,&geometry_info);
1113 source_dissolve=geometry_info.rho/100.0;
1114 canvas_dissolve=1.0;
1115 if ((source_dissolve-MagickEpsilon) < 0.0)
1116 source_dissolve=0.0;
1117 if ((source_dissolve+MagickEpsilon) > 1.0)
1119 canvas_dissolve=2.0-source_dissolve;
1120 source_dissolve=1.0;
1122 if ((flags & SigmaValue) != 0)
1123 canvas_dissolve=geometry_info.sigma/100.0;
1124 if ((canvas_dissolve-MagickEpsilon) < 0.0)
1125 canvas_dissolve=0.0;
1129 case BlendCompositeOp:
1134 value=GetImageArtifact(image,"compose:args");
1135 if (value != (char *) NULL)
1137 flags=ParseGeometry(value,&geometry_info);
1138 source_dissolve=geometry_info.rho/100.0;
1139 canvas_dissolve=1.0-source_dissolve;
1140 if ((flags & SigmaValue) != 0)
1141 canvas_dissolve=geometry_info.sigma/100.0;
1145 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:
1170 Determine the luma and chroma scale.
1172 value=GetImageArtifact(image,"compose:args");
1173 if (value != (char *) NULL)
1175 flags=ParseGeometry(value,&geometry_info);
1176 percent_luma=geometry_info.rho;
1177 if ((flags & SigmaValue) != 0)
1178 percent_chroma=geometry_info.sigma;
1182 case ThresholdCompositeOp:
1188 Determine the amount and threshold.
1190 value=GetImageArtifact(image,"compose:args");
1191 if (value != (char *) NULL)
1193 flags=ParseGeometry(value,&geometry_info);
1194 amount=geometry_info.rho;
1195 threshold=geometry_info.sigma;
1196 if ((flags & SigmaValue) == 0)
1199 threshold*=QuantumRange;
1210 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1211 source_view=AcquireVirtualCacheView(source_image,exception);
1212 image_view=AcquireAuthenticCacheView(image,exception);
1213 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1214 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1215 magick_threads(source_image,image,image->rows,1)
1217 for (y=0; y < (ssize_t) image->rows; y++)
1234 register const Quantum
1243 if (status == MagickFalse)
1245 if (clip_to_self != MagickFalse)
1249 if ((y-y_offset) >= (ssize_t) source_image->rows)
1253 If pixels is NULL, y is outside overlay region.
1255 pixels=(Quantum *) NULL;
1257 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1259 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1260 source_image->columns,1,exception);
1261 if (p == (const Quantum *) NULL)
1268 p-=x_offset*GetPixelChannels(source_image);
1270 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1271 if (q == (Quantum *) NULL)
1279 GetPixelInfo(image,&canvas_pixel);
1280 GetPixelInfo(source_image,&source_pixel);
1281 for (x=0; x < (ssize_t) image->columns; x++)
1301 if (clip_to_self != MagickFalse)
1305 q+=GetPixelChannels(image);
1308 if ((x-x_offset) >= (ssize_t) source_image->columns)
1311 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1312 ((x-x_offset) >= (ssize_t) source_image->columns))
1315 source[MaxPixelChannels];
1322 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,
1324 if (GetPixelReadMask(image,q) == 0)
1326 q+=GetPixelChannels(image);
1329 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1334 PixelChannel channel=GetPixelChannelChannel(image,i);
1335 PixelTrait traits=GetPixelChannelTraits(image,channel);
1336 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1338 if ((traits == UndefinedPixelTrait) ||
1339 (source_traits == UndefinedPixelTrait))
1343 case AlphaCompositeOp:
1344 case ChangeMaskCompositeOp:
1345 case CopyAlphaCompositeOp:
1346 case DstAtopCompositeOp:
1347 case DstInCompositeOp:
1349 case OutCompositeOp:
1350 case SrcInCompositeOp:
1351 case SrcOutCompositeOp:
1353 if (channel == AlphaPixelChannel)
1354 pixel=(MagickRealType) TransparentAlpha;
1356 pixel=(MagickRealType) q[i];
1359 case ClearCompositeOp:
1360 case CopyCompositeOp:
1361 case ReplaceCompositeOp:
1362 case SrcCompositeOp:
1364 if (channel == AlphaPixelChannel)
1365 pixel=(MagickRealType) TransparentAlpha;
1370 case BlendCompositeOp:
1371 case DissolveCompositeOp:
1373 if (channel == AlphaPixelChannel)
1374 pixel=canvas_dissolve*GetPixelAlpha(source_image,
1377 pixel=(MagickRealType) source[channel];
1382 pixel=(MagickRealType) source[channel];
1386 q[i]=ClampToQuantum(pixel);
1388 q+=GetPixelChannels(image);
1392 Authentic composite:
1393 Sa: normalized source alpha.
1394 Da: normalized canvas alpha.
1396 Sa=QuantumScale*GetPixelAlpha(source_image,p);
1397 Da=QuantumScale*GetPixelAlpha(image,q);
1400 case BumpmapCompositeOp:
1402 alpha=GetPixelIntensity(source_image,p)*Sa;
1405 case ColorBurnCompositeOp:
1406 case ColorDodgeCompositeOp:
1407 case DarkenCompositeOp:
1408 case DifferenceCompositeOp:
1409 case DivideDstCompositeOp:
1410 case DivideSrcCompositeOp:
1411 case ExclusionCompositeOp:
1412 case HardLightCompositeOp:
1413 case HardMixCompositeOp:
1414 case LinearBurnCompositeOp:
1415 case LinearDodgeCompositeOp:
1416 case LinearLightCompositeOp:
1417 case LightenCompositeOp:
1418 case MathematicsCompositeOp:
1419 case MinusDstCompositeOp:
1420 case MinusSrcCompositeOp:
1421 case ModulusAddCompositeOp:
1422 case ModulusSubtractCompositeOp:
1423 case MultiplyCompositeOp:
1424 case OverlayCompositeOp:
1425 case PegtopLightCompositeOp:
1426 case PinLightCompositeOp:
1427 case ScreenCompositeOp:
1428 case SoftLightCompositeOp:
1429 case VividLightCompositeOp:
1431 alpha=RoundToUnity(Sa+Da-Sa*Da);
1434 case DstAtopCompositeOp:
1435 case DstInCompositeOp:
1437 case SrcInCompositeOp:
1442 case DissolveCompositeOp:
1444 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*
1445 Sa+canvas_dissolve*Da;
1448 case DstOverCompositeOp:
1453 case DstOutCompositeOp:
1458 case OutCompositeOp:
1459 case SrcOutCompositeOp:
1464 case OverCompositeOp:
1465 case SrcOverCompositeOp:
1470 case BlendCompositeOp:
1471 case PlusCompositeOp:
1473 alpha=RoundToUnity(Sa+Da);
1476 case XorCompositeOp:
1478 alpha=Sa+Da-2.0*Sa*Da;
1487 if (GetPixelReadMask(image,q) == 0)
1489 p+=GetPixelChannels(source_image);
1490 q+=GetPixelChannels(image);
1495 case ColorizeCompositeOp:
1496 case HueCompositeOp:
1497 case LuminizeCompositeOp:
1498 case ModulateCompositeOp:
1499 case SaturateCompositeOp:
1501 GetPixelInfoPixel(source_image,p,&source_pixel);
1502 GetPixelInfoPixel(image,q,&canvas_pixel);
1508 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1514 PixelChannel channel=GetPixelChannelChannel(image,i);
1515 PixelTrait traits=GetPixelChannelTraits(image,channel);
1516 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1518 if (traits == UndefinedPixelTrait)
1520 if ((source_traits == UndefinedPixelTrait) &&
1521 (((compose != CopyAlphaCompositeOp) &&
1522 (compose != ChangeMaskCompositeOp)) ||
1523 (channel != AlphaPixelChannel)))
1529 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1530 Dc=(MagickRealType) q[i];
1531 if ((traits & CopyPixelTrait) != 0)
1536 q[i]=ClampToQuantum(Sc);
1539 if (channel == AlphaPixelChannel)
1546 case AlphaCompositeOp:
1548 pixel=QuantumRange*Sa;
1551 case AtopCompositeOp:
1552 case CopyBlackCompositeOp:
1553 case CopyBlueCompositeOp:
1554 case CopyCyanCompositeOp:
1555 case CopyGreenCompositeOp:
1556 case CopyMagentaCompositeOp:
1557 case CopyRedCompositeOp:
1558 case CopyYellowCompositeOp:
1559 case SrcAtopCompositeOp:
1560 case DstCompositeOp:
1563 pixel=QuantumRange*Da;
1566 case ChangeMaskCompositeOp:
1571 if (Da > ((MagickRealType) QuantumRange/2.0))
1573 pixel=(MagickRealType) TransparentAlpha;
1576 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1577 if (equivalent != MagickFalse)
1578 pixel=(MagickRealType) TransparentAlpha;
1580 pixel=(MagickRealType) OpaqueAlpha;
1583 case ClearCompositeOp:
1585 pixel=(MagickRealType) TransparentAlpha;
1588 case ColorizeCompositeOp:
1589 case HueCompositeOp:
1590 case LuminizeCompositeOp:
1591 case SaturateCompositeOp:
1593 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1595 pixel=QuantumRange*Da;
1598 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1600 pixel=QuantumRange*Sa;
1605 pixel=QuantumRange*Da;
1608 pixel=QuantumRange*Sa;
1611 case CopyAlphaCompositeOp:
1613 if (source_traits == UndefinedPixelTrait)
1614 pixel=GetPixelIntensity(source_image,p);
1616 pixel=QuantumRange*Sa;
1619 case CopyCompositeOp:
1620 case DisplaceCompositeOp:
1621 case DistortCompositeOp:
1622 case DstAtopCompositeOp:
1623 case ReplaceCompositeOp:
1624 case SrcCompositeOp:
1626 pixel=QuantumRange*Sa;
1629 case DarkenIntensityCompositeOp:
1631 pixel=Sa*GetPixelIntensity(source_image,p) <
1632 Da*GetPixelIntensity(image,q) ? Sa : Da;
1635 case LightenIntensityCompositeOp:
1637 pixel=Sa*GetPixelIntensity(source_image,p) >
1638 Da*GetPixelIntensity(image,q) ? Sa : Da;
1641 case ModulateCompositeOp:
1643 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1645 pixel=QuantumRange*Da;
1648 pixel=QuantumRange*Da;
1653 pixel=QuantumRange*alpha;
1657 q[i]=ClampToQuantum(pixel);
1661 Porter-Duff compositions:
1662 Sca: source normalized color multiplied by alpha.
1663 Dca: normalized canvas color multiplied by alpha.
1665 Sca=QuantumScale*Sa*Sc;
1666 Dca=QuantumScale*Da*Dc;
1669 case DarkenCompositeOp:
1670 case LightenCompositeOp:
1671 case ModulusSubtractCompositeOp:
1679 gamma=PerceptibleReciprocal(alpha);
1683 case AlphaCompositeOp:
1685 pixel=QuantumRange*Sa;
1688 case AtopCompositeOp:
1689 case SrcAtopCompositeOp:
1691 pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1694 case BlendCompositeOp:
1696 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1699 case BlurCompositeOp:
1700 case DisplaceCompositeOp:
1701 case DistortCompositeOp:
1702 case CopyCompositeOp:
1703 case ReplaceCompositeOp:
1704 case SrcCompositeOp:
1706 pixel=QuantumRange*Sca;
1709 case BumpmapCompositeOp:
1711 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1716 pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1719 case ChangeMaskCompositeOp:
1724 case ClearCompositeOp:
1729 case ColorBurnCompositeOp:
1731 if ((Sca == 0.0) && (Dca == Da))
1733 pixel=QuantumRange*(Sa*Da+Dca*(1.0-Sa));
1738 pixel=QuantumRange*(Dca*(1.0-Sa));
1741 pixel=QuantumRange*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-Dca/Da)*Sa/Sca)+
1742 Sca*(1.0-Da)+Dca*(1.0-Sa));
1745 case ColorDodgeCompositeOp:
1747 if ((Sca == Sa) && (Dca == 0.0))
1749 pixel=QuantumRange*(Sca*(1.0-Da));
1754 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1757 pixel=QuantumRange*(Sa*Da*MagickMin(1.0,Dca/Da*Sa/(Sa-Sca)));
1760 case ColorizeCompositeOp:
1762 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1767 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1772 CompositeHCL(canvas_pixel.red,canvas_pixel.green,
1773 canvas_pixel.blue,&sans,&sans,&luma);
1774 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1775 &hue,&chroma,&sans);
1776 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1779 case RedPixelChannel: pixel=red; break;
1780 case GreenPixelChannel: pixel=green; break;
1781 case BluePixelChannel: pixel=blue; break;
1782 default: pixel=Dc; break;
1786 case CopyAlphaCompositeOp:
1791 case CopyBlackCompositeOp:
1793 if (channel == BlackPixelChannel)
1794 pixel=(MagickRealType) (QuantumRange-
1795 GetPixelBlack(source_image,p));
1798 case CopyBlueCompositeOp:
1799 case CopyYellowCompositeOp:
1801 if (channel == BluePixelChannel)
1802 pixel=(MagickRealType) GetPixelBlue(source_image,p);
1805 case CopyGreenCompositeOp:
1806 case CopyMagentaCompositeOp:
1808 if (channel == GreenPixelChannel)
1809 pixel=(MagickRealType) GetPixelGreen(source_image,p);
1812 case CopyRedCompositeOp:
1813 case CopyCyanCompositeOp:
1815 if (channel == RedPixelChannel)
1816 pixel=(MagickRealType) GetPixelRed(source_image,p);
1819 case DarkenCompositeOp:
1822 Darken is equivalent to a 'Minimum' method
1823 OR a greyscale version of a binary 'Or'
1824 OR the 'Intersection' of pixel sets.
1826 if ((Sca*Da) < (Dca*Sa))
1828 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1831 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1834 case DarkenIntensityCompositeOp:
1836 pixel=Sa*GetPixelIntensity(source_image,p) <
1837 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1840 case DifferenceCompositeOp:
1842 pixel=QuantumRange*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1845 case DissolveCompositeOp:
1847 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1848 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1851 case DivideDstCompositeOp:
1853 if ((fabs((double) Sca) < MagickEpsilon) &&
1854 (fabs((double) Dca) < MagickEpsilon))
1856 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1859 if (fabs((double) Dca) < MagickEpsilon)
1861 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1864 pixel=QuantumRange*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1867 case DivideSrcCompositeOp:
1869 if ((fabs((double) Dca) < MagickEpsilon) &&
1870 (fabs((double) Sca) < MagickEpsilon))
1872 pixel=QuantumRange*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1875 if (fabs((double) Sca) < MagickEpsilon)
1877 pixel=QuantumRange*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1880 pixel=QuantumRange*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1883 case DstAtopCompositeOp:
1885 pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1888 case DstCompositeOp:
1891 pixel=QuantumRange*Dca;
1894 case DstInCompositeOp:
1896 pixel=QuantumRange*(Dca*Sa);
1899 case DstOutCompositeOp:
1901 pixel=QuantumRange*(Dca*(1.0-Sa));
1904 case DstOverCompositeOp:
1906 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1909 case ExclusionCompositeOp:
1911 pixel=QuantumRange*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
1915 case HardLightCompositeOp:
1919 pixel=QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1922 pixel=QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*
1926 case HardMixCompositeOp:
1935 pixel=(gamma*(1.0-Sca)*(1.0-Dca))+Sa*(1.0-Sca)*Dca+Da*(1.0-Dca)*Sca;
1938 case HueCompositeOp:
1940 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1945 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1950 CompositeHCL(canvas_pixel.red,canvas_pixel.green,
1951 canvas_pixel.blue,&hue,&chroma,&luma);
1952 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1954 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1957 case RedPixelChannel: pixel=red; break;
1958 case GreenPixelChannel: pixel=green; break;
1959 case BluePixelChannel: pixel=blue; break;
1960 default: pixel=Dc; break;
1965 case SrcInCompositeOp:
1967 pixel=QuantumRange*(Sca*Da);
1970 case LinearBurnCompositeOp:
1973 LinearBurn: as defined by Abode Photoshop, according to
1974 http://www.simplefilter.de/en/basics/mixmods.html is:
1976 f(Sc,Dc) = Sc + Dc - 1
1978 pixel=QuantumRange*(Sca+Dca-Sa*Da);
1981 case LinearDodgeCompositeOp:
1983 pixel=(Sa*Sc+Da*Dc);
1986 case LinearLightCompositeOp:
1989 LinearLight: as defined by Abode Photoshop, according to
1990 http://www.simplefilter.de/en/basics/mixmods.html is:
1992 f(Sc,Dc) = Dc + 2*Sc - 1
1994 pixel=QuantumRange*((Sca-Sa)*Da+Sca+Dca);
1997 case LightenCompositeOp:
1999 if ((Sca*Da) > (Dca*Sa))
2001 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2004 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2007 case LightenIntensityCompositeOp:
2010 Lighten is equivalent to a 'Maximum' method
2011 OR a greyscale version of a binary 'And'
2012 OR the 'Union' of pixel sets.
2014 pixel=Sa*GetPixelIntensity(source_image,p) >
2015 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2018 case LuminizeCompositeOp:
2020 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2025 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2030 CompositeHCL(canvas_pixel.red,canvas_pixel.green,
2031 canvas_pixel.blue,&hue,&chroma,&luma);
2032 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2034 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2037 case RedPixelChannel: pixel=red; break;
2038 case GreenPixelChannel: pixel=green; break;
2039 case BluePixelChannel: pixel=blue; break;
2040 default: pixel=Dc; break;
2044 case MathematicsCompositeOp:
2047 'Mathematics' a free form user control mathematical composition
2050 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2052 Where the arguments A,B,C,D are (currently) passed to composite
2053 as a command separated 'geometry' string in "compose:args" image
2056 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2058 Applying the SVG transparency formula (see above), we get...
2060 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2062 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2065 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2066 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2067 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2070 case MinusDstCompositeOp:
2072 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2075 case MinusSrcCompositeOp:
2078 Minus source from canvas.
2082 pixel=QuantumRange*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2085 case ModulateCompositeOp:
2090 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2095 offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2101 CompositeHCL(canvas_pixel.red,canvas_pixel.green,
2102 canvas_pixel.blue,&hue,&chroma,&luma);
2103 luma+=(0.01*percent_luma*offset)/midpoint;
2104 chroma*=0.01*percent_chroma;
2105 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2108 case RedPixelChannel: pixel=red; break;
2109 case GreenPixelChannel: pixel=green; break;
2110 case BluePixelChannel: pixel=blue; break;
2111 default: pixel=Dc; break;
2115 case ModulusAddCompositeOp:
2118 if (pixel > QuantumRange)
2119 pixel-=QuantumRange;
2120 pixel=gamma*(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2123 case ModulusSubtractCompositeOp:
2127 pixel+=QuantumRange;
2128 pixel=gamma*(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2131 case MultiplyCompositeOp:
2133 pixel=QuantumRange*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2136 case OutCompositeOp:
2137 case SrcOutCompositeOp:
2139 pixel=QuantumRange*(Sca*(1.0-Da));
2142 case OverCompositeOp:
2143 case SrcOverCompositeOp:
2145 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2148 case OverlayCompositeOp:
2150 if ((2.0*Dca) <= Da)
2152 pixel=QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2155 pixel=QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*
2159 case PegtopLightCompositeOp:
2162 PegTop: A Soft-Light alternative: A continuous version of the
2163 Softlight function, producing very similar results.
2165 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2167 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2169 if (fabs((double) Da) < MagickEpsilon)
2171 pixel=QuantumRange*(Sca);
2174 pixel=QuantumRange*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+
2178 case PinLightCompositeOp:
2181 PinLight: A Photoshop 7 composition method
2182 http://www.simplefilter.de/en/basics/mixmods.html
2184 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2186 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2188 pixel=QuantumRange*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2191 if ((Dca*Sa) > (2.0*Sca*Da))
2193 pixel=QuantumRange*(Sca*Da+Sca+Dca*(1.0-Sa));
2196 pixel=QuantumRange*(Sca*(1.0-Da)+Dca);
2199 case PlusCompositeOp:
2201 pixel=QuantumRange*(Sca+Dca);
2204 case SaturateCompositeOp:
2206 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2211 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2216 CompositeHCL(canvas_pixel.red,canvas_pixel.green,
2217 canvas_pixel.blue,&hue,&chroma,&luma);
2218 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2219 &sans,&chroma,&sans);
2220 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2223 case RedPixelChannel: pixel=red; break;
2224 case GreenPixelChannel: pixel=green; break;
2225 case BluePixelChannel: pixel=blue; break;
2226 default: pixel=Dc; break;
2230 case ScreenCompositeOp:
2233 Screen: a negated multiply:
2235 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2237 pixel=QuantumRange*(Sca+Dca-Sca*Dca);
2240 case SoftLightCompositeOp:
2244 pixel=QuantumRange*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+Sca*
2245 (1.0-Da)+Dca*(1.0-Sa));
2248 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2250 pixel=QuantumRange*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*(4.0*
2251 (Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+Dca*
2255 pixel=QuantumRange*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-(Dca/
2256 Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2259 case ThresholdCompositeOp:
2265 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2270 pixel=gamma*(Dc+delta*amount);
2273 case VividLightCompositeOp:
2276 VividLight: A Photoshop 7 composition method. See
2277 http://www.simplefilter.de/en/basics/mixmods.html.
2279 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2281 if ((fabs((double) Sa) < MagickEpsilon) ||
2282 (fabs((double) (Sca-Sa)) < MagickEpsilon))
2284 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2287 if ((2.0*Sca) <= Sa)
2289 pixel=QuantumRange*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+
2293 pixel=QuantumRange*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*
2297 case XorCompositeOp:
2299 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2308 q[i]=ClampPixel(pixel);
2310 p+=GetPixelChannels(source_image);
2311 channels=GetPixelChannels(source_image);
2312 if (p >= (pixels+channels*source_image->columns))
2314 q+=GetPixelChannels(image);
2316 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2318 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2323 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2324 #pragma omp critical (MagickCore_CompositeImage)
2326 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2328 if (proceed == MagickFalse)
2332 source_view=DestroyCacheView(source_view);
2333 image_view=DestroyCacheView(image_view);
2334 if (canvas_image != (Image * ) NULL)
2335 canvas_image=DestroyImage(canvas_image);
2337 source_image=DestroyImage(source_image);
2342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2346 % T e x t u r e I m a g e %
2350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2352 % TextureImage() repeatedly tiles the texture image across and down the image
2355 % The format of the TextureImage method is:
2357 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2358 % ExceptionInfo *exception)
2360 % A description of each parameter follows:
2362 % o image: the image.
2364 % o texture_image: This image is the texture to layer on the background.
2367 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2368 ExceptionInfo *exception)
2370 #define TextureImageTag "Texture/Image"
2385 assert(image != (Image *) NULL);
2386 if (image->debug != MagickFalse)
2387 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2388 assert(image->signature == MagickSignature);
2389 if (texture == (const Image *) NULL)
2390 return(MagickFalse);
2391 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2392 return(MagickFalse);
2393 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2394 if (texture_image == (const Image *) NULL)
2395 return(MagickFalse);
2396 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2397 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2400 if ((image->compose != CopyCompositeOp) &&
2401 ((image->compose != OverCompositeOp) ||
2402 (image->alpha_trait != UndefinedPixelTrait) ||
2403 (texture_image->alpha_trait != UndefinedPixelTrait)))
2406 Tile texture onto the image background.
2408 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2413 if (status == MagickFalse)
2415 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2420 thread_status=CompositeImage(image,texture_image,image->compose,
2421 MagickFalse,x+texture_image->tile_offset.x,y+
2422 texture_image->tile_offset.y,exception);
2423 if (thread_status == MagickFalse)
2425 status=thread_status;
2429 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2434 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2436 if (proceed == MagickFalse)
2440 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2441 image->rows,image->rows);
2442 texture_image=DestroyImage(texture_image);
2446 Tile texture onto the image background (optimized).
2449 texture_view=AcquireVirtualCacheView(texture_image,exception);
2450 image_view=AcquireAuthenticCacheView(image,exception);
2451 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2452 #pragma omp parallel for schedule(static,4) shared(status) \
2453 magick_threads(texture_image,image,1,1)
2455 for (y=0; y < (ssize_t) image->rows; y++)
2460 register const Quantum
2473 if (status == MagickFalse)
2475 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2476 (y+texture_image->tile_offset.y) % texture_image->rows,
2477 texture_image->columns,1,exception);
2478 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2479 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2484 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2490 width=texture_image->columns;
2491 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2492 width=image->columns-x;
2493 for (j=0; j < (ssize_t) width; j++)
2498 if (GetPixelReadMask(image,q) == 0)
2500 p+=GetPixelChannels(texture_image);
2501 q+=GetPixelChannels(image);
2504 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2506 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2507 PixelTrait traits=GetPixelChannelTraits(image,channel);
2508 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2510 if ((traits == UndefinedPixelTrait) ||
2511 (texture_traits == UndefinedPixelTrait))
2513 SetPixelChannel(image,channel,p[i],q);
2515 p+=GetPixelChannels(texture_image);
2516 q+=GetPixelChannels(image);
2519 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2520 if (sync == MagickFalse)
2522 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2527 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2529 if (proceed == MagickFalse)
2533 texture_view=DestroyCacheView(texture_view);
2534 image_view=DestroyCacheView(image_view);
2535 texture_image=DestroyImage(texture_image);