Anybody who has ever had a performance problem with a graphically-rich Flash app has seen the “Show Redraw Regions” option in the debug player. Well, at least I hope they have since it’s right there in the context menu and there’s even a function call to toggle them. Normally it’s pretty straightforward what gets redrawn and when, but when you start to mix in advanced features like scrollRect and cacheAsBitmap, things get complicated. Today I’m going to cover a technique for eliminating some mysterious redraw regions.

The scrollRect feature allows you to conveniently and efficiently mask a portion of a DisplayObject with a rectangle. These are commonly used to mask gameplay areas, scroller areas, and many other areas that are naturally rectangular. Yet it is common to see redraw regions outside of the scroll rectangle, even with the contents are as simple as a single Bitmap. The solution to this problem is to enable cacheAsBitmap as the following test app shows:

package
{
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.profiler.*;
	import flash.system.*;
	import flash.text.*;
	import flash.ui.*;
	import flash.utils.*;
 
	[SWF(backgroundColor=0xEEEADB,frameRate=30)]
	public class CacheAsBitmapTest extends Sprite
	{
		public function CacheAsBitmapTest()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
 
			var help:TextField = new TextField();
			help.autoSize = TextFieldAutoSize.LEFT;
			help.defaultTextFormat = new TextFormat("_sans");
			help.htmlText = "Press SPACE to toggle cacheAsBitmap\n"
				+ "By "
				+ "<font color=\"#0071BB\">" 
				+ "<u>"
				+ "<a href=\"http://jacksondunstan.com\">Jackson Dunstan</a>" 
				+ "</u>" 
				+ "</font>";
			addChild(help);
 
			var status:TextField = new TextField();
			status.autoSize = TextFieldAutoSize.LEFT;
			status.defaultTextFormat = new TextFormat("_sans");
			status.text = "cacheAsBitmap: false";
			status.y = help.height;
			addChild(status);
 
			if (!Capabilities.isDebugger)
			{
				var warning:TextField = new TextField();
				warning.autoSize = TextFieldAutoSize.LEFT;
				warning.defaultTextFormat = new TextFormat("_sans");
				warning.htmlText = "<b>WARNING: this app is meant to be run in" + 
					" a debug version of Flash Player</b>";
				warning.y = status.y + status.height;
				addChild(warning);
			}
 
			var dir:int = 1;
			var scrollRect:Rectangle = new Rectangle(0, 0, 50, 50);
 
			var bmd:BitmapData = new BitmapData(200, 200);
			bmd.noise(Math.random()*10000);
 
			var map:Bitmap = new Bitmap(bmd);
			map.x = map.y = 200;
			map.scrollRect = scrollRect;
			addChild(map);
 
			showRedrawRegions(true, 0xff0000);
 
			var lastTime:int = getTimer();
			addEventListener(
				Event.ENTER_FRAME,
				function(ev:Event): void
				{
					var elapsed:int = getTimer() - lastTime;
					lastTime = getTimer();
 
					var moveAmount:int = (elapsed*dir) / 20;
					scrollRect.x = scrollRect.y = scrollRect.x + moveAmount;
					if (scrollRect.x > 150)
					{
						scrollRect.x = scrollRect.y = 150;
						dir = -dir;
					}
					else if (scrollRect.x < 0)
					{
						scrollRect.x = scrollRect.y = 0;
						dir = -dir;
					}
					map.scrollRect = scrollRect;
				}
			);
 
			stage.addEventListener(
				KeyboardEvent.KEY_DOWN,
				function(ev:KeyboardEvent): void
				{
					if (ev.keyCode == Keyboard.SPACE)
					{
						map.cacheAsBitmap = !map.cacheAsBitmap;
						status.text = "cacheAsBitmap: " + map.cacheAsBitmap;
						showRedrawRegions(false, 0xff0000);
						showRedrawRegions(true, 0xff0000);
					}
				}
			);
		}
	}
}

To appreciate this, you really need to run it in the debug plugin (or standalone player). A simple screenshot will not suffice. So here is the app in action:

App

Notice that when cacheAsBitmap is turned on the redraw region is restricted to just the scroll rectangle, but when it’s turned off the redraw region is the entire Bitmap in the scroll rectangle and a very large portion of the stage is redrawn. It’s rather unintuitive to enable cacheAsBitmap on a Bitmap, but it does fix this problem and just might save the day when performance is at a premium.

This is the first time I’ve used the above Flash embedding script, so please let me know if you have any issues with it.