Question

I need to create a custom CSS filter that would convert black and white image to two-color image with two custom colors.

I want to apply filter in something like this way (not sure if this syntax is possible, maybe 6 arguments have to be passed separately):

.duotone {
    -webkit-filter: custom(none url(/filters/duotone.fs),
        black_color [255, 0, 0] white_color [255, 192, 203]);
}

so that black color is replaced with Red in this case, both the white and transparent colours (background) are replaced with Pink and all the shades of grey are replaced with colour in between (using straight-line curve).

Maybe it is even possible to define filter in CSS without an external file?

The application of this filter is to convert black/white icons to colored icons at run-time.

There are very few examples on how to do it, this one is very generic covering much wider range of filters and the specification is also not so easy to understand.

Could somebody post a working filter doing this or something similar? Or at least maybe you could point me to some more focused examples of application of ColorMatrix that will be relevant to my situation.

Was it helpful?

Solution

UPDATE:

In order to do that you will need to use -webkit-mask-image on CSS. This used to be a webkit only but it's now w3c css3.

The support is just present on webkit tho. In order to keep backward compatibility and display a regular black icon you will need to identify if the browser has this capabilities. I'v archived that using modernizr.

http://jsfiddle.net/kkobold/Zq5FZ/5/

old answer:

I believe thats the result you are looking for:

http://s7.postimage.org/vo5v33tuj/Screen_Shot_2013_02_03_at_11_28_46_PM.png

you can archive it by filtering to sepia and then changing hue degree. different combinations will give different results. But a simple color picker can be made for the hue and a slide for sepia to increase or decrease intensity of the color change.

and CSS3 only.

<div class="pull-left span4" >
<img src="/img/bw.jpg" style="-webkit-filter: sepia(0.0) hue-rotate(0deg);" width="300"/><br />
-webkit-filter: sepia(0) hue-rotate(0deg);
</div>
<div class="pull-left span4" >
<img src="/img/bw.jpg" style="-webkit-filter: sepia(0.8) hue-rotate(50deg);" width="300"/><br />
-webkit-filter: sepia(0.8) hue-rotate(50deg);
</div>
<div class="pull-left span4" >
<img src="/img/bw.jpg" style="-webkit-filter: sepia(0.8) hue-rotate(100deg);" width="300"/><br />
-webkit-filter: sepia(0.8) hue-rotate(100deg);
</div>
<div class="pull-left span4" >
<img src="/img/bw.jpg" style="-webkit-filter: sepia(0.8) hue-rotate(150deg);" width="300"/><br />
-webkit-filter: sepia(0.8) hue-rotate(150deg);
</div>
<div class="pull-left span4" >
<img src="/img/bw.jpg" style="-webkit-filter: sepia(0.8) hue-rotate(200deg);" width="300"/><br />
-webkit-filter: sepia(0.8) hue-rotate(200deg);
</div>
<div class="pull-left span4" >
<img src="/img/bw.jpg" style="-webkit-filter: sepia(0.8) hue-rotate(250deg);" width="300"/><br />
-webkit-filter: sepia(0.8) hue-rotate(250deg);
</div>
<div class="pull-left span4" >
<img src="/img/bw.jpg" style="-webkit-filter: sepia(0.8) hue-rotate(300deg);" width="300"/><br />
-webkit-filter: sepia(0.8) hue-rotate(300deg);
</div>
<div class="pull-left span4" >
<img src="/img/bw.jpg" style="-webkit-filter: sepia(0.8) hue-rotate(350deg);" width="300"/><br />
-webkit-filter: sepia(0.8) hue-rotate(350deg);
</div>

OTHER TIPS

A little late to the party here, but I've built you a CSS custom filter that does exactly what you're looking for. All the code can be found on GitHub here: https://github.com/awgreenblatt/css-custom-filters/tree/master/duotone

Because of security restrictions, custom filters can't read pixels; they can only apply transformations to them. So for instance, the filter can't say, if the pixel is black, change it to red. However, there is still a way to do it.

Each color component ranges from [0,1]: All white pixels in the source image will be (1, 1, 1, 1) and all black pixels in the source image will be (0, 0, 0, 1)

Let's call the color you want to map black to: (br, bg, bb, 1) and the color you want to map white to: (wr, wg, wb, 1)

You can define in a fragment shader a css_ColorMatrix which will apply a transformation on the source image. If we set the blend mode to 'multiply' then the css_ColorMatrix will be pre-multiplied by the current source image's color.

So, we set the css_ColorMatrix to:

wr - br,       0,       0,     0,
      0, wg - bg,       0,     0,
      0,       0, wb - bb,     0,
     br,      bg,      bb,     1

If the source pixel is black, (0, 0, 0, 1) then the result of the multiplication will be (br, bg, bb, 1) If the source pixel is white (1, 1, 1, 1) then the result of the multiplication will be

((wr - br + br), (wg - bg + bg), (wb - bb + bb), 1) or (wr, wg, wb, 1)

Here's the fragment shader source:

precision mediump float;

uniform vec3 black;
uniform vec3 white;

// Main

void main()
{
  vec3 b = black / 255.0;
  vec3 w = white / 255.0;

  css_ColorMatrix = mat4(w.r-b.r, 0.0, 0.0, 0.0,
                         0.0, w.g - b.g, 0.0, 0.0,
                         0.0, 0.0, w.b - b.b, 0.0,
                         b.r, b.g, b.b, 1.0);
}

The vertex shader is just the default:

precision mediump float;

// Built-in attributes

attribute vec4 a_position;

// Built-in uniforms

uniform mat4 u_projectionMatrix;

// Main

void main()
{
  gl_Position = u_projectionMatrix * a_position;
}

And you apply the filter as follows:

-webkit-filter: custom(url(duotone.vs) mix(url(duotone.fs) normal source-atop), 
             1 1 border-box, black 255 0 0, white 255 192 203);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top