This filter primitive lights an image using the alpha channel as a bump map. The resulting image is an RGBA opaque image based on the light color with alpha = 1.0 everywhere. The lighting calculation follows the standard diffuse component of the Phong lighting model. The resulting image depends on the light color, light position and surface geometry of the input bump map.
The light map produced by this filter primitive can be combined with a texture image using the multiply term of the arithmetic 'feComposite' compositing method. Multiple light sources can be simulated by adding several of these light maps together before applying it to the texture image.
The formulas below make use of 3x3 filters. Because they operate on pixels, such filters are inherently resolution-dependent. To make 'feDiffuseLighting' produce resolution-independent results, an explicit value should be provided for either the 'filterRes' attribute on the 'filter' element and/or attribute 'kernelUnitLength'.
'kernelUnitLength', in combination with the other attributes, defines an implicit pixel grid in the filter effects coordinate system (i.e., the coordinate system established by the 'primitiveUnits' attribute). If the pixel grid established by 'kernelUnitLength' is not scaled to match the pixel grid established by attribute 'filterRes' (implicitly or explicitly), then the input image will be temporarily rescaled to match its pixels with 'kernelUnitLength'. The 3x3 filters are applied to the resampled image. After applying the filter, the image is resampled back to its original resolution.
When the image must be resampled, it is recommended that high quality viewers make use of appropriate interpolation techniques, for example bilinear or bicubic. Depending on the speed of the available interpolents, this choice may be affected by the 'image-rendering' property setting. Note that implementations might choose approaches that minimize or eliminate resampling when not necessary to produce proper results, such as when the document is zoomed out such that 'kernelUnitLength' is considerably smaller than a device pixel.
For the formulas that follow, the Norm(Ax,Ay,Az) function is defined as:
Norm(Ax,Ay,Az) = sqrt(Ax^2+Ay^2+Az^2)
The resulting RGBA image is computed as follows:
Dr = kd * N.L * Lr
Dg = kd * N.L * Lg
Db = kd * N.L * Lb
Da = 1.0
where
N is a function of x and y and depends on the surface gradient as follows:
The surface described by the input alpha image Ain (x,y) is:
Z (x,y) = surfaceScale * Ain (x,y)
Surface normal is calculated using the Sobel gradient 3x3 filter. Different filter kernels are used depending on whether the given pixel is on the interior or an edge. For each case, the formula is:
Nx (x,y)= - surfaceScale * FACTORx *
(K x(0,0)*I(x-dx,y-dy) + Kx(1,0)*I(x,y-dy) + Kx(2,0)*I(x+dx,y-dy) +
K x(0,1)*I(x-dx,y) + Kx(1,1)*I(x,y) + Kx(2,1)*I(x+dx,y) +
K x(0,2)*I(x-dx,y+dy) + Kx(1,2)*I(x,y+dy) + Kx(2,2)*I(x+dx,y+dy))
Ny (x,y)= - surfaceScale * FACTORy *
(K y(0,0)*I(x-dx,y-dy) + Ky(1,0)*I(x,y-dy) + Ky(2,0)*I(x+dx,y-dy) +
K y(0,1)*I(x-dx,y) + Ky(1,1)*I(x,y) + Ky(2,1)*I(x+dx,y) +
K y(0,2)*I(x-dx,y+dy) + Ky(1,2)*I(x,y+dy) + Ky(2,2)*I(x+dx,y+dy))
Nz (x,y) = 1.0
N = (Nx, Ny, Nz) / Norm((Nx,Ny,Nz))
In these formulas, the dx and dy values (e.g., I(x-dx,y-dy)), represent deltas relative to a given (x,y) position for the purpose of estimating the slope of the surface at that point. These deltas are determined by the value (explicit or implicit) of attribute 'kernelUnitLength'.
|
Top/left corner: FACTORx=2/(3*dx) |
Top row: FACTORx=1/(3*dx) |
Top/right corner: FACTORx=2/(3*dx) |
|
Left column: FACTORx=1/(2*dx) |
Interior pixels: FACTORx=1/(4*dx) |
Right column: FACTORx=1/(2*dx) |
|
Bottom/left corner: FACTORx=2/(3*dx) |
Bottom row: FACTORx=1/(3*dx) |
Bottom/right corner: FACTORx=2/(3*dx) |
L, the unit vector from the image sample to the light, is calculated as follows:
For Infinite light sources it is constant:
Lx = cos(azimuth)*cos(elevation)
Ly = sin(azimuth)*cos(elevation)
Lz = sin(elevation)
For Point and spot lights it is a function of position:
Lx = Lightx - x
Ly = Lighty - y
Lz = Lightz - Z(x,y)
L = (Lx, Ly, Lz) / Norm(Lx, Ly, Lz)
where Lightx, Lighty, and Lightz are the input light position.
Lr,Lg,Lb, the light color vector, is a function of position in the spot light case only:
Lr = Lightr*pow((-L.S),specularExponent)
Lg = Lightg*pow((-L.S),specularExponent)
Lb = Lightb*pow((-L.S),specularExponent)
where S is the unit vector pointing from the light to the point (pointsAtX, pointsAtY, pointsAtZ) in the x-y plane:
Sx = pointsAtX - Lightx
Sy = pointsAtY - Lighty
Sz = pointsAtZ - Lightz
S = (Sx, Sy, Sz) / Norm(Sx, Sy, Sz)
If L.S is positive, no light is present. (Lr = Lg = Lb = 0)
<!ENTITY % SVG.feDiffuseLighting.extra.content "" > <!ENTITY % SVG.feDiffuseLighting.element "INCLUDE" > <![%SVG.feDiffuseLighting.element;[ <!ENTITY % SVG.feDiffuseLighting.content "(( %SVG.feDistantLight.qname; | %SVG.fePointLight.qname; | %SVG.feSpotLight.qname; ), ( %SVG.animate.qname; | %SVG.set.qname; | %SVG.animateColor.qname; %SVG.feDiffuseLig\ hting.extra.content; )*)" > <!ELEMENT %SVG.feDiffuseLighting.qname; %SVG.fe\ DiffuseLighting.content; > <!-- end of SVG.feDiffuseLighting.element -->]]> <!ENTITY % SVG.feDiffuseLighting.attlist "INCLUDE" > <![%SVG.feDiffuseLighting.attlist;[ <!ATTLIST %SVG.feDiffuseLighting.qname; %SVG.Core.attrib; %SVG.Style.attrib; %SVG.Color.attrib; %SVG.FilterColor.attrib; %SVG.FilterPrimitiveWithIn.attrib; lighting-color %SVGColor.datatype; #IMPLIED surfaceScale %Number.datatype; #IMPLIED diffuseConstant %Number.datatype; #IMPLIED kernelUnitLength %NumberOptionalNumber.datatype; #IMPLIED > |
Attribute definitions:
dx and dy, respectively, in the surface normal calculation formulas. By specifying value(s) for 'kernelUnitLength', the kernel becomes defined in a scalable, abstract coordinate system. If 'kernelUnitLength' is not specified, the dx and dy values should represent very small deltas relative to a given (x,y) position, which might be implemented in some cases as one pixel in the intermediate image offscreen bitmap, which is a pixel-based coordinate system, and thus potentially not scalable. For some level of consistency across display media and user agents, it is necessary that a value be provided for at least one of 'filterRes' and 'kernelUnitLength'. Discussion of intermediate images are in the Introduction and in the description of attribute 'filterRes'.The light source is defined by one of the child elements 'feDistantLight', 'fePointLight' or 'feSpotLight'. The light color is specified by property 'lighting-color'.
This filter primitive uses the pixels values from the image from 'in2' to spatially displace the image from 'in'. This is the transformation to be performed:
P'(x,y) <- P( x + scale * (XC(x,y) - .5), y + scale * (YC(x,y) - .5))
where P(x,y) is the input image, 'in', and P'(x,y) is the destination. XC(x,y) and YC(x,y) are the component values of the designated by the xChannelSelector and yChannelSelector. For example, to use the R component of 'in2' to control displacement in x and the G component of Image2 to control displacement in y, set xChannelSelector to "R" and yChannelSelector to "G".
The displacement map defines the inverse of the mapping performed.
The calculations using the pixel values from 'in2' are performed using non-premultiplied color values. If the image from 'in2' consists of premultiplied color values, those values are automatically converted into non-premultiplied color values before performing this operation.
This filter can have arbitrary non-localized effect on the input which might require substantial buffering in the processing pipeline. However with this formulation, any intermediate buffering needs can be determined by scale which represents the maximum range of displacement in either x or y.
When applying this filter, the source pixel location will often lie between several source pixels. In this case it is recommended that high quality viewers apply an interpolent on the surrounding pixels, for example bilinear or bicubic, rather than simply selecting the nearest source pixel. Depending on the speed of the available interpolents, this choice may be affected by the 'image-rendering' property setting.
The 'color-interpolation-filters' property only applies to the 'in2' source image and does not apply to the 'in' source image.
<!ENTITY % SVG.feDisplacementMap.extra.content "" > <!ENTITY % SVG.feDisplacementMap.element "INCLUDE" > <![%SVG.feDisplacementMap.element;[ <!ENTITY % SVG.feDisplacementMap.content "( %SVG.animate.qname; | %SVG.set.qname; %SVG.feDisplacementMap.extra.content; )*" > <!ELEMENT %SVG.feDisplacementMap.qname; %SVG.fe\ DisplacementMap.content; > <!-- end of SVG.feDisplacementMap.element -->]]> <!ENTITY % SVG.feDisplacementMap.attlist "INCLUDE" > <![%SVG.feDisplacementMap.attlist;[ <!ATTLIST %SVG.feDisplacementMap.qname; %SVG.Core.attrib; %SVG.FilterColor.attrib; %SVG.FilterPrimitiveWithIn.attrib; in2 CDATA #REQUIRED scale %Number.datatype; #IMPLIED xChannelSelector ( R | G | B | A ) 'A' yChannelSelector ( R | G | B | A ) 'A' > |
Attribute definitions:
This filter primitive creates a rectangle filled with the color and opacity values from properties 'flood-color' and 'flood-opacity'. The rectangle is as large as the filter primitive subregion established by the 'x', 'y', 'width' and 'height' attributes on the 'feFlood' element.
<!ENTITY % SVG.feFlood.extra.content "" > <!ENTITY % SVG.feFlood.element "INCLUDE" > <![%SVG.feFlood.element;[ <!ENTITY % SVG.feFlood.content "( %SVG.animate.qname; | %SVG.set.qname; | %SVG.animateColor.qname; %SVG.feFlood.extra.content; )*" > <!ELEMENT %SVG.feFlood.qname; %SVG.feFlood.content; > <!-- end of SVG.feFlood.element -->]]> <!ENTITY % SVG.feFlood.attlist "INCLUDE" > <![%SVG.feFlood.attlist;[ <!ATTLIST %SVG.feFlood.qname; %SVG.Core.attrib; %SVG.Style.attrib; %SVG.Color.attrib; %SVG.FilterColor.attrib; %SVG.FilterPrimitiveWithIn.attrib; flood-color %SVGColor.datatype; #IMPLIED flood-opacity %OpacityValue.datatype; #IMPLIED > |
The 'flood-color' property indicates what color to use to flood the current filter primitive subregion. The keyword currentColor and ICC colors can be specified in the same manner as within a <paint> specification for the 'fill' and 'stroke' properties.
| Value: | currentColor | <color> [icc-color(<name>[,<icccolorvalue>]*)] | inherit |
| Initial: | black |
| Applies to: | 'feFlood' elements |
| Inherited: | no |
| Percentages: | N/A |
| Media: | visual |
| Animatable: | yes |
The 'flood-opacity' property defines the opacity value to use across the entire filter primitive subregion.
| Value: | <opacity-value> | inherit |
| Initial: | 1 |
| Applies to: | 'feFlood' elements |
| Inherited: | no |
| Percentages: | N/A |
| Media: | visual |
| Animatable: | yes |
This filter primitive performs a Gaussian blur on the input image.
The Gaussian blur kernel is an approximation of the normalized convolution:
H(x) = exp(-x2/ (2s2)) / sqrt(2* pi*s2)
where 's' is the standard deviation specified by 'stdDeviation'.
The value of 'stdDeviation' can be either one or two numbers. If two numbers are provided, the first number represents a standard deviation value along the x-axis of the current coordinate system and the second value represents a standard deviation in Y. If one number is provided, then that value is used for both X and Y.
Even if only one value is provided for 'stdDeviation', this can be implemented as a separable convolution.
For larger values of 's' (s >= 2.0), an approximation can be used: Three successive box-blurs build a piece-wise quadratic convolution kernel, which approximates the Gaussian kernel to within roughly 3%.
let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
... if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary between the output pixel and the one to the left, the second one centered on the pixel boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel.
Frequently this operation will take place on alpha-only images, such as that produced by the built-in input, SourceAlpha. The implementation may notice this and optimize the single channel case. If the input has infinite extent and is constant, this operation has no effect. If the input has infinite extent and is a tile, the filter is evaluated with periodic boundary conditions.
<!ENTITY % SVG.feGaussianBlur.extra.content "" > <!ENTITY % SVG.feGaussianBlur.element "INCLUDE" > <![%SVG.feGaussianBlur.element;[ <!ENTITY % SVG.feGaussianBlur.content "( %SVG.animate.qname; | %SVG.set.qname; %SVG.feGaussianBlur.extra.content; )*" > <!ELEMENT %SVG.feGaussianBlur.qname; %SVG.feGaussi\ anBlur.content; > <!-- end of SVG.feGaussianBlur.element -->]]> <!ENTITY % SVG.feGaussianBlur.attlist "INCLUDE" > <![%SVG.feGaussianBlur.attlist;[ <!ATTLIST %SVG.feGaussianBlur.qname; %SVG.Core.attrib; %SVG.FilterColor.attrib; %SVG.FilterPrimitiveWithIn.attrib; stdDeviation %NumberOptionalNumber.datatype; #IMPLIED > |
Attribute definitions:
The example at the start of this chapter makes use of the 'feGaussianBlur' filter primitive to create a drop shadow effect.
This filter primitive refers to a graphic external to this filter element, which is loaded or rendered into an RGBA raster and becomes the result of the filter primitive.
This filter primitive can refer to an external image or can be a reference to another piece of SVG. It produces an image similar to the built-in image source SourceGraphic except that the graphic comes from an external source.
If the 'xlink:href' references a stand-alone image resource such as a JPEG, PNG or SVG file, then the image resource is rendered according to the behavior of the 'image' element; otherwise, the referenced resource is rendered according to the behavior of the 'use' element. In either case, the current user coordinate system depends on the value of attribute 'primitiveUnits' on the 'filter' element. The processing of the 'preserveAspectRatio' attribute on the 'feImage' element is identical to that of the 'image' element.
When the referenced image must be resampled to match the device coordinate system, it is recommended that high quality viewers make use of appropriate interpolation techniques, for example bilinear or bicubic. Depending on the speed of the available interpolents, this choice may be affected by the 'image-rendering' property setting.
<!ENTITY % SVG.feImage.extra.content "" > <!ENTITY % SVG.feImage.element "INCLUDE" > <![%SVG.feImage.element;[ <!ENTITY % SVG.feImage.content "( %SVG.animate.qname; | %SVG.set.qname; | %SVG.animateTransform.qname; %SVG.feImage.extra.content; )*" > <!ELEMENT %SVG.feImage.qname; %SVG.feImage.content; > <!-- end of SVG.feImage.element -->]]> <!ENTITY % SVG.feImage.attlist "INCLUDE" > <![%SVG.feImage.attlist;[ <!ATTLIST %SVG.feImage.qname; %SVG.Core.attrib; %SVG.Style.attrib; %SVG.Presentation.attrib; %SVG.FilterPrimitive.attrib; %SVG.XLinkEmbed.attrib; %SVG.External.attrib; preserveAspectRatio %PreserveAspectRatioSpec.datatype; 'xMidYMid meet' > |
This filter primitive composites input image layers on top of each other using the over operator with Input1 (corresponding to the first 'feMergeNode' child element) on the bottom and the last specified input, InputN (corresponding to the last 'feMergeNode' child element), on top.
Many effects produce a number of intermediate layers in order to create the final output image. This filter allows us to collapse those into a single image. Although this could be done by using n-1 Composite-filters, it is more convenient to have this common operation available in this form, and offers the implementation some additional flexibility.
Each 'feMerge' element can have any number of 'feMergeNode' subelements, each of which has an 'in' attribute.
The canonical implementation of feMerge is to render the entire effect into one RGBA layer, and then render the resulting layer on the output device. In certain cases (in particular if the output device itself is a continuous tone device), and since merging is associative, it might be a sufficient approximation to evaluate the effect one layer at a time and render each layer individually onto the output device bottom to top.
If the topmost image input is SourceGraphic and this 'feMerge' is the last filter primitive in the filter, the implementation is encouraged to render the layers up to that point, and then render the SourceGraphic directly from its vector description on top.
<!ENTITY % SVG.feMerge.extra.content "" > <!ENTITY % SVG.feMerge.element "INCLUDE" > <![%SVG.feMerge.element;[ <!ENTITY % SVG.feMerge.content "( %SVG.feMergeNode.qname; %SVG.feMerge.extra.\ content; )*" > <!ELEMENT %SVG.feMerge.qname; %SVG.feMerge.content; > <!-- end of SVG.feMerge.element -->]]> <!ENTITY % SVG.feMerge.attlist "INCLUDE" > <![%SVG.feMerge.attlist;[ <!ATTLIST %SVG.feMerge.qname; %SVG.Core.attrib; %SVG.FilterColor.attrib; %SVG.FilterPrimitive.attrib; > <!-- end of SVG.feMerge.attlist -->]]> <!-- feMergeNode: Filter Effect Merge Node Element ..... --> <!ENTITY % SVG.feMergeNode.extra.content "" > <!ENTITY % SVG.feMergeNode.element "INCLUDE" > <![%SVG.feMergeNode.element;[ <!ENTITY % SVG.feMergeNode.content "( %SVG.animate.qname; | %SVG.set.qname; %SVG.feMergeNode.extra.content; )*" > <!ELEMENT %SVG.feMergeNode.qname; %SVG.feMergeNode.co\ ntent; > <!-- end of SVG.feMergeNode.element -->]]> <!ENTITY % SVG.feMergeNode.attlist "INCLUDE" > <![%SVG.feMergeNode.attlist;[ <!ATTLIST %SVG.feMergeNode.qname; %SVG.Core.attrib; in CDATA #IMPLIED > |
The example at the start of this chapter makes use of the 'feMerge' filter primitive to composite two intermediate filter results together.
This filter primitive performs "fattening" or "thinning" of artwork. It is particularly useful for fattening or thinning an alpha channel.
The dilation (or erosion) kernel is a rectangle with a width of 2*x-radius and a height of 2*y-radius. In dilation, the output pixel is the individual component-wise maximum of the corresponding R,G,B,A values in the input image's kernel rectangle. In erosion, the output pixel is the individual component-wise minimum of the corresponding R,G,B,A values in the input image's kernel rectangle.
Frequently this operation will take place on alpha-only images, such as that produced by the built-in input, SourceAlpha. In that case, the implementation might want to optimize the single channel case.
If the input has infinite extent and is constant, this operation has no effect. If the input has infinite extent and is a tile, the filter is evaluated with periodic boundary conditions.
Because 'feMorphology' operates on premultipied color values, it will always result in color values less than or equal to the alpha channel.
<!ENTITY % SVG.feMorphology.extra.content "" > <!ENTITY % SVG.feMorphology.element "INCLUDE" > <![%SVG.feMorphology.element;[ <!ENTITY % SVG.feMorphology.content "( %SVG.animate.qname; | %SVG.set.qname; %SVG.feMorphology.extra.content; )*" > <!ELEMENT %SVG.feMorphology.qname; %SVG.feMorphology\ .content; > <!-- end of SVG.feMorphology.element -->]]> <!ENTITY % SVG.feMorphology.attlist "INCLUDE" > <![%SVG.feMorphology.attlist;[ <!ATTLIST %SVG.feMorphology.qname; %SVG.Core.attrib; %SVG.FilterColor.attrib; %SVG.FilterPrimitiveWithIn.attrib; operator ( erode | dilate ) 'erode' radius %NumberOptionalNumber.datatype; #IMPLIED > |
Attribute definitions:
Example feMorphology shows examples of the four types of feMorphology operations.
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="5cm" height="7cm" viewBox="0 0 700 500"
version="1.1"
xmlns="http://www.w3.org/2000/svg" version="1.1"> <title>Example feMorphology - Examples of erode and dilate</title> <desc>Five text strings drawn as outlines. The first is unfiltered. The second and third use 'erode'. The fourth and fifth use 'dilate'.</desc> <defs> <filter id="Erode3"> <feMorphology operator="erode" in="SourceGraphic" radius="3" /> </filter> <filter id="Erode6"> <feMorphology operator="erode" in="SourceGraphic" radius="6" /> </filter> <filter id="Dilate3"> <feMorphology operator="dilate" in="SourceGraphic" radius="3" /> </filter> <filter id="Dilate6"> <feMorphology operator="dilate" in="SourceGraphic" radius="6" /> </filter> </defs> <rect fill="none" stroke="blue" stroke-width="2" x="1" y="1" width="698" height="498"/> <g enable-background="new" > <g font-family="Verdana" font-size="75" fill="none" stroke="black" stroke-width="6" > <text x="50" y="90">Unfiltered</text> <text x="50" y="180" filter="url(#Erode3)" >Erode radius 3</text> <text x="50" y="270" filter="url(#Erode6)" >Erode radius 6</text> <text x="50" y="360" filter="url(#Dilate3)" >Dilate radius 3</text> <text x="50" y="450" filter="url(#Dilate6)" >Dilate radius 6</text> </g> </g> </svg>
![]() |