Lately I've been experimenting with generative art in Flash/ActionScript 3. Its been pretty fun and I've created some interesting results. In my experience, and I can only assume in most other artists and developers cases, I really got familiar with the Flash Player's performance limitiations. More specifically, I came to truly find out just how bad the Flash Player is at rendering a lot of display objects at once, even if they're just Shape objects, even if cacheAsBitmap = true, and even if they're not animating in any way. This, of course, forces you to look into performance enhancing techniques.
The most common technique that I see artists and developers using involves rendering bitmaps from a display object. Rendering a bitmap of your display objects is a great way to keep the memory of your piece under control. If you were to just leave every Shape, Sprite or MovieClip you created on the stage, the memory your piece consumes would continually grow and eventually cripple your piece. So what you do is render a bitmap every so often and discard any "dead" display objects. By "dead" I mean display objects that are not animating or updating themselves in any way. Its a pretty simple technique. The following example illustrates how this is done:
The code for this example looks like so:
import flash.display.Sprite; import flash.display.Shape; import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.MouseEvent; var circles:Sprite = new Sprite(); var bitmapData:BitmapData = new BitmapData( stage.stageWidth, stage.stageHeight, true, 0x00FFFFFF ); bitmapData.draw( circles ); var bitmap:Bitmap = new Bitmap( bitmapData ); addChild( bitmap ); function onStageClick( event:MouseEvent ):void { for( var i:int = 0; i < 10; i++ ) circles.addChild( getRandomCircle() ); bitmapData.draw( circles ); while( circles.numChildren ) circles.removeChildAt( 0 ); } function getRandomCircle():Shape { var c:Shape = new Shape(); c.graphics.beginFill( Math.random() * 0xFFFFFF ); c.graphics.drawCircle( 0, 0, Math.random() * 100 ); c.graphics.endFill(); c.x = Math.random() * stage.stageWidth; c.y = Math.random() * stage.stageHeight; return c; } stage.addEventListener( MouseEvent.CLICK, onStageClick );
In this example a bitmap is rendered based on the contents of the circles Sprite object every time the user clicks the stage. However, before the bitmap is rendered, 10 random circles are placed inside the circles Sprite. After the bitmap is rendered, the random circles are removed. One of the best parts of rendering a bitmap is that you don't have to continually create new BitmapData or Bitmap objects when its time to render. If you add more display objects to your subject and want to render them in the bitmap you just have to call the draw() method on the BitmapData object again. Also, notice that the memory used by the piece doesn't increase over time even thought it looks like you're adding hundreds of display objects to the stage. Cool stuff.
The One Small Annoyance
As I've been messing around with this stuff I came across a little oddity which caused me great annoyance for a few hours. I decided to start adding filters to the shapes I was creating. No big deal, right? Well, lets take a look at what I did by adjusting the previous example to apply a BlurFilter instance on each circle that's created.
The code for this example looks like so:
import flash.display.Sprite; import flash.display.Shape; import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.MouseEvent; import flash.filters.BlurFilter; var circles:Sprite = new Sprite(); var bitmapData:BitmapData = new BitmapData( stage.stageWidth, stage.stageHeight, true, 0x00FFFFFF ); bitmapData.draw( circles ); var bitmap:Bitmap = new Bitmap( bitmapData ); addChild( bitmap ); function onStageClick( event:MouseEvent ):void { for( var i:int = 0; i < 10; i++ ) circles.addChild( getRandomCircle() ); bitmapData.draw( circles ); while( circles.numChildren ) circles.removeChildAt( 0 ); } function getRandomCircle():Shape { var c:Shape = new Shape(); c.graphics.beginFill( Math.random() * 0xFFFFFF ); c.graphics.drawCircle( 0, 0, Math.random() * 100 ); c.graphics.endFill(); c.x = Math.random() * stage.stageWidth; c.y = Math.random() * stage.stageHeight; c.filters = [new BlurFilter( 150, 150 )]; return c; } stage.addEventListener( MouseEvent.CLICK, onStageClick );
You should notice occasionally that when the bitmap is rendered there are some clipping issues. You might see some hard lines on a blurred circle as if it was masked. This not only occured for BlurFilter instances, but also any other filters with blur-like effects, such as DropShadowFilter and GlowFilter. The following screen shot illustrates this:

I couldn't for the life of me figure out what was causing this to occur until I accidentally stumbled across a fix while creating these examples. To avoid the clipping, simply add the display object that you're rendering to the stage. Why this works is beyond me, but at least its a fix. As an additional performance measure, you can set the visibility of that display object to false. In the case of these examples we're talking about the circles Sprite. Adjust the line where the circles Sprite is created to look like this:
var circles:Sprite = new Sprite(); addChild( circles ); circles.visible = false;
And the resulting piece should render properly like so:
While this is just a basic example, this technique can be applied in many different ways to improve performance. For example, I've been working on a painting program that paints a loaded image using organic moving brushes that uses a similar technique. The brushes even animate a bit before the are discarded. At any rate, I hope to find more time to be able to share more things that I find useful while working with generative art. Until then, please post questions or any suggestions to techniques you find useful!



7 Responses to “Using BitmapData to Improve Performance (And One Small Annoyance)”
Nice post. If you get a chance, could you log a bug on the clipping issue in the public bug base for Flash?
http://bugs.adobe.com/
mike chambers
mesh@adobe.com
I once wrote an animation that consisted of a bunch of 2D points that moved around with some physics + arbitrary behaviour and on each frame, I would draw a lineTo from each point’s previous position to its current one. As you I found that every so often I needed to do a graphics.clear() on the sprite in charge of rendering the graphics just before I redrew everything to a bitmap. That seemed to give the renderer a “break” and optimize performance.
Also, regarding your weird findings with filters, I think this problem is called screen tearing, and in my case I found that it was related with the wmode of the html wrapper holding the swf, the FP version and the browser version. In my case the problems dissapeared when I upgraded my firefox to the latest one. There seems to be a bit of weakness with the flash player’s compatibility with the sync to the graphics card lately.
Nice work mate. Clear coding as well.
Will do, Mike. It didn’t even cross my mind to log it as a bug since I found a little “fix”.
Nice Matt,
I’ve been enjoying your blog for some time. This is semi-related, but I noticed in flash player 10 that my bitmapData objects and associated filters can get giant even though they are specified as a number typically less than 10. I think the error code was 2015, but I don’t remember for sure and I don’t have any code that is currently offending. I’ve always been able to resolve it by moving the object up or down one pixel, it’s as if something in the rendering engine explodes my height or width. When I first researched this others were having this issue, but no one had a conclusive reason as to why. Basically, I was wondering if you have run into this and if you can add any insight as to why this happens.
Anyway, thanks for the great blog.
Zach, when you say “can get giant” do you mean they can eat up a lot of memory or the display dimensions go out of whack?
What happens is that the height will get so big and exceed the max value allowed(8191, I think). It is with multiple filters and as I mentioned I just have to move something one pixel up or down and there are no problems. When it first happened I found the same issue happening to others and the resolution was the same (just move up or down), but no one understood what was actually happening. You seem like the kind of guy who would have figured out the how/why if you ran into this, so I was just interested if you could shed any further insight. But it seems as if you haven’t seen so if you do you’ll know how to work around it.