Changing the color of image in HTML with an SVG feColorMatrix filter

In a previous post, I changed the color of a simple image (of a pair of eyes) by converting it to an SVG first, and then changing the SVG colors.

But what if you want to a take a complex image and change the color?

 

In Photoshop/Gimp this can be achieved by creating a new layer on top of the image and filling it with a solid color, and then setting its Mode to ‘Multiply’.  But how can we reproduce this on the web?

There are various solutions using svg filters (feFlood and feBlend) but these are not very well supported in browsers.  So I’ve come up with a solution that is very well supported in all modern browsers, including IE.

<svg height="100%" width="100%" >
<defs>
<filter id="f_skintint"  x="0%" y="0%" width="100%" height="100%">
<feColorMatrix
type="matrix"
values="0.5 0   0   0   0
0   0.5 0   0   0
0   0   0.5 0   0
0   0   0   1   0 "/>
</filter>
</defs>
<g filter="url(#f_skintint)" >
<image xlinkHref="girl-head.png" x="507" y="239" width="597" height="491" />
...
</g>
</svg>

Replace the numbers 0.5 with the rgb values of the color that you want.  For example, in react:


hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : {r: 255, g: 255, b: 255};
}

skinColorDef(colorAsString) {
const hex = hexToRgb(colorAsString); /* <-- See next for an improvement*/
const r = hex.r/255;
const g = hex.g/255;
const b = hex.b/255;
return (
<defs>
<filter id="f_skintint"  x="0%" y="0%" width="100%" height="100%">
<feColorMatrix
type="matrix"
values={r +"   0   0   0   0 " +
"0   "+g+"   0   0   0 " +
"0   0   "+b+"   0   0 " +
"0   0   0   1   0 "}/>
</filter>
</defs>
);
}

 

We can now tint our image with a color like skinColorDef(“#e7b48f”).

But let’s make small improvement.  It’s not obvious what the final color is going to be because the tint color is multiplied by the color in the image.  So let’s make it more intuitive by first looking at the main color in the image (e.g. using the color picker in gimp/photoshop) and then dividing (i.e ‘un-multiplying’) the colorAsString by that color.

For example, the skin color in that girl image is #fff2f2 which is (255,242,228).  So:


divideByImageSkinColor(rgb) {
return {r: rgb.r * (255/255), g: rgb.g * (255/242), b: rgb.b * (255/242)}
}

and modify the skinColorDef like:


skinColorDef(colorAsString) {
const hex = divideByImageSkinColor(hexToRgb(colorAsString));

Now we can just chose the colors directly.  For skin, the Fitzpatrick Scale is a nice place to start:

fitzpatrick-color-chart

We can now use these RGB values directly in our skinColorDef function.  Here’s an example html combobox to select the color: (The onChange function is left to you to implement)


<select name="Skin color" onChange={...}>
<option value="#f4d0b1">Light</option>
<option value="#e7b48f">Fair</option>
<option value="#d29e7c">Medium</option>
<option value="#ba7750">Olive</option>
<option value="#a55d2b">Brown</option>
<option value="#3c201d">Black</option>
</select>

And that’s it!

Sidenote: Many years ago, I wrote the graphics drivers (when I worked at Imagination Technologies) to accelerate this sort of multiply operation using shaders.  That driver is used in the iPhone, iPad, TomTom, and many other small devices.

Advertisements

One thought on “Changing the color of image in HTML with an SVG feColorMatrix filter

  1. Pingback: Photoshop/gimp layers to SVG | John Tapsell

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s