Alexander Shutau
Alexander Shutau

Reputation: 2810

How to apply feBlend and preserve source alpha

I'm trying to modify colors of an image by applying feFlood and feBlend to it in "lighten" and "darken" modes. How do I preserve the alpha channel?

<svg>
  <filter id="filter">
    <feFlood result="flood" flood-color="blue" />
    <feBlend in="SourceGraphic" in2="flood" mode="lighten" />
  </filter>
  <image filter="url(#filter)" href="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" />
</svg>

https://jsfiddle.net/utqghr0o/

Upvotes: 3

Views: 631

Answers (2)

Paul LeBeau
Paul LeBeau

Reputation: 101820

The way lighten works is that it takes the maximum colour value from each channel of each of the two inputs (pre-multiplied with the alpha). So if a pixel has zero opacity, it will not ever count as the maximum colour for any channel, and the value from the other input will be used.

What you need to do is first mask the flood with the alpha from the source image ("SourceAlpha"), then blend the masked flood with the original image.

<svg width="544" height="184">
  <filter id="filter">
    <feFlood result="flood" flood-color="blue" />
    <feComposite in="flood" in2="SourceAlpha" operator="atop" result="maskedflood"/>
    <feBlend in="SourceGraphic" in2="maskedflood" mode="lighten" />
  </filter>
  <image filter="url(#filter)" width="544" height="184" href="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" />
</svg>

Upvotes: 5

Michael Mullany
Michael Mullany

Reputation: 31715

I think Paul's answer is close but not exactly correct.

The feBlend formula is:

Opacity of Final Pixel  = 1 - (1- Opacity of Image A Pixel)*(1- Opacity of Image B Pixel)

If you do the mask first and then the blend, the final opacity will be a bit off. For example for a pixel of 0.6 opacity in image A, you'll end up with a final pixel opacity = 1 - (.6 * .6) = .64

That's close to, but not the same as 0.6.

If you want to retain the exact opacity of Image A in the final image - you need to do the blending first and the masking second. That's assuming that you want the lighten to be done on the pre-multiplied, "100%-opaque-equivalent" colors, which is normally the case.

<svg width="544" height="184">
  <filter id="filter">
    <feFlood result="flood" flood-color="blue" />

    <feBlend in="SourceGraphic" in2="flood" mode="lighten" result="blend"/>
    <feComposite in="blend" in2="SourceAlpha" operator="atop" result="maskedflood"/>
  </filter>
  <image filter="url(#filter)" width="544" height="184" href="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" />
</svg>

Upvotes: 2

Related Questions