%
*/
-static void CompositeHSB(const Quantum red,const Quantum green,
- const Quantum blue,double *hue,double *saturation,double *brightness)
+/*
+ Composition based on the SVG specification:
+
+ A Composition is defined by...
+ Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
+ Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
+ Y = 1 for source preserved
+ Z = 1 for destination preserved
+
+ Conversion to transparency (then optimized)
+ Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
+ Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
+
+ Where...
+ Sca = Sc*Sa normalized Source color divided by Source alpha
+ Dca = Dc*Da normalized Dest color divided by Dest alpha
+ Dc' = Dca'/Da' the desired color value for this channel.
+
+ Da' in in the follow formula as 'gamma' The resulting alpla value.
+
+ Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
+ the following optimizations...
+ gamma = Sa+Da-Sa*Da;
+ gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
+ opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
+
+ The above SVG definitions also definate that Mathematical Composition
+ methods should use a 'Over' blending mode for Alpha Channel.
+ It however was not applied for composition modes of 'Plus', 'Minus',
+ the modulus versions of 'Add' and 'Subtract'.
+
+ Mathematical operator changes to be applied from IM v6.7...
+
+ 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
+ 'ModulusAdd' and 'ModulusSubtract' for clarity.
+
+ 2) All mathematical compositions work as per the SVG specification
+ with regard to blending. This now includes 'ModulusAdd' and
+ 'ModulusSubtract'.
+
+ 3) When the special channel flag 'sync' (syncronize channel updates)
+ is turned off (enabled by default) then mathematical compositions are
+ only performed on the channels specified, and are applied
+ independantally of each other. In other words the mathematics is
+ performed as 'pure' mathematical operations, rather than as image
+ operations.
+*/
+static void CompositeHSB(const double red,const double green,
+ const double blue,double *hue,double *saturation,double *brightness)
{
double
- delta;
-
- Quantum
+ delta,
max,
min;
return(x);
return(y);
}
+
static inline double MagickMax(const double x,const double y)
{
if (x > y)
*/
status=MagickTrue;
progress=0;
- image_view=AcquireCacheView(image);
- composite_view=AcquireCacheView(composite_image);
+ composite_view=AcquireVirtualCacheView(composite_image,exception);
+ image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp parallel for schedule(static,4) shared(progress,status)
#endif
ssize_t
y;
- /*
- Composition based on the SVG specification:
-
- A Composition is defined by...
- Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
- Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
- Y = 1 for source preserved
- Z = 1 for destination preserved
-
- Conversion to transparency (then optimized)
- Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
- Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
-
- Where...
- Sca = Sc*Sa normalized Source color divided by Source alpha
- Dca = Dc*Da normalized Dest color divided by Dest alpha
- Dc' = Dca'/Da' the desired color value for this channel.
-
- Da' in in the follow formula as 'gamma' The resulting alpla value.
-
- Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
- the following optimizations...
- gamma = Sa+Da-Sa*Da;
- gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
- opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
-
- The above SVG definitions also definate that Mathematical Composition
- methods should use a 'Over' blending mode for Alpha Channel.
- It however was not applied for composition modes of 'Plus', 'Minus',
- the modulus versions of 'Add' and 'Subtract'.
-
- Mathematical operator changes to be applied from IM v6.7...
-
- 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
- 'ModulusAdd' and 'ModulusSubtract' for clarity.
-
- 2) All mathematical compositions work as per the SVG specification
- with regard to blending. This now includes 'ModulusAdd' and
- 'ModulusSubtract'.
-
- 3) When the special channel flag 'sync' (syncronize channel updates)
- is turned off (enabled by default) then mathematical compositions are
- only performed on the channels specified, and are applied
- independantally of each other. In other words the mathematics is
- performed as 'pure' mathematical operations, rather than as image
- operations.
- */
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
if (image->debug != MagickFalse)
if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
break;
status=MagickTrue;
- image_view=AcquireCacheView(image);
- composite_view=AcquireCacheView(composite_image);
+ composite_view=AcquireVirtualCacheView(composite_image,exception);
+ image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp parallel for schedule(static,4) shared(status)
#endif
channel to exist, to add transparency.
*/
if (image->matte == MagickFalse)
- (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
+ {
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
+ image->matte=MagickFalse;
+ }
break;
}
case BlurCompositeOp:
blur;
/*
+ Blur Image by resampling.
+
Blur Image dictated by an overlay gradient map: X = red_channel;
Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
*/
if (destination_image == (Image *) NULL)
return(MagickFalse);
/*
- Determine the horizontal and vertical maximim blur.
+ Gather the maximum blur sigma values from user.
*/
SetGeometryInfo(&geometry_info);
flags=NoValue;
value=GetImageArtifact(composite_image,"compose:args");
if (value != (char *) NULL)
flags=ParseGeometry(value,&geometry_info);
- if ((flags & WidthValue) == 0 )
- {
+ if ((flags & WidthValue) == 0 ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"InvalidSetting","'%s' '%s'",
+ "compose:args",value);
destination_image=DestroyImage(destination_image);
return(MagickFalse);
}
- width=geometry_info.rho;
- height=geometry_info.sigma;
- blur.x1=geometry_info.rho;
+ /*
+ Users input sigma now needs to be converted to the EWA ellipse size.
+ The filter defaults to a sigma of 0.5 so to make this match the
+ users input the ellipse size needs to be doubled.
+ */
+ width=height=geometry_info.rho*2.0;
+ if ((flags & HeightValue) != 0 )
+ height=geometry_info.sigma*2.0;
+
+ /* default the unrotated ellipse width and height axis vectors */
+ blur.x1=width;
blur.x2=0.0;
blur.y1=0.0;
- blur.y2=geometry_info.sigma;
- angle_start=0.0;
- angle_range=0.0;
- if ((flags & HeightValue) == 0)
- blur.y2=blur.x1;
+ blur.y2=height;
+ /* rotate vectors if a rotation angle is given */
if ((flags & XValue) != 0 )
{
MagickRealType
blur.y1=(-height*sin(angle));
blur.y2=height*cos(angle);
}
+ /* Otherwise lets set a angle range and calculate in the loop */
+ angle_start=0.0;
+ angle_range=0.0;
if ((flags & YValue) != 0 )
{
angle_start=DegreesToRadians(geometry_info.xi);
angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
}
/*
- Blur Image by resampling.
- FUTURE: this is currently broken, especially for small sigma blurs
- This needs to be fixed to use a non-user filter setup that provides
- far more control than currently available.
+ Set up a gaussian cylindrical filter for EWA Bluring.
+
+ As the minimum ellipse radius of support*1.0 the EWA algorithm
+ can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
+ This means that even 'No Blur' will be still a little blurry!
+
+ The solution (as well as the problem of preventing any user
+ expert filter settings, is to set our own user settings, then
+ restore them afterwards.
*/
resample_filter=AcquireResampleFilter(image,exception);
- SetResampleFilter(resample_filter,CubicFilter); /* was blur*2 */
- destination_view=AcquireCacheView(destination_image);
- composite_view=AcquireCacheView(composite_image);
+ SetResampleFilter(resample_filter,GaussianFilter);
+
+ /* do the variable blurring of each pixel in image */
+ GetPixelInfo(image,&pixel);
+ composite_view=AcquireVirtualCacheView(composite_image,exception);
+ destination_view=AcquireAuthenticCacheView(destination_image,exception);
for (y=0; y < (ssize_t) composite_image->rows; y++)
{
MagickBooleanType
blur.y1=(-height*sin(angle));
blur.y2=height*cos(angle);
}
- ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
- GetPixelRed(composite_image,p),blur.y1*QuantumScale*
- GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
- GetPixelRed(composite_image,p),blur.y2*QuantumScale*
- GetPixelGreen(composite_image,p));
+#if 0
+ if ( x == 10 && y == 60 ) {
+ fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
+ blur.x1, blur.x2, blur.y1, blur.y2);
+ fprintf(stderr, "scaled by=%lf,%lf\n",
+ QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
+#endif
+ ScaleResampleFilter(resample_filter,
+ blur.x1*QuantumScale*GetPixelRed(composite_image,p),
+ blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
+ blur.x2*QuantumScale*GetPixelRed(composite_image,p),
+ blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
(void) ResamplePixelColor(resample_filter,(double) x_offset+x,
- (double) y_offset+y,&pixel);
+ (double) y_offset+y,&pixel,exception);
SetPixelInfoPixel(destination_image,&pixel,q);
p+=GetPixelChannels(composite_image);
q+=GetPixelChannels(destination_image);
displacement/distortion map. -- Like a lens...
*/
GetPixelInfo(image,&pixel);
- image_view=AcquireCacheView(image);
- destination_view=AcquireCacheView(destination_image);
- composite_view=AcquireCacheView(composite_image);
+ image_view=AcquireVirtualCacheView(image,exception);
+ composite_view=AcquireVirtualCacheView(composite_image,exception);
+ destination_view=AcquireAuthenticCacheView(destination_image,exception);
for (y=0; y < (ssize_t) composite_image->rows; y++)
{
MagickBooleanType
destination_dissolve=geometry_info.sigma/100.0;
if ((destination_dissolve-MagickEpsilon) < 0.0)
destination_dissolve=0.0;
+ /* posible speed up? -- from IMv6 update
+ clip_to_self=MagickFalse;
+ if ((destination_dissolve+MagickEpsilon) > 1.0 )
+ {
+ destination_dissolve=1.0;
+ clip_to_self=MagickTrue;
+ }
+ */
}
break;
}
status=MagickTrue;
progress=0;
midpoint=((MagickRealType) QuantumRange+1.0)/2;
- image_view=AcquireCacheView(image);
- composite_view=AcquireCacheView(composite_image);
+ composite_view=AcquireVirtualCacheView(composite_image,exception);
+ image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp parallel for schedule(static,4) shared(progress,status)
#endif
red,
saturation;
+ PixelInfo
+ destination_pixel,
+ source_pixel;
+
register const Quantum
*restrict p;
hue=0.0;
saturation=0.0;
brightness=0.0;
+ GetPixelInfo(image,&destination_pixel);
+ GetPixelInfo(composite_image,&source_pixel);
for (x=0; x < (ssize_t) image->columns; x++)
{
MagickRealType
q+=GetPixelChannels(image);
continue;
}
+ switch (compose)
+ {
+ case ColorizeCompositeOp:
+ case HueCompositeOp:
+ case LuminizeCompositeOp:
+ case ModulateCompositeOp:
+ case SaturateCompositeOp:
+ {
+ GetPixelInfoPixel(composite_image,p,&source_pixel);
+ GetPixelInfoPixel(image,q,&destination_pixel);
+ break;
+ }
+ default:
+ break;
+ }
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
double
pixel=Sc;
break;
}
- CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
- GetPixelBlue(image,q),&sans,&sans,&brightness);
- CompositeHSB(GetPixelRed(composite_image,p),
- GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
+ CompositeHSB(destination_pixel.red,destination_pixel.green,
+ destination_pixel.blue,&sans,&sans,&brightness);
+ CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
&hue,&saturation,&sans);
HSBComposite(hue,saturation,brightness,&red,&green,&blue);
switch (channel)
pixel=Sc;
break;
}
- CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
- GetPixelBlue(image,q),&hue,&saturation,&brightness);
- CompositeHSB(GetPixelRed(composite_image,p),
- GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
+ CompositeHSB(destination_pixel.red,destination_pixel.green,
+ destination_pixel.blue,&hue,&saturation,&brightness);
+ CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
&hue,&sans,&sans);
HSBComposite(hue,saturation,brightness,&red,&green,&blue);
switch (channel)
pixel=Sc;
break;
}
- CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
- GetPixelBlue(image,q),&hue,&saturation,&brightness);
- CompositeHSB(GetPixelRed(composite_image,p),
- GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
+ CompositeHSB(destination_pixel.red,destination_pixel.green,
+ destination_pixel.blue,&hue,&saturation,&brightness);
+ CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
&sans,&sans,&brightness);
HSBComposite(hue,saturation,brightness,&red,&green,&blue);
switch (channel)
pixel=Dc;
break;
}
- CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
- GetPixelBlue(image,q),&hue,&saturation,&brightness);
+ CompositeHSB(destination_pixel.red,destination_pixel.green,
+ destination_pixel.blue,&hue,&saturation,&brightness);
brightness+=(0.01*percent_brightness*offset)/midpoint;
saturation*=0.01*percent_saturation;
HSBComposite(hue,saturation,brightness,&red,&green,&blue);
pixel=Sc;
break;
}
- CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
- GetPixelBlue(image,q),&hue,&saturation,&brightness);
- CompositeHSB(GetPixelRed(composite_image,p),
- GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
+ CompositeHSB(destination_pixel.red,destination_pixel.green,
+ destination_pixel.blue,&hue,&saturation,&brightness);
+ CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
&sans,&saturation,&sans);
HSBComposite(hue,saturation,brightness,&red,&green,&blue);
switch (channel)
Tile texture onto the image background (optimized).
*/
status=MagickTrue;
- image_view=AcquireCacheView(image);
- texture_view=AcquireCacheView(texture_image);
+ texture_view=AcquireVirtualCacheView(texture_image,exception);
+ image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp parallel for schedule(static) shared(status)
#endif