Saturday, September 7, 2024

High-Performance Graphics in Flash 8

Early this year, I was inspired to create an experimental piece called varicose-g. Varicose-g uses a simple branching algorithm paired with the drawing API to draw an organic vein network. In Flash Player 7, the experiment ran slowly, and I was forced to dynamically clear older veins to reduce the graphic compositing overhead. This limited the generative quality of the experiment and reduced the complexity of the veins I could draw.

With the release of Flash Player 8, I was able to make a number of updates that significantly increased the performance, complexity, and visual interest of the piece. Check it out (requires Flash Player 8):
This article focuses on those changes, including the use of a custom bitmap caching routine, blurring remnant veins, and applying color adjustments. You can download the source code for this experiment in the Requirements section.

The Original Concept: A Moving Artery

While browsing through my collection of FLA files, I stumbled on an experiment called vein-1 that was created by a fellow Flash developer, Mike Johnson. I was inspired to recreate it in ActionScript 2 and spice up the implementation. The result was a perpetually moving artery with branches of veins and capillaries branching from it at random points (see Figure 1).

I’m not going to discuss the code behind the original experiment in full detail here, but if you are interested, you can download the source from my blog.

Briefly, the VeinManager class instantiates a single instance of a Vein class, which becomes the artery. In each frame, the Vein class randomly alters its angle of movement, and draws a line segment with a random length along that angle. It also randomly instantiates new Vein classes with a lower thickness, and the same initial angle and location as the artery. These child veins then carry out the same functions (drawing line segments and creating children of their own) for a random number of frames.

This experiment obviously generated a lot of line segments, and would quickly overwhelm the ability of Flash Player to composite the vectors. To reduce the impact of vector compositing, the Vein instances in the original experiment fade and remove themselves from the Stage after a few seconds. In Flash Player 8, I was able to make the veins persistent by using a custom bitmap caching routine.

Increasing Performance Through Custom Bitmap Caching

With Flash Player 8, I thought I would be able to increase massively the performance and persistence of varicose-g by caching the vectors to bitmap. My initial thought was to set the full VeinManager canvas so it uses the built-in bitmap caching:

// Set the canvas to cache to bitmap automatically:
canvas.cacheAsBitmap = true;

While this produced a major performance increase, it still slowed down as more veins were added. This is because the vectors are being retained and recomposited in the redraw regions each frame. As more vectors are lain down, the recompositing time increases, slowing the whole experiment down.

Instead, I wrote a custom bitmap caching routine. With each frame, the VeinManager class takes all of the new vectors that were drawn by the Vein classes and composites them onto a bitmap using the draw method of BitmapData, and then clears all of the vectors-the vectors are never even rendered by Flash to the display, you only ever see the bitmap. This means that the CPU usage for compositing never increases, no matter how long the experiment runs, and how many veins are drawn. Also, because I am reusing the same BitmapData object every frame, the amount of RAM that the system uses does not increase.

The simplified code to do the custom caching looks like this:
/* THIS CODE RUNS ONCE */
// Set up the BitmapData object:
canvasBmp = new BitmapData(WIDTH,HEIGHT,true,0);
// Display the bitmap in the “image” clip:
canvas.image.attachBitmap(canvasImage,1);
// Set up the matrix that will be used to draw the veins onto the bitmap:
mat = new Matrix ();
/* THIS CODE RUNS EACH FRAME */
// Draw the new vectors to canvas.veins
// See the Vein source for details.
// Composite the vectors to the BitmapData object each frame:
canvasBmp.draw(canvas.veins,mat);
// Clear out the old vectors:
canvas.veins.clear();

Blurring Remnant Veins

Being able to continually lay down new vein segments without a performance penalty was great, but it quickly became cluttered and well ugly. I decided to use the new blur filter in Flash Player 8 to fade the older vein segments, so that newer veins would be more visible. This was very simple to implement. I set up a BlurFilter instance, then applied it to my BitmapData object in each frame. Over multiple applications of the filter, the blur becomes amplified and provides a nice soft backdrop to lay new veins over.

I show the code in the following color adjustment section.

Applying Color Adjustments

One of the unexpected side effects of applying the blur filter multiple times to the same bitmap data was that the color degraded, eventually leaving me with an unsightly black background. I was able to counter this and create more visual interest by adjusting the color slightly in each frame using an instance of ColorMatrixFilter. I had three goals in mind when applying this filter:

Counter the darkening effect of the iterative blurring
Have the veins shift towards a more cyan (green-blue) color as they faded back
Have the veins fade out over time, to continue revealing the background image
It took a little bit of playing with the numbers in the matrix, but in the end, I wound up with something similar to the following:

import flash.filters.BlurFilter;
import flash.filters.ColorMatrixFilter;
import flash.geom.Rectangle;
import flash.geom.Point;
/* THIS CODE RUNS ONCE */
// set up the blur filter (2×2 blur, quality of 1):
blurF = new BlurFilter(2,2,1);
// set up the color matrix filter:
colorF = new ColorMatrixFilter([
0.97,0,0,0,2, // reduce red fastest
0,0.99,0,0,3, // reduce green slowest
0,0,0.98,0,4, // reduce blue
0,0,0,1,-0.7 // fade down alpha
]);
// set up the rect that we will apply the filter to:
rect = new Rectangle(0,0,WIDTH,HEIGHT);
// set up the point that we will use:
pnt = new Point(0,0);
/* THIS CODE RUNS ONCE EVERY 5 FRAMES */
// apply the filters to the bitmap:
canvasBmp.applyFilter(canvasBmp,rect,pnt,colorF);
canvasBmp.applyFilter(canvasBmp,rect,pnt,blurF);

Where to Go from Here

Download the full source code if you haven’t done so already and take a look at how things work in context. Play with it, tweak numbers, mess with the matrixes, and see what happens. If you build something cool, send me an e-mail-I’d love to see it.

If you’re having some difficulties grasping how the matrixes work, don’t worry, help is on the way-keep an eye on gskinner.com/blog for the gMatrix panel, the ColorMatrix class, and other useful tutorials and code. I have also extended this experiment further into jungle-g . You can see jungle-g and other experiments on my blog.

Grant Skinner is CEO and chief architect of gskinner.com, a Flash development and consulting company. He works with leading new media agencies and progressive corporate clients to create cutting-edge applications, games, and multimedia pieces. Skinner’s expertise in fusing coding with interface design, usability, marketing, and business logic has garnered international acclaim and resulted in a number of prestigious industry awards.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles