3
\$\begingroup\$

I found this code buried under some rocks:

var divAlpha = 0xff00/color.A;
var r = (byte) (color.R*divAlpha >> 8);
var g = (byte) (color.G*divAlpha >> 8);
var b = (byte) (color.B*divAlpha >> 8);

The fields on color are bytes, of course. My guess would be it's avoiding using floating point operations to handle premultiplied alpha, but is it converting from or to? How does this work out?

Example output:

0x44112233 -> a: 0x44, r: 0x3f, g: 0x7f, b: 0xbf
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

This code is converting from premultiplied alpha:

Expanding one of the assignments by substituting for divAlpha:

var r = (byte) (color.R*(0xff00/color.A) >> 8);

which on rearrangement of the multiplicative terms becomes

var r = (byte) ((color.R*(0xff00) >> 8)/color.A);

which in turn simplifies to:

var r = (byte) (color.R*0xff /color.A);

or

var r = (byte) (color.R * (0xff /color.A) );

which is clearly seen to be a division by the (fractional) alpha channel value.

This can be verified by computing the reverse operation:

R: (0x3F * 0x44 + 0x80) >> 8 = 0x113C >> 8 = 0x11
G: (0x7F * 0x44 + 0x80) >> 8 = 0x223C >> 8 = 0x22
B: (0xBF * 0x44 + 0x80) >> 8 = 0x333C >> 8 = 0x33
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Note that to avoid precision loss, and properly take advantage of the structure discovered by OP, the original operation should probably be: var r = (byte) ( (color.R*divAlpha + 0x80) >> 8); \$\endgroup\$ Commented Jan 30, 2016 at 10:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .