3

Actually, I thought that there would be an easy way to achieve that. What I need is pure alpha value information. For testing, I have a 50 x 55 px PNG, where on every edge a 5x5 pixel rectangle is fully transparent. In these areas alpha has to be 0.Everywhere else it has to be 255. I made very sure that my PNG is created correctly, and it also looks correctly.

Please tell me if this is theoretically correct: I created an CGImageRef that has only the alpha channel and nothing else. This is done with CGBitmapContextCreate and kCGImageAlphaOnly as param. CGBitmapContextGetBitsPerPixel(context) returns me 1, so it indicates me that I really have only one component per pixel: The desired alpha value. I've been reading that CGBitmapContextCreate will handle all the conversion from the given image to the new created context. My image was previously PNG-24 with transparency, but pngcrunch from Xcode seems to convert them somehow.

So, just in theory: Do I have any chance to get to the correct, unpremultiplied alpha at this point? The values I get seem to almost match, but in a big 5x5 transparent square I get values like 19, 197, 210, 0, 0, 0, 98 and so on. If they were true, I would have to see something from the image. The image itself is solid blue.

Tim Sylvester
  • 22,897
  • 2
  • 80
  • 94
Thanks
  • 40,109
  • 71
  • 208
  • 322

3 Answers3

8

Premultiplication doesn't affect the alpha channel, it affects the color channels.

The formula for raster compositing (putting one raster image over another) is:

dst.r = src.r * src.a + dst.r * (1.0 - src.a);
dst.g = src.g * src.a + dst.g * (1.0 - src.a);
dst.b = src.b * src.a + dst.b * (1.0 - src.a);

Premultiplication cuts out the first multiplication expression:

dst.r = src.r′ + dst.r * (1.0 - src.a);
dst.g = src.g′ + dst.g * (1.0 - src.a);
dst.b = src.b′ + dst.b * (1.0 - src.a);

This works because the source color components are already multiplied by the alpha component—hence the name “premultiplied”. It doesn't need to multiply them now, because it already has the results.

unpremultiplied alpha

The alpha component itself is never premultiplied: What would you multiply it by? The color components are premultiplied by the alpha.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • I think I get it now. Alpha remains clean. But in praxis, when I create an PNG that has totally transparency (like a clean piece of high-quality non-colored glass), pngcrunch does something to my PNG. After I read the alpha values, some are 0 like they should, but some are 199 or even bigger, which makes absolutely no sense. If they were true, I would have to see some pixels from that image. White image made totally transparent. Black background in iPhone. Nothing shines through, but like 50% of all alphas are bigger than 100... it's really strange. – Thanks May 16 '09 at 14:27
  • As I said on your other question, you're looking at the data incorrectly, and mistaking other components (notably blue) for alpha. – Peter Hosey May 16 '09 at 15:02
  • One other thing: pngcrush has nothing to do with the contents of a bitmap context. A bitmap context contains pure pixels in the format you asked for; it is completely divorced from any and all external representations, past and future. – Peter Hosey May 16 '09 at 15:09
6

Since premultiplying color values is a simple as:

r = (r * a) / 255;
g = (g * a) / 255;
b = (b * a) / 255;

Getting the inverse would be:

if (a > 0) {
    r = (r * 255) / a;
    g = (g * 255) / a;
    b = (b * 255) / a;
}
rpetrich
  • 32,196
  • 6
  • 66
  • 89
  • Nice! So alpha is always "untouched"? I thought that I would also have to calculate something to get the correct alpha value. – Thanks May 16 '09 at 14:21
  • Yes, the alpha is untouched. Premultiplying color channels is just an optimization to cut down the number of multiply operations the graphics chip has to perform. – rpetrich May 16 '09 at 14:30
1

This formular is not correct. The goal is to find unmultiplexed (r, g, b) that would latter result to the same multiplexed values (it is not possible to find the original r, g, b values, though.

However with the formular above we find for the example of

alpha = 100
r_premulti = 1

a reconstructed r = 2;

Latter if this r is multiplexed again we find 2 * 100 / 255 = 0 but we wanted r_premulti == 1 instead!!

The correct formular needs to round up. Example for r-component:

reconstruced r = ceiling(r_premulti * 255 / alpha)
Zenju
  • 123
  • 7