Flash Mobile Drawing: Graphics Source Code
Jan 24, 2011
This is an update from the original article Flash Mobile Drawing Performance: The Best Way?
I promised some code showing how to implement the ‘graphics.rect’ solution mentioned in various other articles. First, some quick notes:
- All class files have been flattened down to one so it is easier for anyone wanting to test it out.
- Change the ‘drawMode’ property to toggle between bitmap and graphics drawing approach.
- Apologies to not crediting whomever created ‘FPSMemCounter’ — I’m not sure where it came from.
- There is an embedded image in the example. Should you need a non-embedded solution, you can uncomment out line 39 and comment out 40.
Here’s what the end result will look like:
And here’s the source code:
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.utils.Dictionary;
[SWF(backgroundColor='#000000', frameRate='60')]
public class POC_BlittingGraphics extends Sprite
{
//--------------------------------------------------------------------------
//
// Constants
//
//--------------------------------------------------------------------------
private const DRAW_MODE_GRAPHICS:String = "graphics";
private const DRAW_MODE_BITMAP:String = "bitmap";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
public function POC_BlittingGraphics()
{
super();
//iconBitmap = new Bitmap(new BitmapData(64, 64, false, 0xFF0000));
iconBitmap = new ImageClass() as Bitmap;
var blitmap:BlitData;
if(drawMode == DRAW_MODE_GRAPHICS)
{
canvasShape = new Shape();
this.addChild(canvasShape);
}
else if(drawMode == DRAW_MODE_BITMAP)
{
canvasBitmap = new Bitmap();
this.addChild(canvasBitmap);
}
this.addChild(new FPSMemCounter(0xFFFFFF));
for(var i:int = 0 ; i < 15 ; i++)
{
blitmap = new BlitData();
blitmap.x = 360 * Math.random();
blitmap.y = 660 * Math.random();
blitmap.velocityX = (Math.random() * 4) - 2;
blitmap.velocityY = (Math.random() * 4) - 2;
blitmap.width = iconBitmap.width;
blitmap.height = iconBitmap.height;
blitDataDictionary["data_" + i] = blitmap;
}
this.stage.align = StageAlign.TOP_LEFT;
this.stage.scaleMode = StageScaleMode.NO_SCALE;
//this.stage.quality = StageQuality.LOW;
this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
[Embed(source="bad-egg-icon.png")]
private var ImageClass:Class;
private var iconBitmap:Bitmap;
private var canvasShape:Shape;
private var canvasBitmap:Bitmap;
private var blitDataDictionary:Dictionary = new Dictionary();
private var drawMode:String = DRAW_MODE_BITMAP;
//--------------------------------------------------------------------------
//
// Private methods
//
//--------------------------------------------------------------------------
private function moveActor(blitData:BlitData):void
{
blitData.x += blitData.velocityX;
blitData.y += blitData.velocityY;
if(blitData.x >= stage.stageWidth - blitData.width || blitData.x <= 0)
{
blitData.velocityX *= -1;
}
if(blitData.y >= stage.stageHeight - blitData.height || blitData.y <= 0)
{
blitData.velocityY *= -1;
}
}
private function drawShape(canvasShapeGraphics:Graphics, blitData:BlitData, bd:BitmapData):void
{
canvasShapeGraphics.beginBitmapFill(bd, new Matrix(1, 0, 0, 1, blitData.x, blitData.y), false, false);
canvasShapeGraphics.drawRect(blitData.x, blitData.y, bd.width, bd.height);
}
private function drawBitmap(canvasBitmapData:BitmapData, blitData:BlitData, bd:BitmapData):void
{
canvasBitmapData.copyPixels(bd, bd.rect, new Point(blitData.x, blitData.y));
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
private function onEnterFrame(event:Event):void
{
var blitData:BlitData;
if(drawMode == DRAW_MODE_GRAPHICS)
{
canvasShape.graphics.clear();
for each(blitData in blitDataDictionary)
{
moveActor(blitData);
drawShape(canvasShape.graphics, blitData, iconBitmap.bitmapData);
}
}
else if(drawMode == DRAW_MODE_BITMAP)
{
var bd:BitmapData = new BitmapData(this.stage.stageWidth, this.stage.stageHeight, false, 0x000000);
for each(blitData in blitDataDictionary)
{
moveActor(blitData);
drawBitmap(bd, blitData, iconBitmap.bitmapData);
}
canvasBitmap.bitmapData = bd;
}
}
}
}
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
internal class BlitData
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
public function BlitData()
{
super();
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
public var x:Number;
public var y:Number;
public var velocityX:Number;
public var velocityY:Number;
public var width:Number;
public var height:Number;
}
import flash.display.Stage;
import flash.system.System;
import flash.events. *;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.utils.getTimer;
internal class FPSMemCounter extends TextField
{
private var fontSize : Number; //the font size for the field
private var lastUpdate : Number; // the results of getTimer() from the last update
private var frameCount : Number; //stores the count of frames passed this second
private var currentTime : Number;
private static const UPDATE_INTERVAL : Number = 1000; //the interval at which the frame count will be be posted
public function FPSMemCounter (textColor : Number = 0xFFFFFF, fontSize : Number = 25) : void
{
this.textColor = textColor;
this.fontSize = 12;
//set the field to autosize from the left
autoSize = TextFieldAutoSize.LEFT;
//make the text unselecteable and disable mouse events
selectable = false;
mouseEnabled = false;
addEventListener (Event.ADDED_TO_STAGE, setFPSUpdate);
addEventListener (Event.REMOVED_FROM_STAGE, clearFPSUpdate);
}
//called when the instance is added to a Display Object
private function setFPSUpdate (event : Event) : void
{
addEventListener (Event.ENTER_FRAME, updateFPS);
frameCount = 0;
updateText (frameCount);
lastUpdate = getTimer ();
}
//called when the instance is removed from a Display Object
private function clearFPSUpdate (event : Event) : void
{
removeEventListener (Event.ENTER_FRAME, updateFPS);
}
//update the frame counter
private function updateFPS (event : Event) : void
{
//get the current time and increment the frame counter
currentTime = getTimer ();
frameCount ++;
//post the frame count if more then a second has passed
if (currentTime >= lastUpdate + UPDATE_INTERVAL)
{
lastUpdate = currentTime;
updateText (frameCount);
frameCount = 0;
}
}
//update the display text
private function updateText (frameNum : Number) : void
{
var mem:String = Number( System.totalMemory / 1024 / 1024 ).toFixed( 2 ) + 'Mb';
htmlText = "<font size='" + fontSize + "'><b>FPS : </b>" + frameNum + " fps</b><b> Memory : </b>"+ mem +"</font>";
}
}Finally, the embedded image:
If you have any questions, comments or findings, please leave a comment below.
Stay update to date on the latest in interactive technology news, releases, trends and, of course, our latest tutorials and experiments by following us on Twitter.















[...] have since the source code from [...]
[...] This post was mentioned on Twitter by karannnnnnnnnnn3, Theory Nine. Theory Nine said: How to use graphics.rect to draw #flash mobile games…with source code. http://ow.ly/3J3rf [...]
In your tests, did DRAW_MODE_GRAPHICS perform better only on mobile devices or also on desktops? Testing on my desktop computer, DRAW_MODE_BITMAP was way faster with a large number of iconBitmap instances (1500 in my test). Interestingly though, I used getTimer to determine the code execution time, and DRAW_MODE_GRAPHICS executed about twice as fast, so the bottleneck must be the rendering.
I managed to keep the memory down by adding a position value to BlitData that is a point, and use that for storing x, y instead of creating a new point every frame update. This also gave me an extra 2fps on average. I also used a single BitmapData object and used floodFill to clear it instead of creating a new BitmapData every frame. That gave me an additional 3 fps increase. Making these changes also kept the memory from spiking and causing the garbage collector to run, which was causing brief drops in framerate.
drawShape gained about 2fps by reusing a single matrix, but it was still less than half the speed of drawBitmap. I’m getting ~19fps using drawShape, and ~53fps using drawBitmap.