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*.


Keni
Hi Ben,
Glad to see that you’re taking Layers to other platforms.
Jan 22nd, 2010
Cyril Mottier
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 !
Feb 2nd, 2010
lailai
Android fail haha
Apr 6th, 2010
Jay
You might want to try inserting images into the res/raw/ directory.
http://developer.android.com/guide/topics/graphics/2d-graphics.html
May 19th, 2010
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!
Aug 30th, 2010
Yahel
Hey,
The real solution is here for in apps :
http://stuffthathappens.com/blog/2010/06/04/android-color-banding/
Tried it just now on my app and it works like a charm except I had to put it in the oncreate and not in the attachedwindow.
Nov 15th, 2010
Kevin
Cyril is absolutely correct. This is neither Android-specific (the iPhone is 6-6-6 so 18 bits of color all in all) nor does the dithering stop any “optimiziations”. Most smart phone displays can only display 16 bits of color. Newer smartphones can show more but still like to use a color depth of 16 bits because it saves memory and cpu. You can tell the window manager to go to 24 or 32 bits if you want to, but your bitmaps will (obviously) need more ram.
Nov 20th, 2010
dori
You can just set
getWindow().setFormat(PixelFormat.RGBA_8888);
before setting the contentView in an activity…solved!
Mar 21st, 2011
Alex Berg
I suggest using the excellent program DamageControl. The program applies dithering to images, and can support dithering to 16 colors. Though new warned that if you resale the images on the device before displaying the image you will likely loose the careful dithering created by Damage Control and banding will reappear.
Mar 27th, 2011
Richard Lalancette
I found a great trick for my game. I use the drawable-no-dpi folder.
This way, nothing happens to my graphics.
I use photoshop only now and create plenty of nice 9 patch PNGs.
Feel free to drop by and grab them on my blog:
android9patch.blogspot.com
Free to use for any apps, including commercial.
They can be editing with photoshop or the android 9 patch tool.
Sep 2nd, 2011
Richard Lalancette
I found a great trick for my game. I use the drawable-no-dpi folder.
This way, nothing happens to my graphics.
I use photoshop only now and create plenty of nice 9 patch PNGs.
Feel free to drop by and grab them on my blog:
android9patch.blogspot.com
Sep 2nd, 2011
Dwayne
@Richard – Minor correction: the name of the folder needs to be “drawable-nodpi” (you had an extra “-”).
BTW, thanks for sharing your 9-patch files – some good stuff there.
Sep 30th, 2011
Kat
We’re having a slightly different problem… there’s no banding, but the colours are appearing completely wrong. We’re trying to get a sandy coloured png to appear, but it turns blue on android :/ and we can’t seem to find any fix for it, or even anyone with the same problem.
Feb 25th, 2012
Reply to 'PNG compression in Android… (You have got to be kidding)'