SVGBasics

Colour Transforms - SVG Does the Heavy Lifting

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 a15
a21 a22 a23 a24 a25
a31 a32 a33 a34 a35
a41 a42 a43 a44 a45
If 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:
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.

<?xml version="1.0" standalone="no"?>
<!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.

<?xml version="1.0" standalone="no"?>
<!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.

<?xml version="1.0" standalone="no"?>
<!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.

<?xml version="1.0" standalone="no"?>
<!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.

©2004 Late Night PC Service | About Us | Site Map | Contact Us