Filter Effects in SVG

 

Excerpted From An SVG Primer for Today’s Browsers by David Dailey (© 2006 by David Dailey, © 2008 by World Wide Web Consortium)

 

SVG has a wealthy set of options for manipulating pictures (either drawn or bitmapped). These are options attached to the <filter> object: a bag of tricks both diverse and, in some cases, complex. Unfortunately, the filters are apparently difficult to implement by the browser developers, and so as of this writing some of the features are not implemented in Internet Explorer, a few more are not implemented yet in Opera, and fewer are yet implemented in Firefox. The development of SVG has split into two tracks – those trying to implement an acceptable subset of SVG (called “SVG Tiny”) on small-display mobile devices, and those working toward compliance with the superset, sometimes called “SVG Full”. Fortunately SVG Full and SVG Tiny are consistent with one another, so it is just a matter of making sure that the features you desire reach the audience you seek to reach, which has been the same fundamental problem with cross-platform computing since it got started in the 1950’s.

SVG’s filtering options are called filter primitives.[1] As primitives they are probably not semantically complete in the sense of allowing us to form all possible expressions (whatever that might mean in the language of imagery). They also lack the irreducibility that one often associates with semantic primitives: many equivalent results can be expressed in several different ways[2]. Filters can be computationally quite time consuming. The larger the region they are applied to, the slower they may take to render. This is particularly relevant when one considers animating any of the attributes of these filter effects.

 

This treatment of SVG’s filters will not be exhaustive. Let us examine a few of the filter primitives to give a basic sense of how they work and what they do.

 

Contents:

Browser support

The basic <filter>

Simpler filter primitives

feGaussianBlur

feColorMatrix

feConvolveMatrix

feComponentTransfer

feMorphology

Filter primitives that stand alone

feFlood

feImage

feTurbulence

feDiffuseLighting and feSpecularLighting

Utility filters (feTile and feOffset)

feTile

feOffset

Combining Filter Primitives

feMerge

Filtering graphics along with their backgrounds

feBlend

feComposite

feDisplacementMap

 

Browser support

 

Currently much of what can be done with filters is supported by Internet Explorer (versions 6 or 7 with the Adobe ASV3.03 plugin), Opera (9.5), and Firefox (3.01). I am not sure what Apple's time frame (or WebKit's) for implementation is, nor what levels of support might currently exist in the Mobile market. The overall support within Opera and IE/ASV appear to be roughly matched with some effects available in one that are missing in the other. FF 3 introduced big gains in its support for filters, with most now supported. It seems not, at the current time, to support feFlood, feDisplacementMap, and perhaps a few others.

 

Note that for this particular presentation, some of the examples are illustrated using SMIL animation. This is done since it shows, quite directly, the effect of varying some parameter of a particular filter. The reader is reminded that SMIL is not yet supported by Safari or FF, so you may want to use Opera or IE/ASV for maximum effect.

 

The basic <filter>

 

A <filter> is applied to another object much as a clipPath or gradient – namely through a filter=”url(#filtername)” attribute defined within the object to which the filter will be applied. The <filter> tag itself must have one or more filter primitives inside it; those primitive operations will be conducted in the order they are defined, from top to bottom.

 

Example syntax:

 

<filter id=”F”>

       <anyParticularPrimitive1>

<anyParticularPrimitive2>

<anyParticularPrimitiveN>

</filter>

<anyParticularSVGObjectOrGroup filter=”url(#F)”/>

 

 

Simpler filter primitives

 

Some filters are composite filters in the sense that they require the prior definition of other filters. Others are a bit simpler, in that they may be applied directly to graphic objects without advance buildup. We’ll begin the study of filters with the simpler ones: feGaussianBlur, feColorMatrix, and feSpecularLighting. Later we’ll cover the more complex ones. Now, let’s get right on to some real examples.

 

 

feGaussianBlur[3]

 

This filter blurs an image. The parameter associated with this filter is the standard deviation (stdDeviation) which controls the distance from which neighboring pixels will be allowed to influence a pixel and hence, the amount of blurring.

 

First, a filter is set up with an <feGaussianBlur> inside:

 

<filter id="A">

       <feGaussianBlur stdDeviation="1" />

</filter>

 

Then the <filter> is applied to an image to be blurred.

 

<rect x="42%" y="10%" width="16%" height="25%"

fill="white" filter="url(#A)"/>

 

On the web, here is a simple illustration of the above.

 

The following shows the effect of increasing the value of  stdDeviation on two different images on a black background.

 

Effect of s=stdDeviation on feGaussianBlur

<filter id="A"><feGaussianBlur stdDeviation=S/></filter>

<rect x="42%" y="10%" width="16%" height="25%" filter="url(#A)" fill="white"/>

blurRect2

blurRect10

blurRect25

S=2

S=10

S=25

<image x="42%" y="10%" width="16%" height="25%" filter="url(#A)" xlink:href="p0.jpg"/>

blurImage2

blurImage10

blurImage25

S=2

S=10

S=25

 

Observe that the blurred object expands beyond its original bounds and that values outside its boundary are considered to be transparent so that any background present (in this case, monochromatic black) will be visible inside the edges of the image itself. To restrict the image so it does not bleed beyond its boundaries, one can either set the x, y, height and width attributes of the filter itself (the easiest way), or use another filter primitive, the feOffset, discussed later in this chapter.

 

Restricting the extent of a filter to the size of the source image.

filterBlurExtent

Restricted to size of source image

Unrestricted to size of source image

<filter id="B" x="0%" y="0%" width="100%" height="100%">

   <feGaussianBlur stdDeviation="25"/>

</filter>

<filter id="A">

   <feGaussianBlur stdDeviation="25"/>

</filter>

 

It is also worth noting that if  <feGaussianBlur> takes two parameters, rather than one, for its stdDeviation attribute, then the first will represent horizontal blurring, while the second represents vertical blurring. The statement

 

<feGaussianBlur id="fGB" stdDeviation="25, 0" />

 

will blur the object only horizontally, in ways that, for a monochromatic rectangle might resemble a linear gradient with three equidistant stops.

 

For a web view of several of these effects refer to this location. For an animated version (using either JavaScript or SMIL for the animation) see this location.

 

feColorMatrix

 

The feColorMatrix primitive allows the redefinition of colors within an image, based on the ability to multiply each pixel’s RGB and alpha levels by numeric coefficients. In the more complex situation, users may specify an entire matrix of twenty coefficients (4 by 5) to be multiplied by the one-by-four vector representing the color value of a given pixel. In simpler situations, predefined matrices have been associated with special flags (such as “saturate”, “hueRotate”, or “luminanceToAlpha”) meaning one may simply specify one of the flags to perform the indicated operation.

 

 

feColorMatrix type=”saturate”

filtersaturate1

filtersaturate2

Original Image

Filtered image

 

 

 

<image x="25%" y="0" width="25%" height="35%" xlink:href="p2.jpg"/>

<filter id="F">

 <feColorMatrix type="saturate"/>

</filter>

 

<image x="50%" y="0" width="25%" height="35%" xlink:href="p2.jpg" filter="url(#F)"/>

 

An animated version that plays with the degree of saturation (through the "values" attribute) may be seen at this location.

 

Below are the results of several experiments with type=”matrix” in which we may specify our own matrix to be multiplied by the pixel values of the image. We begin by observing that multiplying the identity matrix (in which values [i,i]=1):

1 0 0 0 0

0 1 0 0 0

0 0 1 0 0

0 0 0 1 0

 

by an image would result in no change whatever to it.

 

The rows of the matrix represent respectively R,G,B, and alpha, so for example, in the second image we see that the alpha channel is being positively influenced by red, blue and green, while each of those colors negatively influences itself. The result is much like a black and white negative with transparency being maximized where the original image is brightest. The range of effects presented should allow the reader, with some experimentation of her own, to get a feel for how these matrix transformations work.

 


 

feColorMatrix type=”saturate”

Original image

 

 

 

 

filterMatrix2

<feColorMatrix type="matrix"

values="-1 0  0  0  0  

        0 -1  0  0  0

        0  0 -1  0  0  

        1  1  1  0  0

"/><!—inverse hi-contrast B/W w alphaà

<feColorMatrix type="matrix"

values="-1 3  3  0 -.5   

         0 0  0  0   0  

         0 0  0  0   0  

         0 0  0  1   0

"/><!—hyperredà

<feColorMatrix type="matrix"

values=" 1.5 -0.25 -0.25   0  0  

        -0.25  1.5  -0.25  0  0  

        -0.25 -0.25  1.5   0  0   

          0     0     0    1  0

"/><!—oversaturateà


<feColorMatrix type="matrix"

values="3  -1 -1  0 0  

        -1  3 -1  0 0   

        -1 -1  3  0 0   

         0  0  0  1 0

"/><!—supersaturateà

 

The values of the matrix above do not need to be typeset as they are. They could be specified simply as a space delimited string. The above format helps with legibility for both author and reader. An example on the web with these matrices applied to a base image can be seen at this location.

 

feConvolveMatrix

 

This filter allows what in image processing is known as a convolution filter. It allows us to define a square matrix (typically n by n for some odd number n) in which the center cell of the matrix refers to the pixel itself, and the cells above, left, below and right of it within the matrix, refer to the pixels above, left, below, and to the right of that pixel in the source image. The numeric coefficients in the matrix define the weight that each neighboring pixel will have in the calculation of the new color value of that pixel. In the simplest case, the matrix

 

0 0 0

0 1 0

0 0 0

 

leaves any image unaffected, since the new value of a pixel will be equal to 1 times its current value plus the sum of zero times the values of its eight nearest neighbors (those immediately N, NE, E, SE, S, SW, W, and NW of it).

 

A convolution matrix is defined as the value of the attributes kernelMatrix within an <feConvolveMatrix> as follows:

 

<filter id="edge">

       <feConvolveMatrix order="3"

       kernelMatrix="      

 -1  -1  -1

               -1   7  -1

               -1  -1  -1

" />

</filter>

 

<image id="M4" x="465" xlink:href="p17.jpg"

width="150" height="175"

filter="url(#edge)" />

 

We might expect the above to exaggerate those pixels that are very different from their neighbors, since each pixels neighboring pixels are weighted negatively.

 

The convolution matrix specified by

 

       kernelMatrix="      

              -1 -1 -1 -1 -1 -1 -1

               0  0  0  0  0  0  0

               0  0  0  0  0  0  0

               2  2  2  3  2  2  2

               0  0  0  0  0  0  0

               0  0  0  0  0  0  0

              -1 -1 -1 -1 -1 -1 -1

 "

 

will have the effect of striping an image horizontally (akin to applying a horizontal blur). That is because a pixel is averaged with all the pixels, within radius two, that are at the same height. The pixel itself has only a bit more weight than its horizontal neighbors. Likewise the fact that we have chosen to degrade pixels based on similarity to those some vertical distance away, means that we will tend to sharpen our horizontal edges, a bit, since those are where differences between regions are most pronounced and where pixel values will tend to be exaggerated relative to neighbors. To see the effect of these striping convolutions, let us apply both a predominantly horizontal and a predominantly vertical striping effect to the small grained fill pattern, url(#Oval), developed in the last section.

 

Matrices for vertical and horizontal striping

<ellipse fill="url(#Oval)" cx="50%" cy="50%" rx="10%" ry="10%"/>

 

blurMatrix2

The original image

<filter id="vstripe">

       <feConvolveMatrix order="7"

       kernelMatrix="      

              -1 0 0 2 0 0 -1

              -1 0 0 2 0 0 -1

              -1 0 0 2 0 0 -1

              -1 0 0 1 0 0 -1

              -1 0 0 2 0 0 -1

              -1 0 0 2 0 0 -1

              -1 0 0 2 0 0 -1

 " />

</filter>

blurMatrix1

Vertical striping

<filter id="hstripe" x="0" y="0" width="1" height="1">

       <feConvolveMatrix order="7"

       kernelMatrix="      

              -1 -1 -1 -1 -1 -1 -1

               0  0  0  0  0  0  0

               0  0  0  0  0  0  0

               2  2  2  3  2  2  2

               0  0  0  0  0  0  0

               0  0  0  0  0  0  0

              -1 -1 -1 -1 -1 -1 -1

 "/>

</filter>

blurMatrix3

Horizontal striping

 

The above can also be viewed on the web.

 

Lastly, are a series of other matrices performing a variety of image manipulations, to show not only some of the sorts of manipulations possible, but also to give some insights into how these convolutions work.

 

Different kernel matrices for the feConvolveMatrix primitive

filterConvolve

<image id="M4" x="155" xlink:href=
"p17.jpg" width="150" height="175"/>

kernelMatrix=
"

   1  -1   1

  -1  -1  -1

   1  -1   1
"

kernelMatrix=
"

  1   -1   1 

 -1  -.1  -1

  1   -1   1
"

kernelMatrix=
"

  -1  -1  -1

  -1  7  -1

  -1  -1  -1
"

kernelMatrix="

1  1   1  1  1

1 -2  -2 -2  1

1 -2 .01 -2  1

1 -2  -2 -2  1

1  1   1  1  1

"

 

Variants of these, as well as some using animation (and compound filters) may be seen at this location.

 

The National Institute of Health has for many years provided a freeware package known as NIH Image which does a variety of interesting image analytic operations including convolutions. The National Institute of Standards provides some informative reading on convolution filters and image processing in general.[4]

 

feComponentTransfer

 

The <feComponentTransfer> primitive allows the independent redefinition of each of the four color channels: R,G, B, and A (alpha). It allows the adjustment of brightness and contrast through application of any of a variety of different functions to any or all channels of an image. The types of adjustment allowed include identity,  table, discrete, linear, and gamma. Discrete can be used to posterize an image (that is to reduce it to fewer color values). Linear is used for simple brightening and darkening (or contrast adjustment) while table can be used to remap the function (like discrete) only continuously.

 

 

Some uses of <feComponentTransfer>

The unfiltered image.

 

<image  x="5%" y="2%" height="25%" width="20%" xlink:href="p84.jpg"/>

fliterComponentTransfer1

<filter id="inverse">

   <feComponentTransfer>

       <feFuncR type="table" tableValues="1 0"/>

       <feFuncG type="table" tableValues="1 0"/>

       <feFuncB type="table" tableValues="1 0"/>

   </feComponentTransfer>

</filter>

filterComponentTransfer2

Here, with type=’table’ we invert the chromatic range. We take the normal range from 0 to 1 and map to a new distribution: 0à 1 and 1à 0, and all in between, for each of the three channels of the image.

<filter id="discrete">

  <feGaussianBlur stdDeviation="1.5" />

  <feComponentTransfer>

    <feFuncR type="discrete" tableValues="0 .5 1 1"/>

    <feFuncG type="discrete" tableValues="0 .5 1"/>

    <feFuncB type="discrete" tableValues="0"/>

  </feComponentTransfer>

</filter>

filterComponentTransfer3

This option maps the red values in the interval [0,1] to one of the three values as follows:

(0 to .25) à 0; (.25 to .50) à .5; (.50 to .75 and.75 to 1.0) à 1.

The green channel is mapped to either 0%, 50% or 100% green with the threshold between these levels being chosen halfway between the endpoints. The blue channel (relatively insignificant in this particular image) is dampened to black (removing its effect altogether). The source image has no alpha channel (i.e., it is everywhere opaque), hence there is no need to modify that channel.

 

 

An example of type=”linear” is displayed in the section on <feTurbulence> a bit later in this chapter.

 

 

feMorphology

 

Among these filter primitives that take a simple input from a drawn object and produce a visible result effect is <feMorphology>. It is a rather simple effect, having just two parameters controlling the type and magnitude of the effect. The W3C has this to say about <feMorphology>: “This filter primitive performs ‘fattening’ or ‘thinning’ of artwork. A web-based example showing the effect of animating feMorphology can be seen at this link (which runs rather slow in Opera). The filter is particularly useful for fattening or thinning an alpha channel.”[5] A yet fancier version illustrating that may be seen here.

 

Filter primitives that stand alone

 

A few interesting filters exist which do not necessarily receive input, per se, but rather can create imagery by themselves. They are most commonly used in conjunction with other filters but can be most handy when it comes to building imagery or in processing of other images. The most important of these are feFlood, feTile, feTurbulence, feDiffuseLighting and feSpecularLighting.

 

feFlood

 

<feFlood> gives a new way of drawing a rectangle on the screen. The difference between it and other rectangles is that it can easily be combined on-the-fly with a variety of other filters as will be demonstrated shortly. In the following example, an <feFlood> primitive is applied to each of three objects: two rectangles and an ellipse. The geometry here is worth describing in some detail so that we might be able to make some sense of the relative versus        absolute coordinates so often used within SVG.

 

<feFlood> applied to three shapes

filterflood

<filter id="F">

       <feFlood x="50%" y="150" width="40" height="20" flood-color="grey" flood-opacity=".5"/>

</filter>

<rect x="100" y="100" width="100" height="100" filter="url(#F)" />

<rect x="200" y="100" width="100" height="100" filter="url(#F)" />

<ellipse cx="350" cy="150" rx="30" ry="50" filter="url(#F)" />

 

Observe, in the figure above, that the <feFlood> consists of a small grey rectangle. The “x” attribute is set as 50% while the other attributes are all in absolute coordinates. When the filter is applied to each of three shapes, the rectangle begins halfway from the left edge of each. Note that the filter’s rectangle is not clipped to the shape of the ellipse it is applied to. In fact, like other stand-alone operators, <feFlood> is applied to a rectangle that coincides with the filter space – either inherited from the object (as in this case), or as applied through the attributes, x, y, width, and height in the filter tag itself.

 

A simple working web example that works in IE, FF and Opera, may be seen here.

 

The feFlood becomes considerably more interesting when combined with feTile – a process by which patterns (much like the <pattern> object but without actually being rendered) may be created and stored away for subsequent use by other filters.

 

 

feImage

 

Just as feFlood allows the introduction of a colored rectangle into a filter, feImage allows the introduction of a rectangular bitmap into a filter. If for example, we wished to let each of several rectangles overlay the same bitmapped graphic, then we might filter each of those rectangles with a filter that contains the feImage primitive. <feImage> involves no parameters; it simply inserts an external image file into a filter processing stream.

 

Because it is difficult to use the <feImage> construct without the use of multiple inputs to a filter, an example will be given under our discussion of <feMerge> later in this chapter.

 

 

feTurbulence

 

<feTurbulence> is used to create textures. It creates patterns of smooth visual noise that fill a rectangle with rather pleasant swirls of pastel coloration. From the  W3C’s SVG 1.1 specification, we find that it

 

“creates an image using the Perlin turbulence function. It allows the synthesis of artificial textures like clouds or marble.”[6]

 

Like <feFlood>, <feTurbulence> fills a rectangle with new content. It has one required parameter baseFrequency and a variety of optional parameters as well. In the simplest case the primitive is used as follows (with an example here on the web):

 

<filter id="T1">

       <feTurbulence baseFrequency=".04"/>

</filter>

<rect x="30" y="10" height="100" width="100" filter="url(#T1)"/>

 

A more fully populated example of the syntax of the primitive may be seen here:

 

<filter id="T10">

<feTurbulence baseFrequency=".01" type="fractalNoise" numOctaves="3" seed=”23” stitchTiles=”stitch” />

</filter>

 

By varying the values of the parameters baseFrequency, numOctaves (which by default is 1.0), type (which by default is ‘turbulence’), stitchTiles (‘noStitch’ by default) and seed (‘0’ by default), we can produce numerous interesting types of pattern as shown in the following diagram (seen here on the web).

 

 

Various stand-alone uses of <feTurbulence>

turbulence1

 

ßEffects of seed and numOctaves

 

 

 

 

ßEffects of numOctaves and baseFrequency

 

 

 

ßEffects of

type and baseFrequency

A.
<feTurbulence baseFrequency

= ".04"/>

B.
<feTurbulence baseFrequency
= ".04"

numOctaves="2"/>

C.
<feTurbulence baseFrequency = ".04"
numOctaves="2" seed="201"/>

D.
<feTurbulence baseFrequency
= ".04"
numOctaves="5"

seed="201"/>

 

E.
<feTurbulence baseFrequency
= ".04"/>

F.
<feTurbulence baseFrequency
= ".01"/>

G.
<feTurbulence baseFrequency
= ".1" numOctaves="1"

H.
<feTurbulence baseFrequency
= ".1" numOctaves="3" />

I.
<feTurbulence baseFrequency
= ".04"
type = "fractalNoise"/>

J.

<feTurbulence baseFrequency
= ".01"
type = "fractalNoise" numOctaves = "3"/>

 

K.
<feTurbulence baseFrequency
= ".04,.1" />

L.
<feTurbulence baseFrequency
= ".1,.01" />

 

numOctaves

 

In the above examples, note that as we move across the first row from A through D, we vary the numOctaves and also the seed. As numOctaves grows from the default value of 1.0 to 2, and finally to 5, the grain of the pattern becomes tighter and its fractal complexity appears to increase.

 

seed

 

The purpose of seed is to provide a different start position for the random number generator underlying the function. Note that as we move from cell A to either cell B or cell E, the transition is gradual across the cell boundary. That is, the function is continuous across these areas of the table seeded with the same random number. As we move across the boundary from B to C, where the seed changes, the function no longer appears to be continuous, though continuity is preserved (even despite the octave change) across the boundary between C and D.

 

baseFrequency

 

The second row investigates changes in baseFrequency, which is sort of like a scaling variable affecting the size of the associated patterns. If we were to change baseFrequency through a SMIL animation (discussed later) we would see the overall pattern remain intact as it expands and moves away from the origin. Cell F has the largest grained pattern of these shown, with a baseFrequency value less than the others. baseFrequency controls, primarily, the size of the grain of the distortion map. Notice that cells G and H which share seed and baseFrequency values, but differ in numOctaves still appear to be continuous across the G/H boundary.

 

In cells K and L, we observe that we may specify different values of baseFrequency for the horizontal and vertical directions,

 

baseFrequency= ".1,.01"

 

imparting a directional grain to the pattern. This can come in quite handy in uses of feTurbulence in conjunction with other effects, in creating striation as part of our textures. The effect of animating baseFrequency through SMIL can be seen on the web (one will note that I have also synchronized the animation of seed with that of baseFrequency such that every oscillation of baseFrequency reveals a new randomization of the turbulence map.

 

type

 

There are two values of type in the SVG 1.1 specification: type=”turbulence” (the default) and type=”fractalNoise”. In cells I and J we look at the effect of type=”fractalNoise”. The other ten cells all use the default value.

 

<feTurbulence> used in conjunction with other filters can yield a broad range of  quite interesting effects. We will discuss the chaining together and composition of multiple filters shortly, but here are the combined effects of turbulence with saturation (using feColorMatrix) and sharpening (using feConvolveMatrix).

 

 

Effects of sharpening with or without color adjustment

turbulence2

Not sharpened

 

 

 

 

 

 

Sharpened with
<feConvolveMatrix order="3"

  kernelMatrix="       1   -1   1

       -1  -.1  -1

        1   -1   1    
  "
/>

Not color-adjusted

Super-saturated

Unsaturated

 

 

Enhancing the contrast of an <feTurbulence> plot:

 

<filter id="heavycloud">

  <feTurbulence baseFrequency=".01" numOctaves="3" seed="200"/>

  <feComponentTransfer>

   <feFuncR type="linear" slope="4" intercept="-1"/>

   <feFuncG type="linear" slope="4" intercept="-1"/>

   <feFuncB type="linear" slope="4" intercept="-1"/>

   <feFuncA type="linear" slope="0" intercept="1"/>

  </feComponentTransfer>

  <feColorMatrix type="saturate" result="A"/>

</filter>

filterComponentTransfer4

Here we start with turbulence and then use a linear function to exaggerate the slope of the color values for all three channels, other than alpha, which we dampen out by letting opacity become 1.0 everywhere.

 

Herewith two more samplings of various combined effects that involve feTurbulence.

 

Various effects involving feTurbulence

Text and oval distorted using feDisplacement applied to feTurbulence.

TurbWarped

Several copies of an image distorted using feDisplacement applied to feTurbulence.

turbWarped1

Application of feTurbulence to an image, through a mask.

turbulence3

 

Various textural effects

turbulence4

A –
baseFrequency = ".23" with radialGradient overlay

B –

baseFrequency = ".007,.25" with feFlood (to add light brown) and feColorMatrix (to add red tones

C –
baseFrequency = ".15" with feDisplacementMap

Base rectangle with black stroke is heavily perturned. Decorative overlays of other rectangles are also used.

D –

baseFrequency = ".2" streaked with <feConvolveMatrix> and with radialGradient overlay

E. –

Same as B, but with slightly different colors and secondary feTurbulence for finer-grained distortion

F –

baseFrequency=".2,.4"

with feFlood and feColorMatrix and secondary turbulence

 Much work using feTurbulence to create textures can be seen at this location. The SVG community is encouraged to suggest improvements to any of these or to suggest new categories of challenge.

 

feDiffuseLighting and feSpecularLighting

 

Numerous techniques exist within SVG for designing and controlling the placement of light sources. Many of these effects can be simulated through the overlay of partly transparent gradients, but the effects are powerful and quite useful for those who already know something of the landscape of lighting effects. Usually one will want to combine these effects using the various methods for combining multiple filter effects discussed later, but illustrated here is one very example of the use of feSpecularLighting to create an image. A more complex example (using feSpecularLighting to simulate the appearance of spotlights) can be see at this location.

 

 

Placing an <fePointLight> on a black background

Specular

<filter id = "I">

       <feSpecularLighting specularExponent="25" lighting-color="white">

              <fePointLight x="400" y="100" z="100"/>

       </feSpecularLighting>

</filter>

<rect fill="black" width="100%" height="100%" />

<rect filter="url(#I)" width="100%" height="100%" />

 

Utility filters (feTile and feOffset)

 

Two filter primitives which neither operate on raw objects, nor produce stand-alone imagery are discussed a bit separately since they can sometime provide useful results.

 

 

feTile

 

<feFlood> is to <rect> as <feImage> is to <image>.  Likewise, <feTile> is directly akin to <pattern>. It allows us to bring repeating patterns of imagery into the filter apparatus, so that we might then use it to create effects. Herewith is a simple use of the <feTile> primitive:

 

Building tiled layers within a filter using <feTile>

feTile

<rect x="0" y="0" height="100%" width="100%" fill="purple"/>

<filter id="T" filterUnits="objectBoundingBox" x="0" y="0" height="100%" width="100%" >

  <feImage xlink:href="p17.jpg" result="one"

              x="0" y="15" width="50" height="50"/>

  <feImage xlink:href="p84.jpg" result="two"

              x="25" y="0" width="50" height="50"/>

  <feFlood x="25" y="18" width="50" height="10" flood-color="yellow"
flood-opacity=".5" result="three" />

  <feFlood x="45" y="0" width="10" height="70" flood-color="cyan" result="Z"/>

       <feMerge>

              <feMergeNode in="Z"/>

              <feMergeNode in="one"/>

              <feMergeNode in="two"/>

              <feMergeNode in="three"/>

       </feMerge>

       <feTile/>

</filter>

<rect x="0" y="0" height="40%" width="50%" filter="url(#T)" />

 

As can be seen from the above, <feTile> merely takes the imagery that exists within the filter and fills the filter space with it.

 

feOffset

 

This is used to move a chunk of imagery, typically the SourceGraphic or the BackgroundImage, around a bit within a filter for purposes of slight realignment. The following drop shadow result is accomplished with <feOffset>. We proceed by taking in an image and applying a blur filter. That blurred image is then offset (20 pixels to the right and 15 pixels down) and stored as result “B”. Result B is then merged under the original SourceGraphic to create the effect. The reader may view the example here, on the web.


 

<feOffset> to create a blurred drop shadow.

feoffset1

<filter id="offset"  x="-20%" y="-10%" height="130%" width="140%">

       <feGaussianBlur stdDeviation="10"/>

       <feOffset dx="20" dy="15" result="B"/>

       <feMerge>

              <feMergeNode in="B"/>

              <feMergeNode in="SourceGraphic"/>

       </feMerge>

</filter>

 

 

Combining Filter Primitives

 

There are a variety of ways of combining filter primitives. One way is to apply one filter to an object, then nest the object within a <g> which has its own filter applied.

 

<filter id="F1">

       <someFilterPrimitive1/>

</filter>

<filter id="F2">

       <someFilterPrimitive2/>

</filter>

 

<g filter="url(#F2)">

<rect height="20%" width="15%" filter="url(#F1)"/>

</g>

 

An equivalent result can be obtained by chaining filter primitives together within a single <filter> element as follows:

 

<filter id="Fs">

       <someFilterPrimitive1/>

       <someFilterPrimitive2/>

</filter>

<rect height="20%" width="15%" filter="url(#Fs)"/>

 

In the above, we assume that filterPrimitive1 was the essence of #F1, while filterPrimitive2 was the essence of #F2. The latter approach is likely to be more efficient time-wise because it withholds any rendering while processing is still taking place. The run-time behavior of these filters can be a serious consideration, since some of the filters we have discussed in this section take on the order of seconds, rather than milliseconds to perform, at least on contemporary machines.

 

In addition to being able to sequentially chain together the results of different filter primitives, where each successive filter takes the output of the preceding filter (known as its “result”) as its input (known as its “in”) it is also possible to combine filters in more complex orders.

 

First, consider the default way in which filters handle multiple effects. Ordinarily, the first primitive within a <filter> receives, as input, the “SourceGraphic” – the element to which the filter has been applied. For example, if we define

 

<rect filter=”url(#Fs)” … />

 

then it is that rectangle that is considered to be the SourceGraphic of the filter “Fs.” Each primitive in succession (FP1, FP2, …FPk), takes the output or “result” from the previous filter as if it were its input. We show two equivalent approaches the first which just uses default values of the in and result of successive filters, while the second makes all those default values explicit. There would be no reason to specify the values of in or result in the following example, but the example may help make it clear what is meant by the in and the result of a filter. In both cases, it is the final filter, from which the output is rendered into the affected graphical objects.

 

Two equivalent approaches to sequential multi-filter processing

<filter id="Fs">

       <FP1/>

<FP2/>

<FP3/>

<FP4/>

<FP5/>

</filter>

<filter id="Fs">

       <FP1 in=”SourceGraphic” result=”A”/>

<FP2 in=”A” result =”B” />

<FP3 in=”B” result =”C” />

<FP4 in=”C” result =”D” />

<FP5 in=”D” />

</filter>

In the above, FPx refers to any filter primitive (such as feGaussianBlur, etc.)

 

Once we know where the SourceGraphic enters into the computations and how results are named and reused, then we are in a position to start varying the order and using those more complex filter primitives that combine results of two or more primitives, hence chaining filter primitives together in more complex and interesting ways.

 

SVG also gives access to the graphical content underneath a given image. That is, the state of the rendered imagery in the layer below the filtered object may itself be used as a part of the filter. This allows combinations of an image with its background using techniques for combining two images: feMerge, feBlend, feComposite, and feDisplacementMap. The use of BackgroundImage to do this will be revisited shortly.

 

The following are filters which operate on two or more images, or which utilize as input, the output of other filters. We’ll start with the simpler ones and move on from there.

 

feMerge

 

The feMerge filter allows the combination of filters concurrently, rather than serially (as in the earlier examples). Rather than each filter being applied to the output of the preceding filter, feMerge gives us a way to temporarily store the output of each filter. Once several layers have been created and stored as the results of different primitives, then they may be placed on the canvas in order from bottom to top. Topmost layers should have some transparency (or incompleteness) in the fill area, so as to allow those layers underneath to be visible.[7]

 

In the following example, we are interested in converting an image from standard RGB to partial transparency, in this case using the darkest parts of the image, so that an underlying color shines through. In this case, ‘yellow’ created as a part of the filter, is used.

 

 

Using <feMerge> to combine <feFlood> with <feColorMatrix> applied to <image>

feMerge1

<filter id="twoF" x="0%" y="0%" width="100%" height="100%">

       <feFlood  flood-color="yellow" result="A"/>

       <feColorMatrix type="matrix" in="SourceGraphic" result="B"

       values=

               "1   0  0  0 0  

                0   1  0  0 0   

                0   0  1  0 0   

                1   1  1  0 0

       "/>

       <feMerge>

              <feMergeNode in="A"/>

              <feMergeNode in="B"/>

       </feMerge>

</filter>

 

<image x="35%" y="20%" xlink:href="p84.jpg" filter="url(#twoF)" height="50%" width="30%"/>

 

What we have done above, is to create a yellow rectangle with the <feFlood>. We then store it temporarily in the variable “A”. Next we use the <feColorMatrix> to operate on the SourceGraphic (the JPEG image). In the top three rows of the color matrix, we preserve the RG and B channels but in the last, or alpha, row, we let positive values in any of the three channels contribute positively to the alpha channel, hence creating transparency in the darker parts of the image. This interim result is labeled “B.” We then rebuild the image by laying down the interim results: first A, then atop that B. Since B is now partially transparent we may see the yellow, underneath.

 

<feMerge> allows for any number of <feMergeNode>s to be inserted into the filter, so that we may build rather complex objects with it.

 

The reader may realize that there are at least two other ways of accomplishing the above effect. One is to simply build a yellow <rect>, under the <image>.

 

Build <rect> under <image>

<filter id="twoD">

       <feColorMatrix type="matrix"

       values="1   0  0  0 0  

               0   1  0  0 0   

               0   0  1  0 0   

               1   1  1  0 0"/>

</filter>

<rect x="3%" y="20%" height="50%" width="30%" fill="yellow"/>

<image x="3%" y="20%" xlink:href="p80.jpg" filter="url(#twoD)" height="50%" width="30%"/>

 

The advantage of the <feMerge> approach show earlier, is that it is portable. We may apply this filter to any image we wish to without having to build a <rect> underneath it. If we wish to add or withdraw such an effect dynamically, it will be much cleaner since the effect (including its color) is entirely self-contained.

 

In this web-based version, I have succeeded in getting the feFlood merged with the feColor/matrix to work in Opera, though it works as expected in IE/ASV..

 

Another approach is to build a <rect> on top of the image and then using the BackgroundImage (as discussed momentarily) to “swap” the order of the two images within the filter.

 

Here’s another example of the use of <feMerge>, this time in conjunction with <feImage>.

 

Bringing a bitmap into a filter with <feImage>

feImage

<filter id="I2" x="0" y="0" height="100%" width="100%">

<feImage xlink:href='p84.jpg' result="A"/>

<feColorMatrix type="matrix" result="B" in="SourceGraphic"

   values="1 0 0 0 0 , 0 1 0 0 0 , 0 0 1 0 0 , 1 1 1 0 0"/>

<feMerge>

   <feMergeNode in="A"/>

   <feMergeNode in="B"/>

</feMerge></filter>

 

In the above, a particular image is brought in through an <feImage> and laid down at the beginning of the <feMerge>.While effects such as seen above could probably be accomplished through other means (such as <mask> with transparency), the ability to bring an image directly into the processing stream is certainly convenient.

 

 

Filtering graphics along with their backgrounds

 

By increasing the transparency of all or part of an SVG graphic, we allow whatever is underneath it (its background) to become at least partly visible. However the opacity of the top layer does not really allow what is underneath it to interact with it in any substantial way. Though background content may be visible, it is not available for modification or use within the current filter when it is viewed only through the transparency of what is atop it. Just as we may refer to the SourceGraphic as the object to which the filter has been applied, we may also refer to the BackgroundImage as whatever happens to be behind it.

 

In order to allow the content of BackgroundImage to be made available to a filter, a container (a <g> or a <use>) that contains both the SourceGraphic and any underlying elements desired to be filtered with it must be instructed to make that background content available to the filter through setting its enable-background attribute to “new”:

 

<g enable-background="new">

 

An illustration using <feMerge> may demonstrate how this can prove useful. We will apply a Gaussian blur to a source image, and a slightly different blur to its background to see how this allows filters to simultaneous manipulate two images with a single filter.

 

Accessing and using BackgroundImage in a filter

BackgroundImage

Left: making use of BackgroundImage within a filter with feMerge

Right: the same filter without merging of BackgroundImage

<filter id="BI" >

  <feGaussianBlur stdDeviation="35" />

  <feComponentTransfer result="A">

    <feFuncA type="linear" slope="1.1" intercept="0"/>

  </feComponentTransfer>

  <feGaussianBlur in="BackgroundImage" stdDeviation="20,1" result="B"/>

  <feMerge>

    <feMergeNode in="A"/>

    <feMergeNode in="B"/>

  </feMerge>

</filter>

<filter id="SI" >

  <feGaussianBlur in="BackgroundImage" stdDeviation="20,1"/>

  <feGaussianBlur in="SourceGraphic" stdDeviation="35" />

  <feComponentTransfer >

    <feFuncA type="linear" slope="1.1" intercept="0"/>

  </feComponentTransfer>

</filter>

The two SourceGraphics and the three underlying horizontal
stripes  that constitute the BackgroundImage

<g enable-background="new">

<rect x="0" y="12%" height="4%" width="100%" fill="grey"/>

<rect x="0" y="22%" height="4%" width="100%" fill="grey"/>

<rect x="0" y="32%" height="4%" width="100%" fill="grey"/>

<rect x="11%" y="10%"  height="30%" width="18%" filter="url(#BI)" fill="white"/>

<rect x="38%" y="10%"  height="30%" width="18%" filter="url(#SI)" fill="white"/>

</g>

In the above illustration, the three vertical stripes, though lying below the SourceGraphic (the blurred white rectangle) have not been included in the <g> that has enable-background turned on, hence they are not affected by the filter #BI. Note how the three horizontal stripes in the left image (constituting BackgroundImage) have been horizontally blurred and have been layered atop the SourceGraphic. At right, though the BackgroundImage has been made accessible to the filter and a temporary image of it after horizontal blurring has been made, that result has not been shared with the feMerge and hence is discarded prior to rendering.

 

feBlend

 

<feBlend> is used to blend, or mix two images together with a variety of simple methods (i.e., values of the mode attribute): "normal","screen","multiply","lighten", and "darken."

 

FeBlend applied to BackgroundImage using, from left to right, modes "normal","screen","multiply","lighten", and "darken."

feBlend

<filter id="normal">

       <feBlend mode="normal" in2="BackgroundImage" in="SourceGraphic"/>

</filter>

 

<feBlend> receives input from two sources rather than just one, allowing it to take results from other filters as well as SourceGraphic (the default[8]) or BackgroundImage. As such it can combine quite sophisticated processes.

The values of its mode attribute are as they would be in a photo editing program like Adobe Photoshop ®. Stated informally, these modes work as follows:

 

·         normal – allows BackgroundImage (or other in2) to be visible only if SourceGraphic (or other in) contains transparency.

·         screen – allows each image’s values to add brightness to the other.

white screen black = white and
red (#FF0000) screen grey (#808080) = #ff8080 (“rose” or a rose-like color)

·         multiply – allows values of the images to subtract brightness from one another

white mult black = black and
red (#FF0000) mult grey (#808080) = #800000 (a shade of red darker than “darkred”)

 

·         lighten – takes the brighter value of the two images at each pixel

white lighten black = white and
red (#FF0000) lighten grey (#808080) = #ff8080 (“rose”)

 

·         darken – takes the darker value of the two images at each pixel.

white darken black = black and
red (#FF0000) darken grey (#808080) = #800000 (“darker red”)

An illustration of the above may be seen here.

 

 

feComposite

 

Neither <feMerge> nor <feBlend> presents us with a way to either average or intersect two images. <feComposite> can be used for that work. It allows the superimposition of the footprints of images as well as the relative blending of their pixel values. Like <feMerge> it takes two inputs in and in2. By default, in is the SourceGraphic.

 

A typical use would look like

 

<filter id="in">

       <feComposite in2="BackgroundImage" operator="in" />

</filter>

 

The operator attribute takes values of "in", “over","out","atop","xor", and "arithmetic". All of these except “arithmetic” are simple attributes, but when “arithmetic” is specified, four other parameters are invoked: k1, k2, k3, and k4. These assign weights respectively to: a component representing the multiple of the two images, the linear effect of the first image, the linear effect of the second image, and an intercept or brightness adjustment. In the following illustration, when operator is artithmetic, then k1="0" k2="1" k3="-1" and k4="1", meaning that the SourceGraphic (in) contributes positively, the BackgroundImage (in2) contributes negatively and brightness has been boosted.

 

Illustration of four values of operator in <feComposite>

feComposite

 

Of the various operator values, “arithmetic” and “in”, probably are most useful. “Arithmetic” is useful since it allows percentage-based blending of two images (much like opacity) as well as more complex effects as shown above. “In” is useful since it constrains the presence of one image to the footprint of another, much like a clipPath, but done as a part of a filter stream. The above, together with some SMIL animation, can be seen at this location.

 

feDisplacementMap

 

This effect is a bit different from others in the sense that it converts pixel color values in one image into geometric distortions of another image.

 

<feDisplacmentMap> takes in (SourceGraphic by default) and in2, and uses a specified channel (R,G,B, or A) of in2 to serve as displacement values which determine the direction and distance each pixel of in will be moved in either the x or y (or both) direction.

 

For example, if we chose to use the Red channel of in2 to horizontally distort in,   and if the underlying image represented by in2 is say, a red and black checkerboard (high on Red on the red squares and low on Red on the black squares) then those pixels of in which lie above red squares will be moved to the right, while those above black squares will be moved to the left.

 

Using a checkerboard to displace parts of an image.

feDisplace5

<filter id="d" x="0%" y="0%" height="100%" width="100%">

   <feDisplacementMap scale="100" in2="BackgroundImage" xChannelSelector="R"/>

</filter>

<g enable-background="new">

   <rect x="24%" y="16%" height="66%" width="52%" fill="url(#Pattern)"/>

   <image filter='url(#d)' xlink:href="p17.jpg"
     x="34%" y="22%" width="35%" height="50%" />

</g>

 

In the illustration above, we signify that we wish to use the Red channel and that we wish it to be used for horizontal distortion, when we specify

xChannelSelector="R"   .

 

The scale attribute (100 in this case) determines the magnitude of the distortion (100 pixels). A value of zero would mean that no displacement of pixels will occur.

 

Animated versions which animate the scale attribute (and hence the magnitude of the distortion) may be seen at this location.  or here.

 

In the above example, slight discoloration of the warped image occurs in IE/ASV3, though this is not true in the Opera browser. Firefox, as of this writing, does not manage <feDisplacementMap>. One way to circumvent the discoloration is to bring the image into the filter through an <feImage> as shown in the following:

 

<feDisplacementMap> as a part of a more complex filter

feDisplace6

<filter id="d" x="10%" y="-5%" height="100%" width="100%">

   <feImage xlink:href="p17.jpg" result="M" />

   <feDisplacementMap in="M" in2="BackgroundImage"
       scale="100" xChannelSelector="R"/>

</filter>

<rect id="screen" x="0%" y="0%" height="100%" width="100%" fill="black"/>

<g enable-background="new">

   <rect x="24%" y="16%" height="66%" width="52%" fill="url(#Pattern)"/>

   <rect filter='url(#d)'   x="250" y="150"  width="300" height="300"  />

</g>

The fact that we used both red squares and the red channel is really unimportant. Had we used white squares instead of red, the result would have been the same, since the Red channel will be just as strong atop white squares as atop white ones, since white, restricted to the Red channel is “#FF” which translates to 100%.

 

Clearly this filter primitive has lots of potential for creating interesting effects with various warping gradients. The following example uses an underlying reflected gradient.

 

A reflected radial gradient (left) and its use in <feDisplacementMap> (right)

feDisplace1

feDisplace2

 

 

We may use <feDisplacementMap> to define distortions that go beyond those allowed by the isometric spatial transformations: translate, rotate, scale, and skew. Based on continuous radial, and linear gradients, particularly as enhanced by <feTurbulence> it should be possible to build warps of almost any kind desired.


 

A few distortions (random and customized)

feDisplace7

Some text and an ellipse warped through <feDisplacement> by <feTurbulence>

 

fedisplace8

Some text and an ellipse warped through <feDisplacement> by a rotated linear gradient.

 

 

 

The IE and Opera renderings of the web version of the above, are curiously different with the animation failing in IE and the



[1] The W3C standards use the term “filter primitives” to refer to what many in the graphics community label “filters.” It is understandable why the distinction was made: to keep the <filter> tag separate from its children (the primitives). However, given the preexisting common parlance meaning of the term, I will sometimes use the term filter to refer to what is more technically speaking, a filter primitive. I do this since I think readers will be more comfortable with the meanings they may already associate with “filter.”

[2]  A perhaps tighter use of the term primitive may be seen here “The extraction of a minimum set of semantic primitives from a monolingual dictionary is NP-complete.” David P. Dailey. To appear in Readings in the Lexicon, edited by Yorick Wilks, MIT Press, in press 2007.

[3] The filter gets its name from the mathematician Carl Friedrich Gauss (1777-1855) who contributed much to the understanding of parametric statistics. The normal curve or bell curve is sometimes called a Gaussian distribution. Specifically it lets each pixel value in the new image, be determined not only by its own value, but by the value of its neighboring pixels with weights determined by a Gaussian curve.

[4] “Digital Image Processing with NIH Image (Mac) / Scion Image (PC) / ImageJ” by

David S. Bright, National Institute of Standards and Technology, May 2004 available at http://www.nist.gov/lispix/imlab/labs.html

[5] Scalable Vector Graphics (SVG) 1.1 Specification W3C Recommendation 14 January 2003; Chapter 15 Filter Effects, at http://www.w3.org/TR/SVG/filters.html

[6] Scalable Vector Graphics (SVG) 1.1 Specification W3C Recommendation 14 January 2003; Chapter 15 Filter Effects, at http://www.w3.org/TR/SVG/filters.html

[7] Should we wish for the layers to be combined not only concurrently, but also within the same layer, then we may consider either feBlend or feComposite, which offer richer ranges of possibilities.

[8] In the above example, the statement  in=“SourceGraphic” can be removed without changing the results.