# JT's Scribblings

a.k.a. a blog of some description.

## Shiny stuff with SVG, part 4: Primitives II

Sunday November 11th 2012, at 5:08 pm

Firstly, apologies to anyone who read the first four posts in this series expecting (as I did) that they would continue on a daily basis. This one is somewhat delayed as I've been kept busy with many other things. Also I had to figure out how to get equations onto my blog automatically without breaking everything.

Anyway, this post will cover a few more of the basic filter primitives, and then move onto how to combine everything from this and the last four posts to create some effects.

I apologise in advance for the fact that the description of `feColorMatrix` has a load of equations in it. It's not strictly necessary to know the exact technicality of how the various matrices are defined, but I've nonetheless included them for information's sake.

## feColorMatrix

The `feColorMatrix` primitive modifies the input image on a pixel-by-pixel basis according to a matrix transformation, which may seem a little daunting if you've never done anything vector-algebra related:

What this means, if you've never come across matrix multiplication before, is this:

The values for each pixel are measured between 0 and 1 for each of R, G, B and A (instead of 0 to 255 as is often used for colours).

There are few options available for either specifying the matrix entirely manually, component-by-component, or applying some simpler parameterized transforms. These are specified using the `type` attribute, and the various parameters are specified with the `values` parameter. The avaialable values for `type` are: `matrix`, `saturate`, `hueRotate` and `luminanceToAlpha`

### matrix

When using `type="matrix"` (the default if `type` isn't specified), the `values` attribute should just be a space-delimited list of the components of the matrix in left-to-right, top-to-botom order (a00, a01, a02, … a34). The transformation is then performed as simple matrix multiplication.

So, for example, to apply a very simple 240-degree hue rotation to the (rather disturbing) smiley face from the last post by mapping RGB to BRG:

``````<svg>
<defs>
<filter id="mat">
x="0" y="0" width="100" height="100" />
<feColorMatrix values="0 1 0 0 0
0 0 1 0 0
1 0 0 0 0
0 0 0 1 0" />
</filter>
</defs>
<rect x="0" y="0" width="100" height="100" filter="url(#mat)" />
</svg>
``````

If you compare this to the equations above, you'll see that what the above example is doing effectively reduces down to this:

Note that this is not the same result as you'd get from doing a 240-degree `hueRotate`. More on that in a minute.

Or, for example, we could invert all the colours:

``````<svg>
<defs>
<filter id="inv">
x="0" y="0" width="100" height="100" />
<feColorMatrix values="-1  0  0 0 1
0 -1  0 0 1
0  0 -1 0 1
0  0  0 1 0" />
</filter>
</defs>
<rect x="0" y="0" width="100" height="100" filter="url(#inv)" />
</svg>
``````

And this boils down to the following operation:

There are many more interesting colour transformations you can do with an explicitly-specified `feColorMatrix`; I have only covered the most simple possible examples here.

### saturate

With `type="saturate"`, the `values` attribute should be a single real number between 0 and 1. If we call this number s, the matrix operation performed is the following:

This transformation will completely desaturate the image if `values` is `0`, and leave it untouched if `values` is `1`. Although the SVG specification says `values` should lie between 0 and 1, most browsers allow you to set a value greater than 1 to over-saturate the input.

The significance of the various numbers in this matrix is to conform to the luminance calculation from the sRGB colour standard (see equation 1.8 near the end if you're interested), so the input data can be desaturated while preserving its luminance.

For example, to desaturate the smiley by 75%:

``````<svg>
<defs>
<filter id="sat">
x="0" y="0" width="100" height="100" />
<feColorMatrix type="saturate" values="0.25" />
</filter>
</defs>
<rect x="0" y="0" width="100" height="100" filter="url(#sat)" />
</svg>
``````

### hueRotate

The `hueRotate` operation is a little more complicated to represent exactly. The idea of it is to take the `values` attribute, which should be a single numeric value, and rotate the hue of every pixel by that number of degrees. However, the transformation it applies is a luminance-preserving transformation according to sRGB luminance, so (for example) the colour `#123456` will not be transformed to `#561234` by a 120-degree rotation.

For example, to apply a 100-degree hue rotation to the smiley:

``````
<svg>
<defs>
<filter id="hr">
x="0" y="0" width="100" height="100" />
<feColorMatrix type="hueRotate" values="100" />
</filter>
</defs>
<rect x="0" y="0" width="100" height="100" filter="url(#hr)" />
</svg>
``````

The exact definition is as follows:

Where, if θ is the numeric value of `values`,

### luminanceToAlpha

The `luminanceToAlpha` type takes the luminance of the red, green and blue channels and translates that to the value of the alpha channel (and puts `0` in all the RGB channels). This is particularly useful if you have one image that you want to use as an alpha mask for another (using the `feComposite` primitive with `operator="in"`). When used with this type, `feColorMatrix` pays no attention to the `values` attribute.

So, for example:

``````
<svg>
<defs>
<filter id="ltoa">
x="0" y="0" width="100" height="100" />
<feColorMatrix type="luminanceToAlpha" />
</filter>
</defs>
<rect x="0" y="0" width="100" height="100" filter="url(#ltoa)" />
</svg>
``````

You can see that the transparent pixels of the smiley (which have R, G, B and A values all 0) become transparent, the bright yellow area is mostly-opaque, and the black area is completely transparent.

One thing that is worth noting is that the alpha channel of the original image is completely ignored, so if you have transparent pixels in your image you will get different results depending on the RGB values of those pixels (i.e. RGBA(0, 0, 0, 0) and RGBA(255, 255, 255, 0) look the same, but the first will be converted to 100% transparent and the second will be converted to 100% opaque).

The exact definition of the matrix operation used is this:

## feGaussianBlur

The `feGaussianBlur` does exactly what you'd expect it to if you've ever come across Gaussian blurs in image editors or the like. It applies a Gaussian blur (if you haven't come across Gaussian blurs before, this is just a particularly-attractive looking blur calculation) with a standard deviation (blur amount) specified by the `stdDeviation` attribute, measured in pixels. For example:

``````<svg>
<defs>
<filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="10" />
</filter>
</defs>
<circle cx="50" cy="50" r="30" fill="#28d" filter="url(#blur)" />
</svg>
``````

It's worth noting that as a Gaussian blur extends beyond the boundaries of its input, you will most likely need to use the `x`, `y`, `width` and `height` attributes of the filter to extend its clipping rectangle.

The `stdDeviation` attribute can alternatively contain two components, in which case these are the deviations in the X and Y directions respectively. Using this you can achieve some interesting effects by only blurring in one direction:

``````<svg>
<defs>
<filter id="hblur" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="10 0" />
</filter>
</defs>
<rect x="25" y="25" width="50" height="50"
fill="#28d" filter="url(#hblur)" />
</svg>
``````

## feMorphology

The `feMorphology` primitive has the effect of either 'thinning' or 'thickening' the input. You can specify to either `dilate` or `erode` the input using the `operator` attribute, and supply an amount using the `radius` attribute.

For example, to dilate (expand) by 2 pixels:

``````<svg>
<defs>
<filter id="morph1">
<feMorphology operator="dilate" radius="2" />
</filter>
</defs>
<text x="10" y="50" filter="url(#morph1)"
font-size="30">Dilate</text>
</svg>
``````

Or, to erode (contract) by 1 pixel:

``````<svg>
<defs>
<filter id="morph2">
<feMorphology operator="erode" radius="1" />
</filter>
</defs>
<text x="10" y="50" filter="url(#morph2)" font-size="30">Erode</text>
</svg>
``````

It's worth noting that the shape that `feMorphology` uses to calculate the resulting image data is a rectangle rather than an ellipse, so for example if you apply a dilation to a circle the result will not be a larger circle, but will in fact be a square with rounded corners, thus:

``````<svg>
<defs>
<filter id="morph3" x="-50%" y="-50%" width="200%" height="200%">
<feMorphology operator="dilate" radius="20" />
</filter>
</defs>
<circle cx="50" cy="50" r="20" fill="#38d" filter="url(#morph3)" />
</svg>
``````

I may well write a further post with a little more about precisely how `feMorphology` calculates the output data. If I do, I'll update this post to include it.

## Putting it all together

Now that we've covered enough of the basic filter primitives, it's now possible to combine several of these to create a useful effect.

The first example is a box shadow. This example will be a bit exaggerated to demonstrate the method, but it's easily adapted to whatever parameters you need. The basic steps are thus:

1. Offset the input object (using `feOffset`)
• Create a fill of the desired colour and opacity (using `feFlood`)
• Clip the fill to the offset object (using `feComposite` with `operator="in"`)
• Apply a blur to the shadow (using `feGaussianBlur`)
• Compose the shadow behind the original image (using `feMerge`)

So here we go:

``````<svg>
<defs>
<filter id="shadow" x="-50%" y="-50%" width="200%" height="300%">
<feOffset dx="10" dy="10" in="SourceAlpha" result="offset" />
<feFlood flood-color="#000" flood-opacity="0.5" />
<feComposite in="offset" operator="in" />
<feGaussianBlur stdDeviation="5" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<rect x="10" y="10" width="60" height="40" fill="#ace"
rx="4" ry="4" stroke="#58a"
</svg>
``````

Creating an inner shadow is a little more complicated if the shape has a border, as you'll want the inner shadow to appear only inside that border. To achieve this you can use a `feMorphology` to erode the shape to just the area within the border bounds. This has its drawbacks though — if you have a large stroke width, the curvature of any part of the shape will be distorted slightly (due to the rectangular erosion kernel). For smallish borders though, this won't be noticeable.

One method for creating an inner shadow is thus:

1. Create an eroded version of the input, to represent only the area inside the srtoke (using `feMorphology`)
• Create an offset version of it (using `feOffset`)
• Blur the offset version (using `feGaussianBlur`)
• Compose the blurred offset image with the original eroded one using `feComposite` and `operator="out"` to get only those pixels outside the blurred version. This will be used as an alpha mask for the shadow
• Create a flood of the desired colour and opacity (using `feFlood`)
• Compose the flood with the mask (another `feComposite`)
• Blend the shadow with the original (`feBlend` with `mode="multiply"`)
``````<svg>
<defs>
result="eroded" in="SourceAlpha"/>
<feOffset dx="0" dy="5" in="eroded" />
<feGaussianBlur stdDeviation="3" />
<feComposite operator="out" in="eroded" result="mask" />
<feFlood flood-color="#222" flood-opacity="0.7"/>
<feComposite operator="in" in2="mask" />
<feBlend mode="multiply" in2="SourceGraphic" />
</filter>
</defs>
<rect x="10" y="10" width="60" height="40" fill="#ace"
rx="4" ry="4" stroke="#49f"
As you will no doubt have figured out by now, there are many ways of doing the same thing with filters. The method described above is just my particular way of creating an inner shadow in the simplest way I can think of, while demonstrating as many of the primitives covered so far as possible. An equivalent effect could be achieved, for example, by substituting the `feFlood` and second `feComposite` with `<feColorMatrix values="0 0 0 0 0.125 0 0 0 0 0.125 0 0 0 0 0.125 0 0 0 0.5 0" />`. There may well be an even simpler way than that. Go experiment, and see what you come up with!
The next post will be a little shorter than this one. I'll explain `feTurbulence` and how you can use it to create fractal noise and grain effects, plus a few more examples of combining pretty much everything covered so far to create some actually-useful effects, rather than very basic primitives.