Wednesday, March 28, 2012

The Art of Memory optimization in android frame animation.

I remember when I was a child, we have made drawings on papers which acted as animation. We draw numerous slides one by one and arrange them and flip them speedily. Flip books are low-tech fun that still appeal to kids today, especially if they are creating them themselves. Back then , there was not any problem with memory ( in the worse case we add more papers and expend the Flip book ).
Implementing animation on android is a totally different story. Each application has very limited amounts of memory, and it’s based on the specific firmware running on each device. Assume that your animation frame images are compressed (PNG or JPG). The compressed size is not useful for calculating how much memory is needed to display them. For that, you need to think about the uncompressed size. This will be the number of pixels multiplied by the number of bytes per pixel, which is typically 4 (32 bits).So let’s say , you have an animated cannon with 9 position ( Each position contain 8 frames of 130x130).It will require a total of 4,867,200 bytes to hold the raw bitmap data for all the frames , and this is only for one character animation. Within a regular game you have many characters and many animations. That's bring us to "Out Of memory error - Bitmap exceeds VM budget" error message, which is one of the most annoying (and common ) errors when dealing with bitmaps and Android ( you will find plenty of other posts related to this issue), and its caused due to limited virtual memory heap size.
To summarize the subject, you need to find a way to draw your animation using less memory. There are many options, but it depends a lot on how your animation needs to look.   One solution is to re-sample and scale your bitmaps prior to inserting them into the cache, by checking the image size and then down sample it by appropriate factor. Changing the sample size reduces the amount of memory used, but it will come on the cost of image quality. The other solutions is mainly determined by the animation style and an "out of the box" solutions. For example, if we go back to the cannon example. I can optimize the memory, by loading only one direction of the cannon positions, and mirror the other side when needed. The way to do it,without creating a new bitmap, is as followed :

//Load the Cannon drawable ( with only one direction of the cannon positions frames)
Bitmap sprite = BitmapFactory.decodeResource(this.getResources(), R.drawable.spritegfx );

//Create a matrix to be used to transform the bitmap
Matrix mirrorMatrix = new Matrix();

//Set the matrix to mirror the image in the x direction
mirrorMatrix.setScale(-1.0f, 1.0f);

//The image will draw flipped in X, but it will also appear to the left of the Y axis, which defaults to the left edge of the view. post-translate everything to the right by at least the width of the bitmap, to make it appear on screen.
mirrorMatrix.postTranslate(bitmap.getWidth(), 0)

// Save the default matrix state
canvas.save(Canvas.MATRIX_SAVE_FLAG);

// Apply your matrix on the current canvas                        
canvas.concat(mirrorMatrix);

// Draw bitmap on canvas. source Rectangle is created based on the current animation frame.
canvas.drawBitmap(sprite ,sourceRect,destRect,null);

// Restore original state
canvas.restore();

please feel free to contact me If you have any questions.

Good Luck.

Moris Oz
Caapi Technologies