2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7 % C O O MM MM P P O O SS I T E %
8 % C O O M M M PPPP O O SSS I T EEE %
9 % C O O M M P O O SS I T E %
10 % CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
13 % MagickCore Image Composite Methods %
20 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-private.h"
47 #include "MagickCore/cache-view.h"
48 #include "MagickCore/client.h"
49 #include "MagickCore/color.h"
50 #include "MagickCore/color-private.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/colorspace-private.h"
53 #include "MagickCore/composite.h"
54 #include "MagickCore/composite-private.h"
55 #include "MagickCore/constitute.h"
56 #include "MagickCore/draw.h"
57 #include "MagickCore/fx.h"
58 #include "MagickCore/gem.h"
59 #include "MagickCore/geometry.h"
60 #include "MagickCore/image.h"
61 #include "MagickCore/image-private.h"
62 #include "MagickCore/list.h"
63 #include "MagickCore/log.h"
64 #include "MagickCore/monitor.h"
65 #include "MagickCore/monitor-private.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/option.h"
68 #include "MagickCore/pixel-accessor.h"
69 #include "MagickCore/property.h"
70 #include "MagickCore/quantum.h"
71 #include "MagickCore/resample.h"
72 #include "MagickCore/resource_.h"
73 #include "MagickCore/string_.h"
74 #include "MagickCore/thread-private.h"
75 #include "MagickCore/utility.h"
76 #include "MagickCore/utility-private.h"
77 #include "MagickCore/version.h"
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 % C o m p o s i t e I m a g e %
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 % CompositeImage() returns the second image composited onto the first
91 % at the specified offset, using the specified composite method.
93 % The format of the CompositeImage method is:
95 % MagickBooleanType CompositeImage(Image *image,
96 % const CompositeOperator compose,Image *composite_image,
97 % const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
99 % A description of each parameter follows:
101 % o image: the destination image, modified by he composition
103 % o compose: This operator affects how the composite is applied to
104 % the image. The operators and how they are utilized are listed here
105 % http://www.w3.org/TR/SVG12/#compositing.
107 % o composite_image: the composite (source) image.
109 % o x_offset: the column offset of the composited image.
111 % o y_offset: the row offset of the composited image.
113 % Extra Controls from Image meta-data in 'composite_image' (artifacts)
116 % A string containing extra numerical arguments for specific compose
117 % methods, generally expressed as a 'geometry' or a comma separated list
120 % Compose methods needing such arguments include "BlendCompositeOp" and
121 % "DisplaceCompositeOp".
123 % o "compose:outside-overlay"
124 % Modify how the composition is to effect areas not directly covered
125 % by the 'composite_image' at the offset given. Normally this is
126 % dependant on the 'compose' method, especially Duff-Porter methods.
128 % If set to "false" then disable all normal handling of pixels not
129 % covered by the composite_image. Typically used for repeated tiling
130 % of the composite_image by the calling API.
132 % Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
134 % o exception: return any errors or warnings in this structure.
138 static void CompositeHSB(const Quantum red,const Quantum green,
139 const Quantum blue,double *hue,double *saturation,double *brightness)
149 Convert RGB to HSB colorspace.
151 assert(hue != (double *) NULL);
152 assert(saturation != (double *) NULL);
153 assert(brightness != (double *) NULL);
154 max=(red > green ? red : green);
157 min=(red < green ? red : green);
162 *brightness=(double) (QuantumScale*max);
163 if (fabs((double) max) < MagickEpsilon)
165 *saturation=(double) (1.0-min/max);
166 delta=(MagickRealType) max-min;
167 if (fabs(delta) < MagickEpsilon)
169 if (fabs((double) red-max) < MagickEpsilon)
170 *hue=(double) ((green-blue)/delta);
172 if (fabs((double) green-max) < MagickEpsilon)
173 *hue=(double) (2.0+(blue-red)/delta);
175 if (fabs((double) blue-max) < MagickEpsilon)
176 *hue=(double) (4.0+(red-green)/delta);
182 static void HSBComposite(const double hue,const double saturation,
183 const double brightness,double *red,double *green,double *blue)
193 Convert HSB to RGB colorspace.
195 assert(red != (double *) NULL);
196 assert(green != (double *) NULL);
197 assert(blue != (double *) NULL);
198 if (saturation == 0.0)
200 *red=(double) QuantumRange*brightness;
205 h=6.0*(hue-floor(hue));
206 f=h-floor((double) h);
207 p=brightness*(1.0-saturation);
208 q=brightness*(1.0-saturation*f);
209 t=brightness*(1.0-saturation*(1.0-f));
215 *red=(double) QuantumRange*brightness;
216 *green=(double) QuantumRange*t;
217 *blue=(double) QuantumRange*p;
222 *red=(double) QuantumRange*q;
223 *green=(double) QuantumRange*brightness;
224 *blue=(double) QuantumRange*p;
229 *red=(double) QuantumRange*p;
230 *green=(double) QuantumRange*brightness;
231 *blue=(double) QuantumRange*t;
236 *red=(double) QuantumRange*p;
237 *green=(double) QuantumRange*q;
238 *blue=(double) QuantumRange*brightness;
243 *red=(double) QuantumRange*t;
244 *green=(double) QuantumRange*p;
245 *blue=(double) QuantumRange*brightness;
250 *red=(double) QuantumRange*brightness;
251 *green=(double) QuantumRange*p;
252 *blue=(double) QuantumRange*q;
258 static inline double MagickMin(const double x,const double y)
264 static inline double MagickMax(const double x,const double y)
271 static MagickBooleanType CompositeOverImage(Image *image,
272 const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset,
273 ExceptionInfo *exception)
275 #define CompositeImageTag "Composite/Image"
285 modify_outside_overlay,
298 Prepare composite image.
300 modify_outside_overlay=MagickFalse;
301 value=GetImageArtifact(composite_image,"compose:outside-overlay");
302 if (value != (const char *) NULL)
303 modify_outside_overlay=IsMagickTrue(value);
309 image_view=AcquireCacheView(image);
310 composite_view=AcquireCacheView(composite_image);
311 #if defined(MAGICKCORE_OPENMP_SUPPORT)
312 #pragma omp parallel for schedule(static,4) shared(progress,status)
314 for (y=0; y < (ssize_t) image->rows; y++)
319 register const Quantum
328 if (status == MagickFalse)
330 if (modify_outside_overlay == MagickFalse)
334 if ((y-y_offset) >= (ssize_t) composite_image->rows)
338 If pixels is NULL, y is outside overlay region.
340 pixels=(Quantum *) NULL;
342 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
344 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
345 composite_image->columns,1,exception);
346 if (p == (const Quantum *) NULL)
353 p-=x_offset*GetPixelChannels(composite_image);
355 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
356 if (q == (Quantum *) NULL)
361 for (x=0; x < (ssize_t) image->columns; x++)
374 if (modify_outside_overlay == MagickFalse)
378 q+=GetPixelChannels(image);
381 if ((x-x_offset) >= (ssize_t) composite_image->columns)
384 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
385 ((x-x_offset) >= (ssize_t) composite_image->columns))
388 source[MaxPixelChannels];
393 Dc: destination color.
395 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
397 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
406 channel=GetPixelChannelMapChannel(image,i);
407 traits=GetPixelChannelMapTraits(image,channel);
408 composite_traits=GetPixelChannelMapTraits(composite_image,
410 if ((traits == UndefinedPixelTrait) ||
411 (composite_traits == UndefinedPixelTrait))
413 q[i]=source[channel];
415 q+=GetPixelChannels(image);
420 Sa: normalized source alpha.
421 Da: normalized destination alpha.
423 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
424 Da=QuantumScale*GetPixelAlpha(image,q);
425 alpha=Sa*(-Da)+Sa+Da;
426 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
435 channel=GetPixelChannelMapChannel(image,i);
436 traits=GetPixelChannelMapTraits(image,channel);
437 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
438 if ((traits == UndefinedPixelTrait) ||
439 (composite_traits == UndefinedPixelTrait))
441 if ((traits & CopyPixelTrait) != 0)
443 if (channel != AlphaPixelChannel)
448 q[i]=GetPixelChannel(composite_image,channel,p);
454 q[i]=ClampToQuantum(QuantumRange*alpha);
459 Dc: destination color.
461 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
462 Dc=(MagickRealType) q[i];
463 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
464 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
466 p+=GetPixelChannels(composite_image);
467 channels=GetPixelChannels(composite_image);
468 if (p >= (pixels+channels*composite_image->columns))
470 q+=GetPixelChannels(image);
472 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
474 if (image->progress_monitor != (MagickProgressMonitor) NULL)
479 #if defined(MAGICKCORE_OPENMP_SUPPORT)
480 #pragma omp critical (MagickCore_CompositeImage)
482 proceed=SetImageProgress(image,CompositeImageTag,progress++,
484 if (proceed == MagickFalse)
488 composite_view=DestroyCacheView(composite_view);
489 image_view=DestroyCacheView(image_view);
493 MagickExport MagickBooleanType CompositeImage(Image *image,
494 const CompositeOperator compose,const Image *composite_image,
495 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
497 #define CompositeImageTag "Composite/Image"
516 modify_outside_overlay,
524 destination_dissolve,
541 Composition based on the SVG specification:
543 A Composition is defined by...
544 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
545 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
546 Y = 1 for source preserved
547 Z = 1 for destination preserved
549 Conversion to transparency (then optimized)
550 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
551 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
554 Sca = Sc*Sa normalized Source color divided by Source alpha
555 Dca = Dc*Da normalized Dest color divided by Dest alpha
556 Dc' = Dca'/Da' the desired color value for this channel.
558 Da' in in the follow formula as 'gamma' The resulting alpla value.
560 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
561 the following optimizations...
563 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
564 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
566 The above SVG definitions also definate that Mathematical Composition
567 methods should use a 'Over' blending mode for Alpha Channel.
568 It however was not applied for composition modes of 'Plus', 'Minus',
569 the modulus versions of 'Add' and 'Subtract'.
571 Mathematical operator changes to be applied from IM v6.7...
573 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
574 'ModulusAdd' and 'ModulusSubtract' for clarity.
576 2) All mathematical compositions work as per the SVG specification
577 with regard to blending. This now includes 'ModulusAdd' and
580 3) When the special channel flag 'sync' (syncronize channel updates)
581 is turned off (enabled by default) then mathematical compositions are
582 only performed on the channels specified, and are applied
583 independantally of each other. In other words the mathematics is
584 performed as 'pure' mathematical operations, rather than as image
588 assert(image != (Image *) NULL);
589 assert(image->signature == MagickSignature);
590 if (image->debug != MagickFalse)
591 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
592 assert(composite_image != (Image *) NULL);
593 assert(composite_image->signature == MagickSignature);
594 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
596 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
598 status=CompositeOverImage(image,composite_image,x_offset,y_offset,
602 destination_image=(Image *) NULL;
604 destination_dissolve=1.0;
605 modify_outside_overlay=MagickFalse;
606 percent_brightness=100.0;
607 percent_saturation=100.0;
612 case ClearCompositeOp:
613 case DstAtopCompositeOp:
614 case DstInCompositeOp:
618 case SrcInCompositeOp:
619 case SrcOutCompositeOp:
622 Modify destination outside the overlaid region.
624 modify_outside_overlay=MagickTrue;
627 case CopyCompositeOp:
629 if ((x_offset < 0) || (y_offset < 0))
631 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
633 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
636 image_view=AcquireCacheView(image);
637 composite_view=AcquireCacheView(composite_image);
638 #if defined(MAGICKCORE_OPENMP_SUPPORT)
639 #pragma omp parallel for schedule(static,4) shared(status)
641 for (y=0; y < (ssize_t) composite_image->rows; y++)
646 register const Quantum
655 if (status == MagickFalse)
657 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
659 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
660 composite_image->columns,1,exception);
661 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
666 for (x=0; x < (ssize_t) composite_image->columns; x++)
671 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
680 channel=GetPixelChannelMapChannel(composite_image,i);
681 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
682 traits=GetPixelChannelMapTraits(image,channel);
683 if ((traits == UndefinedPixelTrait) ||
684 (composite_traits == UndefinedPixelTrait))
686 SetPixelChannel(image,channel,p[i],q);
688 p+=GetPixelChannels(composite_image);
689 q+=GetPixelChannels(image);
691 sync=SyncCacheViewAuthenticPixels(image_view,exception);
692 if (sync == MagickFalse)
694 if (image->progress_monitor != (MagickProgressMonitor) NULL)
699 #if defined(MAGICKCORE_OPENMP_SUPPORT)
700 #pragma omp critical (MagickCore_CompositeImage)
702 proceed=SetImageProgress(image,CompositeImageTag,
703 (MagickOffsetType) y,image->rows);
704 if (proceed == MagickFalse)
708 composite_view=DestroyCacheView(composite_view);
709 image_view=DestroyCacheView(image_view);
712 case CopyAlphaCompositeOp:
713 case ChangeMaskCompositeOp:
716 Modify destination outside the overlaid region and require an alpha
717 channel to exist, to add transparency.
719 if (image->matte == MagickFalse)
720 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
721 modify_outside_overlay=MagickTrue;
724 case BlurCompositeOp:
746 Blur Image dictated by an overlay gradient map: X = red_channel;
747 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
749 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
751 if (destination_image == (Image *) NULL)
754 Determine the horizontal and vertical maximim blur.
756 SetGeometryInfo(&geometry_info);
758 value=GetImageArtifact(composite_image,"compose:args");
759 if (value != (char *) NULL)
760 flags=ParseGeometry(value,&geometry_info);
761 if ((flags & WidthValue) == 0 )
763 destination_image=DestroyImage(destination_image);
766 width=geometry_info.rho;
767 height=geometry_info.sigma;
768 blur.x1=geometry_info.rho;
771 blur.y2=geometry_info.sigma;
774 if ((flags & HeightValue) == 0)
776 if ((flags & XValue) != 0 )
781 angle=DegreesToRadians(geometry_info.xi);
782 blur.x1=width*cos(angle);
783 blur.x2=width*sin(angle);
784 blur.y1=(-height*sin(angle));
785 blur.y2=height*cos(angle);
787 if ((flags & YValue) != 0 )
789 angle_start=DegreesToRadians(geometry_info.xi);
790 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
793 Blur Image by resampling.
795 resample_filter=AcquireResampleFilter(image,exception);
796 SetResampleFilter(resample_filter,CubicFilter,2.0);
797 destination_view=AcquireCacheView(destination_image);
798 composite_view=AcquireCacheView(composite_image);
799 for (y=0; y < (ssize_t) composite_image->rows; y++)
804 register const Quantum
813 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
815 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
817 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
818 destination_image->columns,1,exception);
819 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
821 for (x=0; x < (ssize_t) composite_image->columns; x++)
823 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
825 p+=GetPixelChannels(composite_image);
828 if (fabs(angle_range) > MagickEpsilon)
833 angle=angle_start+angle_range*QuantumScale*
834 GetPixelBlue(composite_image,p);
835 blur.x1=width*cos(angle);
836 blur.x2=width*sin(angle);
837 blur.y1=(-height*sin(angle));
838 blur.y2=height*cos(angle);
840 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
841 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
842 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
843 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
844 GetPixelGreen(composite_image,p));
845 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
846 (double) y_offset+y,&pixel);
847 SetPixelInfoPixel(destination_image,&pixel,q);
848 p+=GetPixelChannels(composite_image);
849 q+=GetPixelChannels(destination_image);
851 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
852 if (sync == MagickFalse)
855 resample_filter=DestroyResampleFilter(resample_filter);
856 composite_view=DestroyCacheView(composite_view);
857 destination_view=DestroyCacheView(destination_view);
858 composite_image=destination_image;
861 case DisplaceCompositeOp:
862 case DistortCompositeOp:
881 Displace/Distort based on overlay gradient map:
882 X = red_channel; Y = green_channel;
883 compose:args = x_scale[,y_scale[,center.x,center.y]]
885 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
887 if (destination_image == (Image *) NULL)
889 SetGeometryInfo(&geometry_info);
891 value=GetImageArtifact(composite_image,"compose:args");
892 if (value != (char *) NULL)
893 flags=ParseGeometry(value,&geometry_info);
894 if ((flags & (WidthValue|HeightValue)) == 0 )
896 if ((flags & AspectValue) == 0)
898 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
900 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
904 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
905 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
910 horizontal_scale=geometry_info.rho;
911 vertical_scale=geometry_info.sigma;
912 if ((flags & PercentValue) != 0)
914 if ((flags & AspectValue) == 0)
916 horizontal_scale*=(composite_image->columns-1.0)/200.0;
917 vertical_scale*=(composite_image->rows-1.0)/200.0;
921 horizontal_scale*=(image->columns-1.0)/200.0;
922 vertical_scale*=(image->rows-1.0)/200.0;
925 if ((flags & HeightValue) == 0)
926 vertical_scale=horizontal_scale;
929 Determine fixed center point for absolute distortion map
931 Displace offset relative to a fixed absolute point
932 Select that point according to +X+Y user inputs.
933 default = center of overlay image
934 arg flag '!' = locations/percentage relative to background image
936 center.x=(MagickRealType) x_offset;
937 center.y=(MagickRealType) y_offset;
938 if (compose == DistortCompositeOp)
940 if ((flags & XValue) == 0)
941 if ((flags & AspectValue) == 0)
942 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
945 center.x=((MagickRealType) image->columns-1)/2.0;
947 if ((flags & AspectValue) == 0)
948 center.x=(MagickRealType) x_offset+geometry_info.xi;
950 center.x=geometry_info.xi;
951 if ((flags & YValue) == 0)
952 if ((flags & AspectValue) == 0)
953 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
955 center.y=((MagickRealType) image->rows-1)/2.0;
957 if ((flags & AspectValue) == 0)
958 center.y=(MagickRealType) y_offset+geometry_info.psi;
960 center.y=geometry_info.psi;
963 Shift the pixel offset point as defined by the provided,
964 displacement/distortion map. -- Like a lens...
966 GetPixelInfo(image,&pixel);
967 image_view=AcquireCacheView(image);
968 destination_view=AcquireCacheView(destination_image);
969 composite_view=AcquireCacheView(composite_image);
970 for (y=0; y < (ssize_t) composite_image->rows; y++)
975 register const Quantum
984 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
986 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
988 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
989 destination_image->columns,1,exception);
990 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
992 for (x=0; x < (ssize_t) composite_image->columns; x++)
994 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
996 p+=GetPixelChannels(composite_image);
1000 Displace the offset.
1002 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1003 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1004 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1006 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1007 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1008 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1010 (void) InterpolatePixelInfo(image,image_view,
1011 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1014 Mask with the 'invalid pixel mask' in alpha channel.
1016 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1017 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
1018 SetPixelInfoPixel(destination_image,&pixel,q);
1019 p+=GetPixelChannels(composite_image);
1020 q+=GetPixelChannels(destination_image);
1022 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1023 if (sync == MagickFalse)
1026 destination_view=DestroyCacheView(destination_view);
1027 composite_view=DestroyCacheView(composite_view);
1028 image_view=DestroyCacheView(image_view);
1029 composite_image=destination_image;
1032 case DissolveCompositeOp:
1035 Geometry arguments to dissolve factors.
1037 value=GetImageArtifact(composite_image,"compose:args");
1038 if (value != (char *) NULL)
1040 flags=ParseGeometry(value,&geometry_info);
1041 source_dissolve=geometry_info.rho/100.0;
1042 destination_dissolve=1.0;
1043 if ((source_dissolve-MagickEpsilon) < 0.0)
1044 source_dissolve=0.0;
1045 if ((source_dissolve+MagickEpsilon) > 1.0)
1047 destination_dissolve=2.0-source_dissolve;
1048 source_dissolve=1.0;
1050 if ((flags & SigmaValue) != 0)
1051 destination_dissolve=geometry_info.sigma/100.0;
1052 if ((destination_dissolve-MagickEpsilon) < 0.0)
1053 destination_dissolve=0.0;
1054 modify_outside_overlay=MagickTrue;
1055 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1057 destination_dissolve=1.0;
1058 modify_outside_overlay=MagickFalse;
1063 case BlendCompositeOp:
1065 value=GetImageArtifact(composite_image,"compose:args");
1066 if (value != (char *) NULL)
1068 flags=ParseGeometry(value,&geometry_info);
1069 source_dissolve=geometry_info.rho/100.0;
1070 destination_dissolve=1.0-source_dissolve;
1071 if ((flags & SigmaValue) != 0)
1072 destination_dissolve=geometry_info.sigma/100.0;
1073 modify_outside_overlay=MagickTrue;
1074 if ((destination_dissolve+MagickEpsilon) > 1.0)
1075 modify_outside_overlay=MagickFalse;
1079 case MathematicsCompositeOp:
1082 Just collect the values from "compose:args", setting.
1083 Unused values are set to zero automagically.
1085 Arguments are normally a comma separated list, so this probably should
1086 be changed to some 'general comma list' parser, (with a minimum
1089 SetGeometryInfo(&geometry_info);
1090 value=GetImageArtifact(composite_image,"compose:args");
1091 if (value != (char *) NULL)
1092 (void) ParseGeometry(value,&geometry_info);
1095 case ModulateCompositeOp:
1098 Determine the brightness and saturation scale.
1100 value=GetImageArtifact(composite_image,"compose:args");
1101 if (value != (char *) NULL)
1103 flags=ParseGeometry(value,&geometry_info);
1104 percent_brightness=geometry_info.rho;
1105 if ((flags & SigmaValue) != 0)
1106 percent_saturation=geometry_info.sigma;
1110 case ThresholdCompositeOp:
1113 Determine the amount and threshold.
1115 value=GetImageArtifact(composite_image,"compose:args");
1116 if (value != (char *) NULL)
1118 flags=ParseGeometry(value,&geometry_info);
1119 amount=geometry_info.rho;
1120 threshold=geometry_info.sigma;
1121 if ((flags & SigmaValue) == 0)
1124 threshold*=QuantumRange;
1130 value=GetImageArtifact(composite_image,"compose:outside-overlay");
1131 if (value != (const char *) NULL)
1132 modify_outside_overlay=IsMagickTrue(value);
1138 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1139 image_view=AcquireCacheView(image);
1140 composite_view=AcquireCacheView(composite_image);
1141 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1142 #pragma omp parallel for schedule(static,4) shared(progress,status)
1144 for (y=0; y < (ssize_t) image->rows; y++)
1157 register const Quantum
1166 if (status == MagickFalse)
1168 if (modify_outside_overlay == MagickFalse)
1172 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1176 If pixels is NULL, y is outside overlay region.
1178 pixels=(Quantum *) NULL;
1180 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1182 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1183 composite_image->columns,1,exception);
1184 if (p == (const Quantum *) NULL)
1191 p-=x_offset*GetPixelChannels(composite_image);
1193 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1194 if (q == (Quantum *) NULL)
1202 for (x=0; x < (ssize_t) image->columns; x++)
1217 if (modify_outside_overlay == MagickFalse)
1221 q+=GetPixelChannels(image);
1224 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1227 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1228 ((x-x_offset) >= (ssize_t) composite_image->columns))
1231 source[MaxPixelChannels];
1236 Dc: destination color.
1238 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1240 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1252 channel=GetPixelChannelMapChannel(image,i);
1253 traits=GetPixelChannelMapTraits(image,channel);
1254 composite_traits=GetPixelChannelMapTraits(composite_image,
1256 if ((traits == UndefinedPixelTrait) ||
1257 (composite_traits == UndefinedPixelTrait))
1261 case ChangeMaskCompositeOp:
1262 case CopyAlphaCompositeOp:
1263 case DstAtopCompositeOp:
1264 case DstInCompositeOp:
1266 case OutCompositeOp:
1267 case SrcInCompositeOp:
1268 case SrcOutCompositeOp:
1270 pixel=(MagickRealType) q[i];
1271 if (channel == AlphaPixelChannel)
1272 pixel=(MagickRealType) TransparentAlpha;
1275 case ClearCompositeOp:
1276 case CopyCompositeOp:
1277 case ReplaceCompositeOp:
1278 case SrcCompositeOp:
1280 if (channel == AlphaPixelChannel)
1282 pixel=(MagickRealType) TransparentAlpha;
1288 case BlendCompositeOp:
1289 case DissolveCompositeOp:
1291 if (channel == AlphaPixelChannel)
1293 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1297 pixel=(MagickRealType) source[channel];
1302 pixel=(MagickRealType) source[channel];
1306 q[i]=ClampToQuantum(pixel);
1308 q+=GetPixelChannels(image);
1312 Authentic composite:
1313 Sa: normalized source alpha.
1314 Da: normalized destination alpha.
1316 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1317 Da=QuantumScale*GetPixelAlpha(image,q);
1320 case BumpmapCompositeOp:
1322 alpha=GetPixelIntensity(composite_image,p)*Sa;
1325 case ColorBurnCompositeOp:
1326 case ColorDodgeCompositeOp:
1327 case DifferenceCompositeOp:
1328 case DivideDstCompositeOp:
1329 case DivideSrcCompositeOp:
1330 case ExclusionCompositeOp:
1331 case HardLightCompositeOp:
1332 case LinearBurnCompositeOp:
1333 case LinearDodgeCompositeOp:
1334 case LinearLightCompositeOp:
1335 case MathematicsCompositeOp:
1336 case MinusDstCompositeOp:
1337 case MinusSrcCompositeOp:
1338 case ModulusAddCompositeOp:
1339 case ModulusSubtractCompositeOp:
1340 case MultiplyCompositeOp:
1341 case OverlayCompositeOp:
1342 case PegtopLightCompositeOp:
1343 case PinLightCompositeOp:
1344 case ScreenCompositeOp:
1345 case SoftLightCompositeOp:
1346 case VividLightCompositeOp:
1348 alpha=RoundToUnity(Sa+Da-Sa*Da);
1351 case DarkenCompositeOp:
1352 case DstAtopCompositeOp:
1353 case DstInCompositeOp:
1355 case LightenCompositeOp:
1356 case SrcInCompositeOp:
1361 case DissolveCompositeOp:
1363 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1364 Sa+destination_dissolve*Da;
1367 case DstOverCompositeOp:
1369 alpha=Da*(-Sa)+Da+Sa;
1372 case DstOutCompositeOp:
1377 case OutCompositeOp:
1378 case SrcOutCompositeOp:
1383 case OverCompositeOp:
1384 case SrcOverCompositeOp:
1386 alpha=Sa*(-Da)+Sa+Da;
1389 case BlendCompositeOp:
1390 case PlusCompositeOp:
1392 alpha=RoundToUnity(Sa+Da);
1395 case XorCompositeOp:
1397 alpha=Sa+Da-2.0*Sa*Da;
1406 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1418 channel=GetPixelChannelMapChannel(image,i);
1419 traits=GetPixelChannelMapTraits(image,channel);
1420 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
1421 if ((traits == UndefinedPixelTrait) ||
1422 (composite_traits == UndefinedPixelTrait))
1426 Dc: destination color.
1428 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
1429 Dc=(MagickRealType) q[i];
1430 if ((traits & CopyPixelTrait) != 0)
1432 if (channel != AlphaPixelChannel)
1437 q[i]=ClampToQuantum(Sc);
1445 case AtopCompositeOp:
1446 case CopyBlackCompositeOp:
1447 case CopyBlueCompositeOp:
1448 case CopyCyanCompositeOp:
1449 case CopyGreenCompositeOp:
1450 case CopyMagentaCompositeOp:
1451 case CopyRedCompositeOp:
1452 case CopyYellowCompositeOp:
1453 case SrcAtopCompositeOp:
1454 case DstCompositeOp:
1457 pixel=QuantumRange*Da;
1460 case ChangeMaskCompositeOp:
1465 if (Da > ((MagickRealType) QuantumRange/2.0))
1467 pixel=(MagickRealType) TransparentAlpha;
1470 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
1471 if (equivalent != MagickFalse)
1473 pixel=(MagickRealType) TransparentAlpha;
1476 pixel=(MagickRealType) OpaqueAlpha;
1479 case ClearCompositeOp:
1481 pixel=(MagickRealType) TransparentAlpha;
1484 case ColorizeCompositeOp:
1485 case HueCompositeOp:
1486 case LuminizeCompositeOp:
1487 case SaturateCompositeOp:
1489 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1491 pixel=QuantumRange*Da;
1494 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1496 pixel=QuantumRange*Sa;
1501 pixel=QuantumRange*Da;
1504 pixel=QuantumRange*Sa;
1507 case CopyCompositeOp:
1508 case CopyAlphaCompositeOp:
1509 case DisplaceCompositeOp:
1510 case DistortCompositeOp:
1511 case DstAtopCompositeOp:
1512 case ReplaceCompositeOp:
1513 case SrcCompositeOp:
1515 pixel=QuantumRange*Sa;
1518 case DarkenIntensityCompositeOp:
1520 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1521 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
1524 case LightenIntensityCompositeOp:
1526 pixel=Sa*GetPixelIntensity(composite_image,p) >
1527 Da*GetPixelIntensity(image,q) ? Sa : Da;
1530 case ModulateCompositeOp:
1532 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1534 pixel=QuantumRange*Da;
1537 pixel=QuantumRange*Da;
1542 pixel=QuantumRange*alpha;
1546 q[i]=ClampToQuantum(pixel);
1550 Porter-Duff compositions:
1551 Sca: source normalized color multiplied by alpha.
1552 Dca: normalized destination color multiplied by alpha.
1554 Sca=QuantumScale*Sa*Sc;
1555 Dca=QuantumScale*Da*Dc;
1558 case DarkenCompositeOp:
1559 case LightenCompositeOp:
1560 case ModulusSubtractCompositeOp:
1568 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
1571 case AtopCompositeOp:
1572 case SrcAtopCompositeOp:
1574 pixel=Sc*Sa+Dc*(1.0-Sa);
1577 case BlendCompositeOp:
1579 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1582 case BlurCompositeOp:
1583 case DisplaceCompositeOp:
1584 case DistortCompositeOp:
1585 case CopyCompositeOp:
1586 case ReplaceCompositeOp:
1587 case SrcCompositeOp:
1592 case BumpmapCompositeOp:
1594 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1599 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1602 case ChangeMaskCompositeOp:
1607 case ClearCompositeOp:
1612 case ColorBurnCompositeOp:
1615 Refer to the March 2009 SVG specification.
1617 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1619 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1622 if (Sca < MagickEpsilon)
1624 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1627 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1628 Sca*(1.0-Da)+Dca*(1.0-Sa));
1631 case ColorDodgeCompositeOp:
1633 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1635 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1638 if (fabs(Sca-Sa) < MagickEpsilon)
1640 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1643 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
1647 case ColorizeCompositeOp:
1649 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1654 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1659 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1660 GetPixelBlue(image,q),&sans,&sans,&brightness);
1661 CompositeHSB(GetPixelRed(composite_image,p),
1662 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1663 &hue,&saturation,&sans);
1664 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1667 case RedPixelChannel: pixel=red; break;
1668 case GreenPixelChannel: pixel=green; break;
1669 case BluePixelChannel: pixel=blue; break;
1670 default: pixel=Dc; break;
1674 case CopyAlphaCompositeOp:
1676 if (channel == AlphaPixelChannel)
1678 if (composite_image->matte != MagickFalse)
1679 pixel=(MagickRealType) GetPixelAlpha(composite_image,p);
1681 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
1687 case CopyBlackCompositeOp:
1689 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
1692 case CopyBlueCompositeOp:
1693 case CopyYellowCompositeOp:
1695 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
1698 case CopyGreenCompositeOp:
1699 case CopyMagentaCompositeOp:
1701 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
1704 case CopyRedCompositeOp:
1705 case CopyCyanCompositeOp:
1707 pixel=(MagickRealType) GetPixelRed(composite_image,p);
1710 case DarkenCompositeOp:
1713 Darken is equivalent to a 'Minimum' method
1714 OR a greyscale version of a binary 'Or'
1715 OR the 'Intersection' of pixel sets.
1719 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1722 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1725 case DarkenIntensityCompositeOp:
1727 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1728 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
1731 case DifferenceCompositeOp:
1733 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1736 case DissolveCompositeOp:
1738 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1739 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1742 case DivideDstCompositeOp:
1744 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1746 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1749 if (fabs(Dca) < MagickEpsilon)
1751 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1754 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1757 case DivideSrcCompositeOp:
1759 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1761 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1764 if (fabs(Sca) < MagickEpsilon)
1766 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1769 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1772 case DstAtopCompositeOp:
1774 pixel=Dc*Da+Sc*(1.0-Da);
1777 case DstCompositeOp:
1783 case DstInCompositeOp:
1785 pixel=gamma*(Sa*Dc*Sa);
1788 case DstOutCompositeOp:
1790 pixel=gamma*(Da*Dc*(1.0-Sa));
1793 case DstOverCompositeOp:
1795 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1798 case ExclusionCompositeOp:
1800 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1804 case HardLightCompositeOp:
1808 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
1812 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1816 case HueCompositeOp:
1818 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1823 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1828 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1829 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1830 CompositeHSB(GetPixelRed(composite_image,p),
1831 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1833 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1836 case RedPixelChannel: pixel=red; break;
1837 case GreenPixelChannel: pixel=green; break;
1838 case BluePixelChannel: pixel=blue; break;
1839 default: pixel=Dc; break;
1844 case SrcInCompositeOp:
1846 pixel=gamma*(Da*Sc*Da);
1849 case LinearBurnCompositeOp:
1852 LinearBurn: as defined by Abode Photoshop, according to
1853 http://www.simplefilter.de/en/basics/mixmods.html is:
1855 f(Sc,Dc) = Sc + Dc - 1
1857 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1860 case LinearDodgeCompositeOp:
1862 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1865 case LinearLightCompositeOp:
1868 LinearLight: as defined by Abode Photoshop, according to
1869 http://www.simplefilter.de/en/basics/mixmods.html is:
1871 f(Sc,Dc) = Dc + 2*Sc - 1
1873 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1876 case LightenCompositeOp:
1880 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1883 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1886 case LightenIntensityCompositeOp:
1889 Lighten is equivalent to a 'Maximum' method
1890 OR a greyscale version of a binary 'And'
1891 OR the 'Union' of pixel sets.
1893 pixel=Sa*GetPixelIntensity(composite_image,p) >
1894 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1897 case LuminizeCompositeOp:
1899 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1904 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1909 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1910 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1911 CompositeHSB(GetPixelRed(composite_image,p),
1912 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1913 &sans,&sans,&brightness);
1914 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1917 case RedPixelChannel: pixel=red; break;
1918 case GreenPixelChannel: pixel=green; break;
1919 case BluePixelChannel: pixel=blue; break;
1920 default: pixel=Dc; break;
1924 case MathematicsCompositeOp:
1927 'Mathematics' a free form user control mathematical composition
1930 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
1932 Where the arguments A,B,C,D are (currently) passed to composite
1933 as a command separated 'geometry' string in "compose:args" image
1936 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
1938 Applying the SVG transparency formula (see above), we get...
1940 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
1942 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
1945 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
1946 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
1947 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
1950 case MinusDstCompositeOp:
1952 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
1955 case MinusSrcCompositeOp:
1958 Minus source from destination.
1962 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
1965 case ModulateCompositeOp:
1970 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1975 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
1981 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1982 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1983 brightness+=(0.01*percent_brightness*offset)/midpoint;
1984 saturation*=0.01*percent_saturation;
1985 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1988 case RedPixelChannel: pixel=red; break;
1989 case GreenPixelChannel: pixel=green; break;
1990 case BluePixelChannel: pixel=blue; break;
1991 default: pixel=Dc; break;
1995 case ModulusAddCompositeOp:
1998 if (pixel > QuantumRange)
1999 pixel-=(QuantumRange+1.0);
2000 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2003 case ModulusSubtractCompositeOp:
2007 pixel+=(QuantumRange+1.0);
2008 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2011 case MultiplyCompositeOp:
2013 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2016 case OutCompositeOp:
2017 case SrcOutCompositeOp:
2019 pixel=gamma*(Sa*Sc*(1.0-Da));
2022 case OverCompositeOp:
2023 case SrcOverCompositeOp:
2025 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
2028 case OverlayCompositeOp:
2032 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2036 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2040 case PegtopLightCompositeOp:
2043 PegTop: A Soft-Light alternative: A continuous version of the
2044 Softlight function, producing very similar results.
2046 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2048 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2050 if (fabs(Da) < MagickEpsilon)
2052 pixel=QuantumRange*gamma*(Sca);
2055 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2059 case PinLightCompositeOp:
2062 PinLight: A Photoshop 7 composition method
2063 http://www.simplefilter.de/en/basics/mixmods.html
2065 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2067 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2069 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2072 if ((Dca*Sa) > (2.0*Sca*Da))
2074 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2077 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2080 case PlusCompositeOp:
2082 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
2085 case SaturateCompositeOp:
2087 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
2092 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
2097 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2098 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2099 CompositeHSB(GetPixelRed(composite_image,p),
2100 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
2101 &sans,&saturation,&sans);
2102 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2105 case RedPixelChannel: pixel=red; break;
2106 case GreenPixelChannel: pixel=green; break;
2107 case BluePixelChannel: pixel=blue; break;
2108 default: pixel=Dc; break;
2112 case ScreenCompositeOp:
2115 Screen: a negated multiply:
2117 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2119 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2122 case SoftLightCompositeOp:
2125 Refer to the March 2009 SVG specification.
2129 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2130 Sca*(1.0-Da)+Dca*(1.0-Sa));
2133 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2135 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2136 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2140 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2141 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2144 case ThresholdCompositeOp:
2150 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2155 pixel=gamma*(Dc+delta*amount);
2158 case VividLightCompositeOp:
2161 VividLight: A Photoshop 7 composition method. See
2162 http://www.simplefilter.de/en/basics/mixmods.html.
2164 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2166 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2168 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2171 if ((2.0*Sca) <= Sa)
2173 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2174 (1.0-Da)+Dca*(1.0-Sa));
2177 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2181 case XorCompositeOp:
2183 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
2192 q[i]=ClampToQuantum(pixel);
2194 p+=GetPixelChannels(composite_image);
2195 channels=GetPixelChannels(composite_image);
2196 if (p >= (pixels+channels*composite_image->columns))
2198 q+=GetPixelChannels(image);
2200 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2202 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2207 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2208 #pragma omp critical (MagickCore_CompositeImage)
2210 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2212 if (proceed == MagickFalse)
2216 composite_view=DestroyCacheView(composite_view);
2217 image_view=DestroyCacheView(image_view);
2218 if (destination_image != (Image * ) NULL)
2219 destination_image=DestroyImage(destination_image);
2224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2228 % T e x t u r e I m a g e %
2232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2234 % TextureImage() repeatedly tiles the texture image across and down the image
2237 % The format of the TextureImage method is:
2239 % MagickBooleanType TextureImage(Image *image,const Image *texture_image,
2240 % ExceptionInfo *exception)
2242 % A description of each parameter follows:
2244 % o image: the image.
2246 % o texture_image: This image is the texture to layer on the background.
2249 MagickExport MagickBooleanType TextureImage(Image *image,
2250 const Image *texture_image,ExceptionInfo *exception)
2252 #define TextureImageTag "Texture/Image"
2264 assert(image != (Image *) NULL);
2265 if (image->debug != MagickFalse)
2266 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2267 assert(image->signature == MagickSignature);
2268 if (texture_image == (const Image *) NULL)
2269 return(MagickFalse);
2270 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod);
2271 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2272 return(MagickFalse);
2274 if ((image->compose != CopyCompositeOp) &&
2275 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
2276 (texture_image->matte != MagickFalse)))
2279 Tile texture onto the image background.
2281 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2282 #pragma omp parallel for schedule(static,4) shared(status) omp_throttle(1)
2284 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2289 if (status == MagickFalse)
2291 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2296 thread_status=CompositeImage(image,image->compose,texture_image,x+
2297 texture_image->tile_offset.x,y+texture_image->tile_offset.y,
2299 if (thread_status == MagickFalse)
2301 status=thread_status;
2305 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2310 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2311 #pragma omp critical (MagickCore_TextureImage)
2313 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2315 if (proceed == MagickFalse)
2319 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2320 image->rows,image->rows);
2324 Tile texture onto the image background (optimized).
2327 image_view=AcquireCacheView(image);
2328 texture_view=AcquireCacheView(texture_image);
2329 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2330 #pragma omp parallel for schedule(static,4) shared(status) omp_throttle(1)
2332 for (y=0; y < (ssize_t) image->rows; y++)
2337 register const Quantum
2350 if (status == MagickFalse)
2352 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2353 (y+texture_image->tile_offset.y) % texture_image->rows,
2354 texture_image->columns,1,exception);
2355 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2356 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2361 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2367 width=texture_image->columns;
2368 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2369 width=image->columns-x;
2370 for (j=0; j < (ssize_t) width; j++)
2375 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2384 channel=GetPixelChannelMapChannel(texture_image,i);
2385 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2386 traits=GetPixelChannelMapTraits(image,channel);
2387 if ((traits == UndefinedPixelTrait) ||
2388 (texture_traits == UndefinedPixelTrait))
2390 SetPixelChannel(image,channel,p[i],q);
2392 p+=GetPixelChannels(texture_image);
2393 q+=GetPixelChannels(image);
2396 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2397 if (sync == MagickFalse)
2399 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2404 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2405 #pragma omp critical (MagickCore_TextureImage)
2407 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2409 if (proceed == MagickFalse)
2413 texture_view=DestroyCacheView(texture_view);
2414 image_view=DestroyCacheView(image_view);