Tuesday, June 29, 2010

android.Bitmap and premultiplied alpha

While working on City at Night, I had to work out how to get the streaks of traffic to blend cleanly with the photographic image.  I couldn't just stack them on top, because they need to be behind the signs and lamp posts that line the road.  I also wanted to keep my number of passes to a minimum.

After some consideration, I decided to render the streaks first, then draw the city image on top, with an alpha channel cutting out the roadway features.  The plan was to blend this using GL_ONE GL_ONE_MINUS_SRC_ALPHA, which would keep all the brightness of the source image, but mask out streaks based on the alpha channel.  Simple.

I made my alpha cutout, got the images loaded, and discovered that my road surface seemed awfully dark.  Holding  the phone side by side with my original, they were really strangely dark.  My first thought was to blame Gimp, which I used to create the alpha.  I've seen it needlessly throw away color data in places with 0% alpha before.

After double and triple checking Gimp and not solving the problem, I downloaded paint.net and tried it there, but got the same result.  Then I tried it with an out of date version of Paint Shop Pro that I know for an absolute fact didn't mess with the color channel when you changed the alpha.  Same problem.

At this point I was confident the image was fine.  That means that after the image gets loaded, my colors turned dark.  I conducted an experiment by making a java array of a 2x2 texture, with pure white colors but alpha values of 50%.  I used android.Bitmap to make a bitmap of this, and lo and behold discovered that when blended GL_ONE GL_ZERO, I got middle grey instead of white.  Ahah!

So, the question then was:  is this happening at load time or when I hand it to OpenGL?  I pack my color values into ints and hand the array directly to glTexImage2D, and when I do that, I get the pure white I'm expecting.  Some searching around online confirmed this - android.Bitmap premultiplies your alpha for you, and there's no way to stop it from doing so.

This identified the culprit but didn't solve the problem.  PNG itself is a relatively complex image format, but I've read TGA files before, so proceeded to get a TGA loader working.  Reading in a TGA and handing the resultant arrays to OpenGL finally got me proper colors on my city highways, at the cost of some bloated filesize

So, if you're trying to do any kind of special alpha blending and are having mysterious problems with the colors in your artwork, keep that in mind: android.Bitmap forcibly premultiplies your alpha, so your colors will be darker than you started.

No comments:

Post a Comment