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)/2.0;
966 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
970 horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
971 vertical_scale=(MagickRealType) (image->rows-1)/2.0;
976 horizontal_scale=geometry_info.rho;
977 vertical_scale=geometry_info.sigma;
978 if ((flags & PercentValue) != 0)
980 if ((flags & AspectValue) == 0)
982 horizontal_scale*=(source_image->columns-1)/200.0;
983 vertical_scale*=(source_image->rows-1)/200.0;
987 horizontal_scale*=(image->columns-1)/200.0;
988 vertical_scale*=(image->rows-1)/200.0;
991 if ((flags & HeightValue) == 0)
992 vertical_scale=horizontal_scale;
995 Determine fixed center point for absolute distortion map
997 Displace offset relative to a fixed absolute point
998 Select that point according to +X+Y user inputs.
999 default = center of overlay image
1000 arg flag '!' = locations/percentage relative to background image
1002 center.x=(MagickRealType) x_offset;
1003 center.y=(MagickRealType) y_offset;
1004 if (compose == DistortCompositeOp)
1006 if ((flags & XValue) == 0)
1007 if ((flags & AspectValue) != 0)
1008 center.x=(MagickRealType) ((image->columns-1)/2.0);
1010 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1013 if ((flags & AspectValue) != 0)
1014 center.x=geometry_info.xi;
1016 center.x=(MagickRealType) (x_offset+geometry_info.xi);
1017 if ((flags & YValue) == 0)
1018 if ((flags & AspectValue) != 0)
1019 center.y=(MagickRealType) ((image->rows-1)/2.0);
1021 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1023 if ((flags & AspectValue) != 0)
1024 center.y=geometry_info.psi;
1026 center.y=(MagickRealType) (y_offset+geometry_info.psi);
1029 Shift the pixel offset point as defined by the provided,
1030 displacement/distortion map. -- Like a lens...
1032 GetPixelInfo(image,&pixel);
1033 image_view=AcquireVirtualCacheView(image,exception);
1034 source_view=AcquireVirtualCacheView(source_image,exception);
1035 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1036 for (y=0; y < (ssize_t) source_image->rows; y++)
1041 register const Quantum
1050 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1052 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1054 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1056 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1058 for (x=0; x < (ssize_t) source_image->columns; x++)
1060 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1062 p+=GetPixelChannels(source_image);
1066 Displace the offset.
1068 offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1069 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1070 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1072 offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1073 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1074 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1076 (void) InterpolatePixelInfo(image,image_view,
1077 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1080 Mask with the 'invalid pixel mask' in alpha channel.
1082 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
1083 (QuantumScale*GetPixelAlpha(source_image,p));
1084 SetPixelViaPixelInfo(canvas_image,&pixel,q);
1085 p+=GetPixelChannels(source_image);
1086 q+=GetPixelChannels(canvas_image);
1088 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1089 if (sync == MagickFalse)
1092 canvas_view=DestroyCacheView(canvas_view);
1093 source_view=DestroyCacheView(source_view);
1094 image_view=DestroyCacheView(image_view);
1095 source_image=DestroyImage(source_image);
1096 source_image=canvas_image;
1099 case DissolveCompositeOp:
1105 Geometry arguments to dissolve factors.
1107 value=GetImageArtifact(image,"compose:args");
1108 if (value != (char *) NULL)
1110 flags=ParseGeometry(value,&geometry_info);
1111 source_dissolve=geometry_info.rho/100.0;
1112 canvas_dissolve=1.0;
1113 if ((source_dissolve-MagickEpsilon) < 0.0)
1114 source_dissolve=0.0;
1115 if ((source_dissolve+MagickEpsilon) > 1.0)
1117 canvas_dissolve=2.0-source_dissolve;
1118 source_dissolve=1.0;
1120 if ((flags & SigmaValue) != 0)
1121 canvas_dissolve=geometry_info.sigma/100.0;
1122 if ((canvas_dissolve-MagickEpsilon) < 0.0)
1123 canvas_dissolve=0.0;
1127 case BlendCompositeOp:
1132 value=GetImageArtifact(image,"compose:args");
1133 if (value != (char *) NULL)
1135 flags=ParseGeometry(value,&geometry_info);
1136 source_dissolve=geometry_info.rho/100.0;
1137 canvas_dissolve=1.0-source_dissolve;
1138 if ((flags & SigmaValue) != 0)
1139 canvas_dissolve=geometry_info.sigma/100.0;
1143 case MathematicsCompositeOp:
1149 Just collect the values from "compose:args", setting.
1150 Unused values are set to zero automagically.
1152 Arguments are normally a comma separated list, so this probably should
1153 be changed to some 'general comma list' parser, (with a minimum
1156 SetGeometryInfo(&geometry_info);
1157 value=GetImageArtifact(image,"compose:args");
1158 if (value != (char *) NULL)
1159 (void) ParseGeometry(value,&geometry_info);
1162 case ModulateCompositeOp:
1168 Determine the luma and chroma scale.
1170 value=GetImageArtifact(image,"compose:args");
1171 if (value != (char *) NULL)
1173 flags=ParseGeometry(value,&geometry_info);
1174 percent_luma=geometry_info.rho;
1175 if ((flags & SigmaValue) != 0)
1176 percent_chroma=geometry_info.sigma;
1180 case ThresholdCompositeOp:
1186 Determine the amount and threshold.
1188 value=GetImageArtifact(image,"compose:args");
1189 if (value != (char *) NULL)
1191 flags=ParseGeometry(value,&geometry_info);
1192 amount=geometry_info.rho;
1193 threshold=geometry_info.sigma;
1194 if ((flags & SigmaValue) == 0)
1197 threshold*=QuantumRange;
1208 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1209 source_view=AcquireVirtualCacheView(source_image,exception);
1210 image_view=AcquireAuthenticCacheView(image,exception);
1211 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1212 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1213 magick_threads(source_image,image,image->rows,1)
1215 for (y=0; y < (ssize_t) image->rows; y++)
1232 register const Quantum
1241 if (status == MagickFalse)
1243 if (clip_to_self != MagickFalse)
1247 if ((y-y_offset) >= (ssize_t) source_image->rows)
1251 If pixels is NULL, y is outside overlay region.
1253 pixels=(Quantum *) NULL;
1255 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1257 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1258 source_image->columns,1,exception);
1259 if (p == (const Quantum *) NULL)
1266 p-=x_offset*GetPixelChannels(source_image);
1268 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1269 if (q == (Quantum *) NULL)
1277 GetPixelInfo(image,&canvas_pixel);
1278 GetPixelInfo(source_image,&source_pixel);
1279 for (x=0; x < (ssize_t) image->columns; x++)
1299 if (clip_to_self != MagickFalse)
1303 q+=GetPixelChannels(image);
1306 if ((x-x_offset) >= (ssize_t) source_image->columns)
1309 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1310 ((x-x_offset) >= (ssize_t) source_image->columns))
1313 source[MaxPixelChannels];
1320 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,
1322 if (GetPixelReadMask(image,q) == 0)
1324 q+=GetPixelChannels(image);
1327 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1332 PixelChannel channel=GetPixelChannelChannel(image,i);
1333 PixelTrait traits=GetPixelChannelTraits(image,channel);
1334 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1336 if ((traits == UndefinedPixelTrait) ||
1337 (source_traits == UndefinedPixelTrait))
1341 case AlphaCompositeOp:
1342 case ChangeMaskCompositeOp:
1343 case CopyAlphaCompositeOp:
1344 case DstAtopCompositeOp:
1345 case DstInCompositeOp:
1347 case OutCompositeOp:
1348 case SrcInCompositeOp:
1349 case SrcOutCompositeOp:
1351 if (channel == AlphaPixelChannel)
1352 pixel=(MagickRealType) TransparentAlpha;
1354 pixel=(MagickRealType) q[i];
1357 case ClearCompositeOp:
1358 case CopyCompositeOp:
1359 case ReplaceCompositeOp:
1360 case SrcCompositeOp:
1362 if (channel == AlphaPixelChannel)
1363 pixel=(MagickRealType) TransparentAlpha;
1368 case BlendCompositeOp:
1369 case DissolveCompositeOp:
1371 if (channel == AlphaPixelChannel)
1372 pixel=canvas_dissolve*GetPixelAlpha(source_image,
1375 pixel=(MagickRealType) source[channel];
1380 pixel=(MagickRealType) source[channel];
1384 q[i]=ClampToQuantum(pixel);
1386 q+=GetPixelChannels(image);
1390 Authentic composite:
1391 Sa: normalized source alpha.
1392 Da: normalized canvas alpha.
1394 Sa=QuantumScale*GetPixelAlpha(source_image,p);
1395 Da=QuantumScale*GetPixelAlpha(image,q);
1398 case BumpmapCompositeOp:
1400 alpha=GetPixelIntensity(source_image,p)*Sa;
1403 case ColorBurnCompositeOp:
1404 case ColorDodgeCompositeOp:
1405 case DarkenCompositeOp:
1406 case DifferenceCompositeOp:
1407 case DivideDstCompositeOp:
1408 case DivideSrcCompositeOp:
1409 case ExclusionCompositeOp:
1410 case HardLightCompositeOp:
1411 case HardMixCompositeOp:
1412 case LinearBurnCompositeOp:
1413 case LinearDodgeCompositeOp:
1414 case LinearLightCompositeOp:
1415 case LightenCompositeOp:
1416 case MathematicsCompositeOp:
1417 case MinusDstCompositeOp:
1418 case MinusSrcCompositeOp:
1419 case ModulusAddCompositeOp:
1420 case ModulusSubtractCompositeOp:
1421 case MultiplyCompositeOp:
1422 case OverlayCompositeOp:
1423 case PegtopLightCompositeOp:
1424 case PinLightCompositeOp:
1425 case ScreenCompositeOp:
1426 case SoftLightCompositeOp:
1427 case VividLightCompositeOp:
1429 alpha=RoundToUnity(Sa+Da-Sa*Da);
1432 case DstAtopCompositeOp:
1433 case DstInCompositeOp:
1435 case SrcInCompositeOp:
1440 case DissolveCompositeOp:
1442 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*
1443 Sa+canvas_dissolve*Da;
1446 case DstOverCompositeOp:
1451 case DstOutCompositeOp:
1456 case OutCompositeOp:
1457 case SrcOutCompositeOp:
1462 case OverCompositeOp:
1463 case SrcOverCompositeOp:
1468 case BlendCompositeOp:
1469 case PlusCompositeOp:
1471 alpha=RoundToUnity(Sa+Da);
1474 case XorCompositeOp:
1476 alpha=Sa+Da-2.0*Sa*Da;
1485 if (GetPixelReadMask(image,q) == 0)
1487 p+=GetPixelChannels(source_image);
1488 q+=GetPixelChannels(image);
1493 case ColorizeCompositeOp:
1494 case HueCompositeOp:
1495 case LuminizeCompositeOp:
1496 case ModulateCompositeOp:
1497 case SaturateCompositeOp:
1499 GetPixelInfoPixel(source_image,p,&source_pixel);
1500 GetPixelInfoPixel(image,q,&canvas_pixel);
1506 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1512 PixelChannel channel=GetPixelChannelChannel(image,i);
1513 PixelTrait traits=GetPixelChannelTraits(image,channel);
1514 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1516 if (traits == UndefinedPixelTrait)
1518 if ((source_traits == UndefinedPixelTrait) &&
1519 (((compose != CopyAlphaCompositeOp) &&
1520 (compose != ChangeMaskCompositeOp)) ||
1521 (channel != AlphaPixelChannel)))
1527 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1528 Dc=(MagickRealType) q[i];
1529 if ((traits & CopyPixelTrait) != 0)
1534 q[i]=ClampToQuantum(Sc);
1537 if (channel == AlphaPixelChannel)
1544 case AlphaCompositeOp:
1546 pixel=QuantumRange*Sa;
1549 case AtopCompositeOp:
1550 case CopyBlackCompositeOp:
1551 case CopyBlueCompositeOp:
1552 case CopyCyanCompositeOp:
1553 case CopyGreenCompositeOp:
1554 case CopyMagentaCompositeOp:
1555 case CopyRedCompositeOp:
1556 case CopyYellowCompositeOp:
1557 case SrcAtopCompositeOp:
1558 case DstCompositeOp:
1561 pixel=QuantumRange*Da;
1564 case ChangeMaskCompositeOp:
1569 if (Da > ((MagickRealType) QuantumRange/2.0))
1571 pixel=(MagickRealType) TransparentAlpha;
1574 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1575 if (equivalent != MagickFalse)
1576 pixel=(MagickRealType) TransparentAlpha;
1578 pixel=(MagickRealType) OpaqueAlpha;
1581 case ClearCompositeOp:
1583 pixel=(MagickRealType) TransparentAlpha;
1586 case ColorizeCompositeOp:
1587 case HueCompositeOp:
1588 case LuminizeCompositeOp:
1589 case SaturateCompositeOp:
1591 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1593 pixel=QuantumRange*Da;
1596 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1598 pixel=QuantumRange*Sa;
1603 pixel=QuantumRange*Da;
1606 pixel=QuantumRange*Sa;
1609 case CopyAlphaCompositeOp:
1611 if (source_traits == UndefinedPixelTrait)
1612 pixel=GetPixelIntensity(source_image,p);
1614 pixel=QuantumRange*Sa;
1617 case CopyCompositeOp:
1618 case DisplaceCompositeOp:
1619 case DistortCompositeOp:
1620 case DstAtopCompositeOp:
1621 case ReplaceCompositeOp:
1622 case SrcCompositeOp:
1624 pixel=QuantumRange*Sa;
1627 case DarkenIntensityCompositeOp:
1629 pixel=Sa*GetPixelIntensity(source_image,p) <
1630 Da*GetPixelIntensity(image,q) ? Sa : Da;
1633 case LightenIntensityCompositeOp:
1635 pixel=Sa*GetPixelIntensity(source_image,p) >
1636 Da*GetPixelIntensity(image,q) ? Sa : Da;
1639 case ModulateCompositeOp:
1641 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1643 pixel=QuantumRange*Da;
1646 pixel=QuantumRange*Da;
1651 pixel=QuantumRange*alpha;
1655 q[i]=ClampToQuantum(pixel);
1659 Porter-Duff compositions:
1660 Sca: source normalized color multiplied by alpha.
1661 Dca: normalized canvas color multiplied by alpha.
1663 Sca=QuantumScale*Sa*Sc;
1664 Dca=QuantumScale*Da*Dc;
1667 case DarkenCompositeOp:
1668 case LightenCompositeOp:
1669 case ModulusSubtractCompositeOp:
1677 gamma=PerceptibleReciprocal(alpha);
1681 case AlphaCompositeOp:
1683 pixel=QuantumRange*Sa;
1686 case AtopCompositeOp:
1687 case SrcAtopCompositeOp:
1689 pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1692 case BlendCompositeOp:
1694 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1697 case BlurCompositeOp:
1698 case CopyCompositeOp:
1699 case ReplaceCompositeOp:
1700 case SrcCompositeOp:
1702 pixel=QuantumRange*Sca;
1705 case DisplaceCompositeOp:
1706 case DistortCompositeOp:
1711 case BumpmapCompositeOp:
1713 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1718 pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1721 case ChangeMaskCompositeOp:
1726 case ClearCompositeOp:
1731 case ColorBurnCompositeOp:
1733 if ((Sca == 0.0) && (Dca == Da))
1735 pixel=QuantumRange*(Sa*Da+Dca*(1.0-Sa));
1740 pixel=QuantumRange*(Dca*(1.0-Sa));
1743 pixel=QuantumRange*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-Dca/Da)*Sa/Sca)+
1744 Sca*(1.0-Da)+Dca*(1.0-Sa));
1747 case ColorDodgeCompositeOp:
1749 if ((Sca == Sa) && (Dca == 0.0))
1751 pixel=QuantumRange*(Sca*(1.0-Da));
1756 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1759 pixel=QuantumRange*(Sa*Da*MagickMin(1.0,Dca/Da*Sa/(Sa-Sca)));
1762 case ColorizeCompositeOp:
1764 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1769 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1774 CompositeHCL(canvas_pixel.red,canvas_pixel.green,
1775 canvas_pixel.blue,&sans,&sans,&luma);
1776 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1777 &hue,&chroma,&sans);
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 CopyAlphaCompositeOp:
1793 case CopyBlackCompositeOp:
1795 if (channel == BlackPixelChannel)
1796 pixel=(MagickRealType) (QuantumRange-
1797 GetPixelBlack(source_image,p));
1800 case CopyBlueCompositeOp:
1801 case CopyYellowCompositeOp:
1803 if (channel == BluePixelChannel)
1804 pixel=(MagickRealType) GetPixelBlue(source_image,p);
1807 case CopyGreenCompositeOp:
1808 case CopyMagentaCompositeOp:
1810 if (channel == GreenPixelChannel)
1811 pixel=(MagickRealType) GetPixelGreen(source_image,p);
1814 case CopyRedCompositeOp:
1815 case CopyCyanCompositeOp:
1817 if (channel == RedPixelChannel)
1818 pixel=(MagickRealType) GetPixelRed(source_image,p);
1821 case DarkenCompositeOp:
1824 Darken is equivalent to a 'Minimum' method
1825 OR a greyscale version of a binary 'Or'
1826 OR the 'Intersection' of pixel sets.
1828 if ((Sca*Da) < (Dca*Sa))
1830 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1833 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1836 case DarkenIntensityCompositeOp:
1838 pixel=Sa*GetPixelIntensity(source_image,p) <
1839 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1842 case DifferenceCompositeOp:
1844 pixel=QuantumRange*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1847 case DissolveCompositeOp:
1849 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1850 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1853 case DivideDstCompositeOp:
1855 if ((fabs((double) Sca) < MagickEpsilon) &&
1856 (fabs((double) Dca) < MagickEpsilon))
1858 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1861 if (fabs((double) Dca) < MagickEpsilon)
1863 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1866 pixel=QuantumRange*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1869 case DivideSrcCompositeOp:
1871 if ((fabs((double) Dca) < MagickEpsilon) &&
1872 (fabs((double) Sca) < MagickEpsilon))
1874 pixel=QuantumRange*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1877 if (fabs((double) Sca) < MagickEpsilon)
1879 pixel=QuantumRange*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1882 pixel=QuantumRange*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1885 case DstAtopCompositeOp:
1887 pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1890 case DstCompositeOp:
1893 pixel=QuantumRange*Dca;
1896 case DstInCompositeOp:
1898 pixel=QuantumRange*(Dca*Sa);
1901 case DstOutCompositeOp:
1903 pixel=QuantumRange*(Dca*(1.0-Sa));
1906 case DstOverCompositeOp:
1908 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1911 case ExclusionCompositeOp:
1913 pixel=QuantumRange*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
1917 case HardLightCompositeOp:
1921 pixel=QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1924 pixel=QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*
1928 case HardMixCompositeOp:
1937 pixel=(gamma*(1.0-Sca)*(1.0-Dca))+Sa*(1.0-Sca)*Dca+Da*(1.0-Dca)*Sca;
1940 case HueCompositeOp:
1942 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1947 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1952 CompositeHCL(canvas_pixel.red,canvas_pixel.green,
1953 canvas_pixel.blue,&hue,&chroma,&luma);
1954 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1956 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1959 case RedPixelChannel: pixel=red; break;
1960 case GreenPixelChannel: pixel=green; break;
1961 case BluePixelChannel: pixel=blue; break;
1962 default: pixel=Dc; break;
1967 case SrcInCompositeOp:
1969 pixel=QuantumRange*(Sca*Da);
1972 case LinearBurnCompositeOp:
1975 LinearBurn: as defined by Abode Photoshop, according to
1976 http://www.simplefilter.de/en/basics/mixmods.html is:
1978 f(Sc,Dc) = Sc + Dc - 1
1980 pixel=QuantumRange*(Sca+Dca-Sa*Da);
1983 case LinearDodgeCompositeOp:
1985 pixel=(Sa*Sc+Da*Dc);
1988 case LinearLightCompositeOp:
1991 LinearLight: as defined by Abode Photoshop, according to
1992 http://www.simplefilter.de/en/basics/mixmods.html is:
1994 f(Sc,Dc) = Dc + 2*Sc - 1
1996 pixel=QuantumRange*((Sca-Sa)*Da+Sca+Dca);
1999 case LightenCompositeOp:
2001 if ((Sca*Da) > (Dca*Sa))
2003 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2006 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2009 case LightenIntensityCompositeOp:
2012 Lighten is equivalent to a 'Maximum' method
2013 OR a greyscale version of a binary 'And'
2014 OR the 'Union' of pixel sets.
2016 pixel=Sa*GetPixelIntensity(source_image,p) >
2017 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2020 case LuminizeCompositeOp:
2022 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2027 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2032 CompositeHCL(canvas_pixel.red,canvas_pixel.green,
2033 canvas_pixel.blue,&hue,&chroma,&luma);
2034 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2036 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2039 case RedPixelChannel: pixel=red; break;
2040 case GreenPixelChannel: pixel=green; break;
2041 case BluePixelChannel: pixel=blue; break;
2042 default: pixel=Dc; break;
2046 case MathematicsCompositeOp:
2049 'Mathematics' a free form user control mathematical composition
2052 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2054 Where the arguments A,B,C,D are (currently) passed to composite
2055 as a command separated 'geometry' string in "compose:args" image
2058 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2060 Applying the SVG transparency formula (see above), we get...
2062 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2064 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2067 pixel=gamma*QuantumRange*(geometry_info.rho*Sca*Dca+
2068 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2069 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2072 case MinusDstCompositeOp:
2074 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2077 case MinusSrcCompositeOp:
2080 Minus source from canvas.
2084 pixel=QuantumRange*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2087 case ModulateCompositeOp:
2092 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2097 offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2103 CompositeHCL(canvas_pixel.red,canvas_pixel.green,
2104 canvas_pixel.blue,&hue,&chroma,&luma);
2105 luma+=(0.01*percent_luma*offset)/midpoint;
2106 chroma*=0.01*percent_chroma;
2107 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2110 case RedPixelChannel: pixel=red; break;
2111 case GreenPixelChannel: pixel=green; break;
2112 case BluePixelChannel: pixel=blue; break;
2113 default: pixel=Dc; break;
2117 case ModulusAddCompositeOp:
2120 if (pixel > QuantumRange)
2121 pixel-=QuantumRange;
2122 pixel=gamma*(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2125 case ModulusSubtractCompositeOp:
2129 pixel+=QuantumRange;
2130 pixel=gamma*(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2133 case MultiplyCompositeOp:
2135 pixel=QuantumRange*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2138 case OutCompositeOp:
2139 case SrcOutCompositeOp:
2141 pixel=QuantumRange*(Sca*(1.0-Da));
2144 case OverCompositeOp:
2145 case SrcOverCompositeOp:
2147 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2150 case OverlayCompositeOp:
2152 if ((2.0*Dca) <= Da)
2154 pixel=QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2157 pixel=QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*
2161 case PegtopLightCompositeOp:
2164 PegTop: A Soft-Light alternative: A continuous version of the
2165 Softlight function, producing very similar results.
2167 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2169 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2171 if (fabs((double) Da) < MagickEpsilon)
2173 pixel=QuantumRange*(Sca);
2176 pixel=QuantumRange*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+
2180 case PinLightCompositeOp:
2183 PinLight: A Photoshop 7 composition method
2184 http://www.simplefilter.de/en/basics/mixmods.html
2186 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2188 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2190 pixel=QuantumRange*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2193 if ((Dca*Sa) > (2.0*Sca*Da))
2195 pixel=QuantumRange*(Sca*Da+Sca+Dca*(1.0-Sa));
2198 pixel=QuantumRange*(Sca*(1.0-Da)+Dca);
2201 case PlusCompositeOp:
2203 pixel=QuantumRange*(Sca+Dca);
2206 case SaturateCompositeOp:
2208 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2213 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2218 CompositeHCL(canvas_pixel.red,canvas_pixel.green,
2219 canvas_pixel.blue,&hue,&chroma,&luma);
2220 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2221 &sans,&chroma,&sans);
2222 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2225 case RedPixelChannel: pixel=red; break;
2226 case GreenPixelChannel: pixel=green; break;
2227 case BluePixelChannel: pixel=blue; break;
2228 default: pixel=Dc; break;
2232 case ScreenCompositeOp:
2235 Screen: a negated multiply:
2237 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2239 pixel=QuantumRange*(Sca+Dca-Sca*Dca);
2242 case SoftLightCompositeOp:
2246 pixel=QuantumRange*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+Sca*
2247 (1.0-Da)+Dca*(1.0-Sa));
2250 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2252 pixel=QuantumRange*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*(4.0*
2253 (Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+Dca*
2257 pixel=QuantumRange*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-(Dca/
2258 Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2261 case ThresholdCompositeOp:
2267 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2272 pixel=gamma*(Dc+delta*amount);
2275 case VividLightCompositeOp:
2278 VividLight: A Photoshop 7 composition method. See
2279 http://www.simplefilter.de/en/basics/mixmods.html.
2281 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2283 if ((fabs((double) Sa) < MagickEpsilon) ||
2284 (fabs((double) (Sca-Sa)) < MagickEpsilon))
2286 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2289 if ((2.0*Sca) <= Sa)
2291 pixel=QuantumRange*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+
2295 pixel=QuantumRange*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*
2299 case XorCompositeOp:
2301 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2310 q[i]=ClampPixel(pixel);
2312 p+=GetPixelChannels(source_image);
2313 channels=GetPixelChannels(source_image);
2314 if (p >= (pixels+channels*source_image->columns))
2316 q+=GetPixelChannels(image);
2318 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2320 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2325 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2326 #pragma omp critical (MagickCore_CompositeImage)
2328 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2330 if (proceed == MagickFalse)
2334 source_view=DestroyCacheView(source_view);
2335 image_view=DestroyCacheView(image_view);
2336 if (canvas_image != (Image * ) NULL)
2337 canvas_image=DestroyImage(canvas_image);
2339 source_image=DestroyImage(source_image);
2344 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2348 % T e x t u r e I m a g e %
2352 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2354 % TextureImage() repeatedly tiles the texture image across and down the image
2357 % The format of the TextureImage method is:
2359 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2360 % ExceptionInfo *exception)
2362 % A description of each parameter follows:
2364 % o image: the image.
2366 % o texture_image: This image is the texture to layer on the background.
2369 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2370 ExceptionInfo *exception)
2372 #define TextureImageTag "Texture/Image"
2387 assert(image != (Image *) NULL);
2388 if (image->debug != MagickFalse)
2389 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2390 assert(image->signature == MagickSignature);
2391 if (texture == (const Image *) NULL)
2392 return(MagickFalse);
2393 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2394 return(MagickFalse);
2395 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2396 if (texture_image == (const Image *) NULL)
2397 return(MagickFalse);
2398 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2399 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2402 if ((image->compose != CopyCompositeOp) &&
2403 ((image->compose != OverCompositeOp) ||
2404 (image->alpha_trait != UndefinedPixelTrait) ||
2405 (texture_image->alpha_trait != UndefinedPixelTrait)))
2408 Tile texture onto the image background.
2410 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2415 if (status == MagickFalse)
2417 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2422 thread_status=CompositeImage(image,texture_image,image->compose,
2423 MagickFalse,x+texture_image->tile_offset.x,y+
2424 texture_image->tile_offset.y,exception);
2425 if (thread_status == MagickFalse)
2427 status=thread_status;
2431 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2436 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2438 if (proceed == MagickFalse)
2442 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2443 image->rows,image->rows);
2444 texture_image=DestroyImage(texture_image);
2448 Tile texture onto the image background (optimized).
2451 texture_view=AcquireVirtualCacheView(texture_image,exception);
2452 image_view=AcquireAuthenticCacheView(image,exception);
2453 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2454 #pragma omp parallel for schedule(static,4) shared(status) \
2455 magick_threads(texture_image,image,1,1)
2457 for (y=0; y < (ssize_t) image->rows; y++)
2462 register const Quantum
2475 if (status == MagickFalse)
2477 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2478 (y+texture_image->tile_offset.y) % texture_image->rows,
2479 texture_image->columns,1,exception);
2480 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2481 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2486 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2492 width=texture_image->columns;
2493 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2494 width=image->columns-x;
2495 for (j=0; j < (ssize_t) width; j++)
2500 if (GetPixelReadMask(image,q) == 0)
2502 p+=GetPixelChannels(texture_image);
2503 q+=GetPixelChannels(image);
2506 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2508 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2509 PixelTrait traits=GetPixelChannelTraits(image,channel);
2510 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2512 if ((traits == UndefinedPixelTrait) ||
2513 (texture_traits == UndefinedPixelTrait))
2515 SetPixelChannel(image,channel,p[i],q);
2517 p+=GetPixelChannels(texture_image);
2518 q+=GetPixelChannels(image);
2521 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2522 if (sync == MagickFalse)
2524 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2529 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2531 if (proceed == MagickFalse)
2535 texture_view=DestroyCacheView(texture_view);
2536 image_view=DestroyCacheView(image_view);
2537 texture_image=DestroyImage(texture_image);