Re-use your BitmapData objects, speed up your apps

December 12th, 2006

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');
}

10 Comments »

  1. Tek wrote,

    Really interesting, bookmarked !

    Comment on December 12, 2006 @ 5:25 am

  2. danielyuen.hk Blog : links for 2006-12-12 wrote,

    [...] Daniel Hai » Re-use your BitmapData objects, speed up your apps (tags: BitmapData) [...]

    Pingback on December 12, 2006 @ 8:29 am

  3. Rezmason wrote,

    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

  4. AS3 Optimizations « Jjroox’s about user experience wrote,

    [...] Daniel Hai » Re-use your BitmapData objects, speed up your apps: [...]

    Pingback on March 22, 2008 @ 8:52 am

  5. Hunter Loftis wrote,

    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

  6. Dennis wrote,

    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

  7. Valentin wrote,

    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

  8. Visual Harmonics » Post Topic » Speeds of different BitmapData drawing methods wrote,

    [...] 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

  9. ColePark wrote,

    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

  10. Marko wrote,

    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

Leave a comment

RSS feed for comments on this post. TrackBack URI