Re-use your BitmapData objects, speed up your apps
This is a post that I started writing about 5 months ago, and I may not be posting for a while, so here’s some info I gathered when doing per-frame analysis of Onyx’s bitmap rendering engine …. I tried to optimize per-frame bitmapdata drawing, since there were so many bitmapdata events happening on a per frame basis. The flow for one layer was as follows:
var bitmap = new BitmapData();
bitmap.draw(layer);
filter.apply(bitmap);
filter.apply(bitmap);
filter.apply(bitmap); // repeat for as many filters are added into the layer
preview.draw(bitmap, scaleDownMatrix);
Now on average, even without any filters being applied, drawing one layer was taking 8ms on a really really fast machine. With 5 active layers in their simplist state - that’s 40ms — which is already reducing the application to around 24fps. Not so great. Now there’s not really a ton you can do about it, but hey, if you can speed up your operations by 1ms or 2ms, that will make a big difference if you’re running 5 of those processes. Here’s what little I found about BitmapData operations:
Expensive Operations (from most expensive to least expensive):
BitmapData.draw() - drawing content into a bitmap
new BitmapData() - creation of a new bitmap in memory
BitmapData.clone() - clones a bitmap
BitmapData.fillRect() - fill a bitmap with a color
BitmapData.copyPixels() - Copy pixels from a bitmap to another
10000 operations of “BitmapData.draw()” 4068 ms
10000 operations of “new BitmapData()” : 2324 ms
10000 operations of “BitmapData.clone()” 1012 ms
10000 operations of “BitmapData.copyPixels()” 962 ms
10000 operations of “BitmapData.fillRect()” : 515 ms
Methods to speed up bitmap operations.
1) When doing per-frame bitmap operations, create and store a bitmap outside of the per-frame operation, and do a fillrect per-frame.
Slower:
- creation of a bitmapdata
- bitmapdata.draw on an object every frame
Faster:
- Create and store a bitmapdata
- on every frame fillRect with 0×00000000, then do your bitmapdata.draw
Slower:
- BitmapData.clone() into a newly created bitmapdata
Faster:
- Create and store a bitmap in memory
- BitmapData.copyPixels into another bitmapdata object
Below is the code i used for the tests (i executed the code a second after the plug-in started). If I’m wrong with any of this, I give you permission to shoot me.
/**
* Testing bitmap speed operations
*/
public function test():void {
var start:int, count:int, times:int;
times = 10000;
var bmp1:BitmapData = new BitmapData(320,240,true,0x00000000);
var bmp2:BitmapData = new BitmapData(320,240,true,0x00000000);
start = getTimer();
for (count = 0; count < times; count++) {
bmp2.fillRect(bmp2.rect, 0x00000000);
}
trace(times, 'operations of "BitmapData.fillRect()" :', getTimer() - start, 'ms');
start = getTimer();
for (count = 0; count < times; count++) {
bmp1 = new BitmapData(320,240,true,0x00000000);
}
trace(times, 'operations of "new BitmapData()" :', getTimer() - start, 'ms');
start = getTimer();
for (count = 0; count < times; count++) {
var bmp3:BitmapData = bmp2.clone();
}
trace(times, 'operations of "BitmapData.clone()"', getTimer() - start, 'ms');
start = getTimer();
for (count = 0; count < times; count++) {
bmp1.copyPixels(bmp2, bmp2.rect, new Point(0,0));
}
trace(times, 'operations of "BitmapData.copyPixels()"', getTimer() - start, 'ms');
start = getTimer();
for (count = 0; count < times; count++) {
bmp1.draw(bmp2);
}
trace(times, 'operations of "BitmapData.draw()"', getTimer() - start, 'ms');
}


Really interesting, bookmarked !
Comment on December 12, 2006 @ 5:25 am
[...] Daniel Hai » Re-use your BitmapData objects, speed up your apps (tags: BitmapData) [...]
Pingback on December 12, 2006 @ 8:29 am
Huh! I think I can put these to good use.
Is there any chance that these faster methods are quicker because they rely less on creating new objects? For instance, clone() returns a reference to a new object that is a duplicate of an old one, whereas copyPixels() doesn’t rely on dynamically creating anything.
Comment on April 2, 2007 @ 10:59 am
[...] Daniel Hai » Re-use your BitmapData objects, speed up your apps: [...]
Pingback on March 22, 2008 @ 8:52 am
Is copyPixels or fillRect the least expensive? The paragraphs conflict with each other. Nice analysis though, this is good stuff to know (and I wish more performance info was available from the API itself)
Comment on December 11, 2008 @ 5:34 am
I think that your timing of copyPixels() might be slightly biased because you’re creating a new Point object at every iteration.
The timing might get much faster by creating a single Point object before iterating.
Comment on March 13, 2009 @ 7:10 am
I got different results with flash player 10 and flex compiler.
“BitmapData.draw()” 2896 ms
“BitmapData.clone()” 2306 ms
“new BitmapData()” : 2031 ms
“BitmapData.fillRect()” : 664 ms
“BitmapData.copyPixels()” 499 ms
Comment on October 19, 2009 @ 8:34 am
[...] a reference to Daniel Hai’s article on this from some time ago: 10000 operations of “BitmapData.draw()” 4068 ms 10000 operations of [...]
Pingback on November 11, 2009 @ 3:47 am
Wow… amazing post. This has given me some really cool ideas of how to use BMDs and bitmaps haha
Comment on May 23, 2010 @ 9:22 am
10000 x “new BitmapData()” : 2324 ms
10000 x “BitmapData.clone()” 1012 ms
Cloning a bitmap is faster than creating an empty one? This is one of those times I wish flash.* package was open source, so we could track down what exactly happens during those calls.
Comment on July 16, 2010 @ 10:10 am