PNG compression in Android… (You have got to be kidding)

Over the last few weeks, I’ve been learning the Android SDK in an effort to bring Layers to Android devices. It’s going pretty well, but every once and a while I run into a truly WTF moment.

Tonight I was importing some images from the iPhone version of Layers when I noticed that Android seems to visibly reduce the quality of PNG files at compile time. In images with fine gradients, smooth color transitions, or very light shadows you tend to get banding. It almost looks like the image were being converted to a GIF file.

I figured it’d be easy to fix. Go into Eclipse, right click on everything, look in menus… repeat… profit! Unfortunately, it seems there’s no way to turn off compression for specific file or choose a non-lossy compression method. However, I found this gem of a solution in the Android Widget Design Guidelines:

“To reduce banding when exporting a widget, apply the following Photoshop Add Noise setting to your graphic.”

Um… what now?

It turns out, you can get around the compression algorithm by adding a small amount of pixel noise to your images. It’s mostly invisible, and it prevents the compression from producing obvious bands.

It’s an instant fix, but I almost laughed out loud. Seriously? This is the documented solution? *sigh*.

  1. Hi Ben,

    Glad to see that you’re taking Layers to other platforms.

  2. Nice article ! Unfortunately, you’re not understanding the problem in the correct way. Actually, Android is not compressing graphic resources at all. The problem comes from the fact most (I think all for now :() Android devices are based on a display that uses a 16-bits color palette to render colors. As a result, you get banding effects with simple gradient.

    I’ve wrote an article dealing with that problem (http://android.cyrilmottier.com/?p=196) : it’s in French but I’m sure Google Translate will help you :).

    Good luck and welcome to the Android world !

  3. lailai

    Android fail haha

  4. Jay

    You might want to try inserting images into the res/raw/ directory.

    http://developer.android.com/guide/topics/graphics/2d-graphics.html

  5. Andrew Lundin

    Hi! I’m just getting into Android development, and I happened to run across your article yesterday. This morning I read about something that may provide a better solution to this problem. The following “Note” comes from this page:

    http://developer.android.com/guide/topics/graphics/2d-graphics.html

    Note: Image resources placed in res/drawable/ may be automatically optimized with lossless image compression by the aapt tool. For example, a true-color PNG that does not require more than 256 colors may be converted to an 8-bit PNG with a color palette. This will result in an image of equal quality but which requires less memory. So be aware that the image binaries placed in this directory can change during the build. If you plan on reading an image as a bit stream in order to convert it to a bitmap, put your images in the res/raw/ folder instead, where they will not be optimized.

    I guess the noise they suggested to add is probably chromatic noise, which would expand the palette beyond the 256 colors eligible for 8-bit compression, meaning it probably won’t be recompressed at all. But if you don’t mind the hassle of reading the image as a bit stream, you can prevent the recompression without altering the image. I have not tried this, but it looks promising. I hope it helps!

Reply to 'PNG compression in Android… (You have got to be kidding)'