10

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

Tixz
  • 135
  • 1
  • 8
  • 1
    you've read http://en.wikipedia.org/wiki/Alpha_compositing i guess? you calculate the alpha in a similar way to how you calculate the colours - see the formulae for "outA" in the link above. – andrew cooke Sep 15 '11 at 22:57
  • I do understand the algorithm, my problem is if I want to calculate the alpha channel so it can be converted to CSS, how do I go about it then? Do I calculate for each color channel and average the result or do I average the colors and use the formula in the PDF specification? – Tixz Sep 16 '11 at 07:02
  • you do neither. you follow the formulae in the specification for both alpha and colours. each component (R,G,B and A) is calculated. – andrew cooke Sep 16 '11 at 11:59
  • Yes, but look at the alpha formulae. It requires backdrop color and a source color as arguments as well as a a backdrop alpha and a source alpha. What color is this? Do you use the average color for the backdrop and source or do you calculate it for each channel and average it to get the RGBA format after? – Tixz Sep 16 '11 at 13:51
  • Now I know why I got it all wrong. What I interperted as a alpha compositing algorithm was indeed a color compositing algorithm. I still have a problem figuring out how r is supposed to be calculated? – Tixz Sep 18 '11 at 17:23

1 Answers1

18

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!)

peterh
  • 11,875
  • 18
  • 85
  • 108
Paul d'Aoust
  • 3,019
  • 1
  • 25
  • 36
  • 1
    Thanks for the answer, the pre-multiplying alpha bit helped me out with something completely unrelated! – Ian Link Oct 13 '12 at 19:44
  • 1
    Glad it helped! I find that premultiplying alpha helps to make the entire concept of compositing easier to grasp and perform. Curious about what unrelated thing it hleped you out with :-) – Paul d'Aoust Oct 18 '12 at 17:12
  • 1
    I've also found that the premultiplication technique is great for mixing an arbitrary number of colours together: red_final = 0; green_final = 0; blue_final = 0; foreach (colour in colours) { red_final += red(colour) * 1 / colours.length; green_final += green(colour) * 1 / colours.length; blue_final += blue(colour) * 1 / colours.length; } – Paul d'Aoust Oct 18 '12 at 17:16
  • What is the reason for or intuition behind `ac = a1 + a2 - a1 * a2`? I couldn’t find anything about it. – Lenar Hoyt Nov 07 '14 at 15:07
  • 1
    @mcb The final alpha (what I assume to be `ac` in your equation) is always going to be more than colour 1's alpha `a1` and more than colour 2's alpha `a2`. (Think about laying two sheets of tracing paper on each other). And for any two alphas `< 1`, there'll still be a bit of transparency between the two, however slight. ex: given `a1 = 0.9` and `a2 = 0.5`, the combined alpha should only have a transmissivity of `0.95`. So `0.9 + 0.5 - (0.9 * 0.5) = 0.95`. If we merely added the two alphas together we'd get the extremely unhelpful (and invalid) `1.4`. – Paul d'Aoust Feb 04 '15 at 03:38
  • @Pauld'Aoust Thanks, your explanations were very helpful. I have completely missed the fact that this is the *over operator* ac = a1 + a2 · (1 - a1). – Lenar Hoyt Feb 04 '15 at 15:24
  • 2
    Thanks in particlar for mentioning the SVG spec with their formulas. Felt like having searched half the internet for those. – Loilo Nov 23 '16 at 22:35
  • @Pauld'Aoust It certainly did! You don't have similarly nice sources for "hue", "saturation", "color" and "luminosity" blend modes, do you? Those sadly aren't listed in the mentioned SVG spec. – Loilo Nov 24 '16 at 00:30
  • @Loilo heh, sorry, no, I never even thought about those blending modes at the time. I've moved on from front-end dev since I wrote this article, so I haven't had much cause to revisit/refine this. – Paul d'Aoust Nov 24 '16 at 17:16
  • Aww. Thanks anyway. I guess I'll make this a question for the broader StackOverflow (or StackExchange Software Engineering) community. :) – Loilo Nov 24 '16 at 17:51
  • Note that it is quite common to skip step 5 so that your result ends up premultiplied. This allows you to skip step 3 when compositing in the next layer. – Ian Ollmann Dec 10 '21 at 20:50