Use the bitmap caching feature for complex vector content, when appropriate.
A good optimization can be made by using the bitmap caching feature. This feature caches a vector object, renders it as a bitmap internally, and uses that bitmap for rendering. The result can be a huge performance boost for rendering, but it can require a significant amount of memory. Use the bitmap caching feature for complex vector content, like complex gradients or text.
Turning on bitmap caching for an animated object that contains complex vector graphics (such as text or gradients) improves performance. However, if bitmap caching is enabled on a display object such as a movie clip that has its timeline playing, you get the opposite result. On each frame, the runtime must update the cached bitmap and then redraw it onscreen, which requires many CPU cycles. The bitmap caching feature is an advantage only when the cached bitmap can be generated once and then used without the need to update it.
If you turn on bitmap caching for a Sprite object, the object can be moved
without causing the runtime to regenerate the cached bitmap. Changing the x
and y
properties of the object does not cause regeneration. However, any
attempt to rotate it, scale it, or change its alpha value causes the runtime to
regenerate the cached bitmap, and as a result, hurts performance.
Note: The
DisplayObject.cacheAsBitmapMatrix
property available in AIR and the Packager for iPhone does not have this limitation. By using thecacheAsBitmapMatrix
property, you can rotate, scale, skew, and change the alpha value of a display object without triggering bitmap regeneration.
A cached bitmap can use more memory than a regular movie clip instance. For example, if the movie clip on the Stage is 250 x 250 pixels, when cached it uses about 250 KB, instead of 1 KB uncached.
The following example involves a Sprite object that contains an image of an apple. The following class is attached to the apple symbol:
package org.bytearray.bitmap
{
import flash.display.Sprite;
import flash.events.Event;
public class Apple extends Sprite
{
private var destinationX:Number;
private var destinationY:Number;
public function Apple ()
{
addEventListener(Event.ADDED_TO_STAGE,activation);
addEventListener(Event.REMOVED_FROM_STAGE,deactivation);
}
private function activation(e:Event):void
{
initPos();
addEventListener (Event.ENTER_FRAME,handleMovement);
}
private function deactivation(e:Event):void
{
removeEventListener(Event.ENTER_FRAME,handleMovement);
}
private function initPos():void
{
destinationX = Math.random()*(stage.stageWidth - (width>>1));
destinationY = Math.random()*(stage.stageHeight - (height>>1));
}
private function handleMovement(e:Event):void
{
x -= (x - destinationX)*.5;
y -= (y - destinationY)*.5;
if (Math.abs(x - destinationX) < 1 && Math.abs(y - destinationY) < 1)
initPos();
}
}
}
The code uses the Sprite class instead of the MovieClip class, because a timeline is not needed for each apple. For best performance, use the most lightweight object possible. Next, the class is instantiated with the following code:
import org.bytearray.bitmap.Apple;
stage.addEventListener(MouseEvent.CLICK,createApples);
stage.addEventListener(KeyboardEvent.KEY_DOWN,cacheApples);
const MAX_NUM:int = 100;
var apple:Apple;
var holder:Sprite = new Sprite();
addChild(holder);
function createApples(e:MouseEvent):void
{
for (var i:int = 0; i< MAX_NUM; i++)
{
apple = new Apple();
holder.addChild(apple);
}
}
function cacheApples(e:KeyboardEvent):void
{
if (e.keyCode == 67)
{
var lng:int = holder.numChildren;
for (var i:int = 0; i < lng; i++)
{
apple = holder.getChildAt (i) as Apple;
apple.cacheAsBitmap = Boolean(!apple.cacheAsBitmap);
}
}
}
When the user clicks the mouse, the apples are created without caching. When the user presses the C key (keycode 67), the apple vectors are cached as bitmaps and shown onscreen. This technique greatly improves rendering performance, both on the desktop and on mobile devices, when the CPU is slow.
However, although using the bitmap caching feature improves rendering performance, it can quickly consume large amounts of memory. As soon as an object is cached, its surface is captured as a transparent bitmap and stored in memory, as shown in the following diagram:
Object and its surface bitmap stored in memory
Flash Player 10.1 and AIR 2.5 optimize the use of memory by taking the same approach as described in the Filters and dynamic bitmap unloading. If a cached display object is hidden or offscreen, its bitmap in memory is freed when unused for a while.
Note: If the display object's
opaqueBackground
property is set to a specific color, the runtime considers the display object to be opaque. When used with thecacheAsBitmap
property, the runtime creates a non-transparent 32-bit bitmap in memory. The alpha channel is set to 0xFF, which improves performance, because no transparency is required to draw the bitmap onscreen. Avoiding alpha blending makes rendering even faster. If the current screen depth is limited to 16 bits, then the bitmap in memory is stored as a 16-bit image. Using theopaqueBackground
property does not implicitly activate bitmap caching.
To save memory, use the cacheAsBitmap
property and activate it on each display
object rather than on the container. Activating bitmap caching on the container
makes the final bitmap much larger in memory, creating a transparent bitmap with
dimensions of 211 x 279 pixels. The image uses around 229 KB of memory:
Activating bitmap caching on container
In addition, by caching the container, you risk having the whole bitmap updated in memory, if any apple starts to move on a frame. Activating the bitmap caching on the individual instances results in caching six 7-KB surfaces in memory, which uses only 42 KB of memory:
Activating bitmap caching on instances
Accessing each apple instance through the display list and calling the
getChildAt()
method stores references in a Vector object for easier access:
import org.bytearray.bitmap.Apple;
stage.addEventListener(KeyboardEvent.KEY_DOWN, cacheApples);
const MAX_NUM:int = 200;
var apple:Apple;
var holder:Sprite = new Sprite();
addChild(holder);
var holderVector:Vector.<Apple> = new Vector.<Apple>(MAX_NUM, true);
for (var i:int = 0; i< MAX_NUM; i++)
{
apple = new Apple();
holder.addChild(apple);
holderVector[i] = apple;
}
function cacheApples(e:KeyboardEvent):void
{
if (e.keyCode == 67)
{
var lng:int = holderVector.length
for (var i:int = 0; i < lng; i++)
{
apple = holderVector[i];
apple.cacheAsBitmap = Boolean(!apple.cacheAsBitmap);
}
}
}
Keep in mind that bitmap caching improves rendering if the cached content is not
rotated, scaled, or changed on each frame. However, for any transformation other
than translation on the x- and y-axes, rendering is not improved. In these
cases, Flash Player updates the cached bitmap copy for every transformation
occurring on the display object. Updating the cached copy can result in high CPU
usage, slow performance, and high battery usage. Again, the
cacheAsBitmapMatrix
property in AIR or the Packager for iPhone does not have
this limitation.
The following code changes the alpha value in the movement method, which changes the opacity of the apple on every frame:
private function handleMovement(e:Event):void
{
alpha = Math.random();
x -= (x - destinationX)*.5;
y -= (y - destinationY)*.5;
if (Math.abs(x - destinationX) < 1 && Math.abs(y - destinationY) < 1)
initPos();
}
Using bitmap caching causes a performance slowdown. Changing the alpha value forces the runtime to update the cached bitmap in memory whenever the alpha value is modified.
Filters rely on bitmaps that are updated whenever the playhead of a cached movie
clip moves. So, using a filter automatically sets the cacheAsBitmap
property
to true
. The following figure illustrates an animated movie clip:
Animated movie clip
Avoid using filters on animated content, because it can cause performance problems. In the following figure, the designer adds a drop shadow filter:
Animated movie clip with drop shadow filter
As a result, if the timeline inside the movie clip is playing, the bitmap must be regenerated. The bitmap must also be regenerated if the content is modified in any way other than a simple x or y transformation. Each frame forces the runtime to redraw the bitmap, which requires more CPU resources, causes poor performance, and consumes more battery life.
Paul Trani provides examples of using Flash Professional and ActionScript to optimize graphics using bitmaps in the following training videos: