feColorMatrix
The idea behind the feColorMatrix effect is to apply a
matrix transform to the Red, Green, Blue and Alpha channels
of each pixel in the source image. Using it will require at
least a basic understanding of matrix multiplication. I'll
give you a couple tips in case you're rusty, but for more
details have a look at
Wikipedia.
The matrix used for this effect looks like this (I've used
one-based indices like most matrix math, the spec uses
zero-based indices like many programming languages):
a11 a12 a13 a14 a15If the incoming pixel colours values are ir, ig, ib and ia then the resulting colour after the matrix is applied will be (qr,qg,qb,qa) with the components calculated as follows:
a21 a22 a23 a24 a25
a31 a32 a33 a34 a35
a41 a42 a43 a44 a45
qr = ir*a11 + ig*a12 + ib*a13 + ia*a14 + a15*1
qg = ir*a21 + ig*a22 + ib*a23 + ia*a24 + a25*1
qb = ir*a31 + ig*a32 + ib*a33 + ia*a34 + a35*1
qa = ir*a41 + ig*a42 + ib*a43 + ia*a44 + a45*1
The fifth column (a15, a25, ... a55) is provided for a
constant offset for each colour channel.
At the risk of losing a few readers, I'm going to give one
more quick tip on the matrix. As I show in the drop shadow
example, The matrix can be used to multiply each channel
independently by putting the multipliers in a11, a22, and a33
with a44 set to 1 and all the other matrix values set to
0
Colour Matrix Types
The feColorMatrix effect takes a type parameter which
allows us to specify the full matrix or a shorthand value
that will calculate the rest of the matrix values for us.
Setting the type to "matrix" gives us access to the entire
matrix as I've just described. The matrix coefficients are
given in row-major order in a string separated by white space
(including end of line) or commas. If you're bringing code in
from another source or text book and it doesn't behave, check
that coefficients are given row by row and not column by
column.
The following example isn't meant to be practical, just a
demonstration of the math. The leftmost shapes have an
identity matrix applied, which has no net effect. The center
image is made "more blue" by adding 1 to the blue channel of
the output. The rightmost image gets each colour channel from
the sum of half of the other two channels.
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 1200 600" version = "1.1">
<desc>
Filter example
</desc>
<filter id = "ident">
<feColorMatrix in = "SourceGraphic" type = "matrix" values = " 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0"/>
</filter>
<filter id = "bluer">
<feColorMatrix in = "SourceGraphic" type = "matrix" values = " 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0"/>
</filter>
<filter id = "halfRemix">
<feColorMatrix in = "SourceGraphic" type = "matrix" values = " 0.0 0.5 0.5 0.0 0.0 0.5 0.0 0.5 0.0 0.0 0.5 0.5 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0"/>
</filter>
<defs>
<rect id = "r1" x = "150" y = "50" width = "100" height = "100" stroke-width = "5" stroke = "blue" fill = "yellow"/>
<circle id = "c1" cx = "200" cy = "300" r = "50" stroke-width = "5" stroke = "green" fill = "orange"/>
</defs>
<g width = "100%" height = "100%">
<use xlink:href = "#r1" filter = "url(#ident)"/>
<use xlink:href = "#c1" filter = "url(#ident)"/>
<text font-family = "monospace" font-size = "30">
<tspan x = "30" y = "500">
1.0 0.0 0.0 0.0 0.0
</tspan>
<tspan x = "30" y = "525">
0.0 1.0 0.0 0.0 0.0
</tspan>
<tspan x = "30" y = "550">
0.0 0.0 1.0 0.0 0.0
</tspan>
<tspan x = "30" y = "575">
0.0 0.0 0.0 1.0 0.0
</tspan>
</text>
</g>
<g width = "100%" height = "100%" transform = "translate(400,0)">
<use xlink:href = "#r1" filter = "url(#bluer)"/>
<use xlink:href = "#c1" filter = "url(#bluer)"/>
<text font-family = "monospace" font-size = "30">
<tspan x = "30" y = "500">
1.0 0.0 0.0 0.0 0.0
</tspan>
<tspan x = "30" y = "525">
0.0 1.0 0.0 0.0 0.0
</tspan>
<tspan x = "30" y = "550">
0.0 0.0 1.0 0.0 1.0
</tspan>
<tspan x = "30" y = "575">
0.0 0.0 0.0 1.0 0.0
</tspan>
</text>
</g>
<g width = "100%" height = "100%" transform = "translate(800,0)">
<use xlink:href = "#r1" filter = "url(#halfRemix)"/>
<use xlink:href = "#c1" filter = "url(#halfRemix)"/>
<text font-family = "monospace" font-size = "30">
<tspan x = "30" y = "500">
0.0 0.5 0.5 0.0 0.0
</tspan>
<tspan x = "30" y = "525">
0.5 0.0 0.5 0.0 0.0
</tspan>
<tspan x = "30" y = "550">
0.5 0.5 0.0 0.0 0.0
</tspan>
<tspan x = "30" y = "575">
0.0 0.0 0.0 1.0 0.0
</tspan>
</text>
</g>
</svg>
For the shorthand types, I'll give an brief explanation
and example, but the w3c spec has the exact formulae.
A matrix type of "saturate" adjusts the saturation of the
colour using a real number value of 0 to 1 supplied as the
"values" attribute of feColorMatrix.
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 300 300" version = "1.1">
<desc>
Filter example
</desc>
<filter id = "sat1">
<feColorMatrix in = "SourceGraphic" type = "saturate" values = "0.1"/>
</filter>
<filter id = "sat2">
<feColorMatrix in = "SourceGraphic" type = "saturate" values = "0.5"/>
</filter>
<filter id = "sat3">
<feColorMatrix in = "SourceGraphic" type = "saturate" values = "0.9"/>
</filter>
<defs>
<circle id = "c1" cx = "75" cy = "40" r = "25" stroke-width = "5" stroke = "cadetblue" fill = "khaki"/>
</defs>
<g font-family = "sans-serif" font-size = "30" text-anchor = "middle">
<g>
<use xlink:href = "#c1"/>
<text>
<tspan x = "75" y = "100">
No filter
</tspan>
</text>
</g>
<g transform = "translate(150,0)">
<use xlink:href = "#c1" filter = "url(#sat1)"/>
<text>
<tspan x = "75" y = "100">
0.1
</tspan>
</text>
</g>
<g transform = "translate(0,150)">
<use xlink:href = "#c1" filter = "url(#sat2)"/>
<text>
<tspan x = "75" y = "100">
0.5
</tspan>
</text>
</g>
<g transform = "translate(150,150)">
<use xlink:href = "#c1" filter = "url(#sat3)"/>
<text>
<tspan x = "75" y = "100">
0.9
</tspan>
</text>
</g>
</g>
</svg>
The "hueRotate" type takes an angle (in degree) as the value of "values" and rotates the pixel hue by the given angle.
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 300 300" version = "1.1">
<desc>
Filter example
</desc>
<filter id = "hue1">
<feColorMatrix in = "SourceGraphic" type = "hueRotate" values = "30"/>
</filter>
<filter id = "hue2">
<feColorMatrix in = "SourceGraphic" type = "hueRotate" values = "90"/>
</filter>
<filter id = "hue3">
<feColorMatrix in = "SourceGraphic" type = "hueRotate" values = "270"/>
</filter>
<defs>
<circle id = "c1" cx = "75" cy = "40" r = "25" stroke-width = "5" stroke = "cadetblue" fill = "khaki"/>
</defs>
<g font-family = "sans-serif" font-size = "30" text-anchor = "middle">
<g>
<use xlink:href = "#c1"/>
<text>
<tspan x = "75" y = "100">
No filter
</tspan>
</text>
</g>
<g transform = "translate(150,0)">
<use xlink:href = "#c1" filter = "url(#hue1)"/>
<text>
<tspan x = "75" y = "100">
30
</tspan>
</text>
</g>
<g transform = "translate(0,150)">
<use xlink:href = "#c1" filter = "url(#hue2)"/>
<text>
<tspan x = "75" y = "100">
90
</tspan>
</text>
</g>
<g transform = "translate(150,150)">
<use xlink:href = "#c1" filter = "url(#hue3)"/>
<text>
<tspan x = "75" y = "100">
270
</tspan>
</text>
</g>
</g>
</svg>
Finally, a type of "luminanceToAlpha" converts the red, green and blue channels into a luminance value then sets the output alpha channel based to the result. The "values" attribute is not used with this type.
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 300 300" version = "1.1">
<desc>
Filter example
</desc>
<filter id = "lum1">
<feColorMatrix in = "SourceGraphic" type = "luminanceToAlpha"/>
</filter>
<defs>
<circle id = "c1" cx = "75" cy = "40" r = "25" stroke-width = "5" stroke = "cadetblue" fill = "khaki"/>
</defs>
<g font-family = "sans-serif" font-size = "30" text-anchor = "middle">
<g>
<use xlink:href = "#c1"/>
<text>
<tspan x = "75" y = "100">
No filter
</tspan>
</text>
</g>
<g transform = "translate(150,0)">
<use xlink:href = "#c1" filter = "url(#lum1)"/>
</g>
</g>
</svg>
Note: It seems that if a filter effect is created but not used, or if a filter effect which does not exist is specified as an 'in' parameter, then the Adobe beta 6 plugin crashes.