Search code examples
svg-filters

Flood Color Not respected on certain image


So I'm trying to use a filter on an image in an HTML page like so in order to overlay the image with a certain color. My sample image is white:

img {
  filter: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' height='0'><defs><filter id='bronzo-filter'><feColorMatrix type='luminanceToAlpha' result='L2A'/><feFlood flood-color='green' result='colorfield'/><feBlend mode='multiply' in='L2A' in2='colorfield'/><feComposite operator='in' in2='SourceGraphic'/></filter></defs></svg>#bronzo-filter");
}
<img src="https://i.sstatic.net/K8CIc.png">

It seems that the result is always a black image even though the flood-color is specified as cyan. Is this because only a gray image would work properly for this kind of filtering overlay?

On the other hand for this image it seems with the same filter the flood color is respected. The question I want answered is why.

img {
  filter: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' height='0'><defs><filter id='bronzo-filter'><feColorMatrix type='luminanceToAlpha' result='L2A'/><feFlood flood-color='green' result='colorfield'/><feBlend mode='multiply' in='L2A' in2='colorfield'/><feComposite operator='in' in2='SourceGraphic'/></filter></defs></svg>#bronzo-filter");
}
<img src="http://via.placeholder.com/350x150">

I tried with a third image, a greyified version of the first at #aaaaaa. It seems to respect the flood color. Here is the third try:

img {
  filter: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' height='0'><defs><filter id='bronzo-filter'><feColorMatrix type='luminanceToAlpha' result='L2A'/><feFlood flood-color='green' result='colorfield'/><feBlend mode='multiply' in='L2A' in2='colorfield'/><feComposite operator='in' in2='SourceGraphic'/></filter></defs></svg>#bronzo-filter");
}
<img src="https://i.sstatic.net/V4gGT.png" />

After some testing I realized that flood colors can simply be specified via rgb like rgb(208,164,114); This still doesn't answer the question of why the white image always goes black while the other ones colored #aaaaaa always respect the flood color.


Solution

  • luminanceToAlpha converts the source image to a solid black image with varying transparency, it literally converts the luminance to alpha values and zeros out the regular color channels.

    For your white image, it converts it into a solid black image (rgb 0,0,0) with 100% opacity. When you multiply anything with 0 you get 0. So when you multiply this with any flood color you get black.

    For your other non-white images, the filter is converting them into partially transparent black images. When they reach the blend step, they're pre-multiplied into fully opaque shades of grey before being multiplied with the flood-color, so you're seeing the flood color show up.

    For your use case of white images, you don't want to use L2A, you just want a simple multiply.

    <svg version='1.1' xmlns='http://www.w3.org/2000/svg' height='0'>
    <defs>
        <filter id='bronzo-filter'>
          <feFlood flood-color='green' result='colorfield'/>
          <feBlend mode='multiply' in='SourceGraphic' in2='colorfield'/>
          <feComposite operator='in' in2='SourceGraphic'/>
        </filter>
      </defs>
    </svg>