Alpha Compositing Algorithm (Blend Modes)

Tixz picture Tixz · Sep 16, 2011 · Viewed 7.3k times · Source

I'm trying to implement blend modes from the PDF specification, for my own pleasure, in SASS.

PDF Specification: http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf

Page 322 is the alpha compositing section.

The input values are in RGBA format, but now I'm trying to implement blending of the alpha component. How do I excatly go about doing it? From what I gather my values should be between 0.0 and 1.0, that's done. However from the specs it seems that you should blend for each color channel? Do I just average it out to get back to RGBA form for my alpha component?

Any help is appriciated, I don't mind reading blog, books etc. to get my answer, as this is purely an intellectual exercise.

Thanks in advance,

Emil

Answer

Paul d'Aoust picture Paul d'Aoust · Jun 22, 2012

The SVG spec has a lot of good equations for various blending modes. And yes, you do have to calculate both the new alpha and the new colour -- for each channel. For standard blending modes, the alpha is calculated this way:

alpha_final = alpha_bg + alpha_fg - alpha_bg * alpha_fg

Note: I see you're considering alpha to be between 0 and 1, which is good. Alpha values in CSS are always defined as float values from 0 to 1; it's good to stick with this convention, because it makes the calculations immensely easier.

It helps to 'premultiply' each colour channel by its alpha; these are more helpful for interpreting and using the usual formulae:

colour_bg_a = colour_bg * alpha_bg

In other words:

red_bg_a = red_bg * alpha_bg
green_bg_a = green_bg * alpha_bg
blue_bg_a = blue_bg * alpha_bg

Then, for plain-jane alpha compositing (like overlaying sheets of tracing paper, also known as src-over in Porter and Duff's original paper and the SVG alpha compositing spec), you take each channel and calculate it thus:

colour_final_a = colour_fg_a + colour_bg_a * (1 - alpha_fg)

The last step is to 'un-multiply' each final colour channel value by the final alpha:

colour_final = colour_final_a / alpha_final

and put it into your mixin somehow:

rgba(red_final, green_final, blue_final, alpha_final)

The other blending modes (multiply, difference, screen, etc) are slightly more complicated formulas, but the concept for every single mode is the same:

  1. Separate the R, G, B, and A values of both the foreground and background colours
  2. Calculate the alpha of the new, final colour with the above formula
  3. Pre-multiply all the R, G, and B values by their alpha value
  4. Calculate the new, final R, G, and B values (insert blending mode formula here)
  5. Un-multiply the final R, G, and B values by the final alpha
  6. Clip the final R, G, and B values so that they are between 0 and 255 (necessary for some modes, but not all)
  7. Put the colour back together again!

If you're still interested in this, I've been doing the very thing in Stylus. You can see my progress here: https://github.com/pdaoust/stylus-helpers/blob/master/blend.styl You might be able to use it as a starting point for your own Sass mixin.

The first thing I do is convert all the R, G, and B values from 0 - 255 values to 0 - 1 float values for the purposes of the calculations. I don't know if that's necessary, and it does require converting them back to 0 - 255 values. It felt right to me, and Porter and Duff worked in 0 - 1 float values in their original paper.

(I'm encountering trouble with some of the compositing modes, which produce wildly different results from the expected results that the SVG spec pictures. I suspect that the spec gives the wrong equations. If anyone knows about Porter/Duff blending modes, I'd be very grateful for their help!)