By Michael James Williams
 
Created
22 February 2010
 

Requirements

 
Prerequisite knowledge

You should have a basic understanding of the Flash authoring interface, be able to work with movie clips, and know how to create a basic ActionScript 3 project. You should also be familiar with the topics covered in Rendering game assets in ActionScript using blitting techniques and Flash Builder 4.
 

 
User level

Intermediate
 

 
Required products

 
Sample files

Blitting is a higher-performance alternative to using the built-in display list in Adobe Flash for drawing objects on the Stage. This technique involves copying the individual pixels of an existing image directly on to the screen—a bit like painting all of your game's spaceships and monsters onto a canvas.
 
Working manually with pixels is more complicated than using the display list, but the improvement in performance more than makes up for the extra effort. For situations where there are many objects moving around the Stage, you must usually choose either a smooth frame rate or a small memory footprint. With blitting you can have both.
 
The basic concepts of blitting are covered in Rendering game assets in ActionScript using blitting techniques and Flash Builder 4 by Renaun Erickson. In this tutorial, you'll learn how to cache an animated movie clip symbol as an array of bitmaps and then blit it to the screen, all within the Flash authoring environment.
 
Note: Throughout this tutorial, I use the term global to refer to any public instance variable that is defined outside of all functions and whose scope is the entire document class.
 

 
Blitting a movie clip

To start, you'll need a movie clip on the Stage. Think of it as an artist's reference:
 
  1. Download the sample ZIP file and extract it to your hard drive.
  2. Open Blitting.fla in your Flash authoring environment. Its document class is set to the DocumentClass.as file and its library contains an animated movie clip called BlockFace.
  3. Open DocumentClass.as, create a new global BlockFace instance, and add it to the left side of the Stage. Your code should look like this:
package { import flash.display.MovieClip; public class DocumentClass extends MovieClip { public var blockFace:BlockFace; public function DocumentClass() { blockFace = new BlockFace(); blockFace.x = 85; blockFace.y = 160; addChild(blockFace); } } }
Note: Code changes are highlighted as shown.
 
  1. Save the file and choose Control > Test Movie to test the movie and make sure it is working (see Figure 1).
You should see this friendly face animating on the left of the Stage.
 
Figure 1 . You should see this friendly face animating on the left of the Stage.
  1. Prepare a canvas to which you can blit copies of this face (a canvas is just a rectangular Bitmap object filled with a single color). Here is the new code:
package { import flash.display.MovieClip; //import this class import flash.display.Bitmap; public class DocumentClass extends MovieClip { public var blockFace:BlockFace; //create a public var to hold the canvas bitmap public var canvasBitmap:Bitmap; public function DocumentClass() { blockFace = new BlockFace(); blockFace.x = 85; blockFace.y = 160; addChild(blockFace); //create the canvas bitmap, position on stage, //and add it to the display list canvasBitmap = new Bitmap(); canvasBitmap.x = 0; canvasBitmap.y = 0; addChild(canvasBitmap); } } }
If you test the movie now, you'll see no visible change to the SWF yet. Bitmaps don't contain pixels themselves, BitmapData objects do. Bitmaps merely display the pixels that are inside a given BitmapData object.
 
  1. Create a BitmapData object, fill it with a solid color, and link the canvas bitmap to it:
package { import flash.display.MovieClip; import flash.display.Bitmap; //import these two classes import flash.display.BitmapData; import flash.geom.Rectangle; public class DocumentClass extends MovieClip { public var blockFace:BlockFace; public var canvasBitmap:Bitmap; //assign a new public variable to hold the canvas's pixels public var canvasBitmapData:BitmapData; //create a new rectangle the size of the canvas //(use the stage's dimensions) public var canvasRectangle:Rectangle = new Rectangle(0,0,550,400); public function DocumentClass() { blockFace = new BlockFace(); blockFace.x = 85; blockFace.y = 160; addChild(blockFace); canvasBitmap = new Bitmap(); //create the actual BitmapData instance //(give it the same dimensions as the stage) canvasBitmapData = new BitmapData(550, 400); //fill the canvas's pixels with a rectangle of color //(the rectangle is as large as the canvas, //and 0xFFDDDDDD defines the color gray) canvasBitmapData.fillRect(canvasRectangle,0xFFDDDDDD) //link the canvas Bitmap to this BitmapData //(in effect telling it to display these pixels) canvasBitmap.bitmapData = canvasBitmapData; canvasBitmap.x = 0; canvasBitmap.y = 0; addChild(canvasBitmap); } } }
  1. Test the movie; you'll see that the gray canvas takes up the whole Stage and covers the movie clip.
  2. To get around this for now, just move it 275 pixels to the right by replacing this:
canvasBitmap.x = 0;
with the following:
 
canvasBitmap.x = 275;
  1. Save your changes and test the movie (see Figure 2).
The movie clip sits on the left side of the Stage; the canvas takes up the right.
 
Figure 2. The movie clip sits on the left side of the Stage; the canvas takes up the right.
 
Blitting the movie clip to the canvas
There are two steps involved in actually blitting an object to the canvas. Essentially, you just draw an image of whatever you want to blit into a new BitmapData object, and then copy the pixels from that object on to the canvas:
 
  1. Create a new BitmapData object to hold an image of BlockFace, and draw an image of the BlockFace instance into this object. Your DocumentClass() function should now look like this:
public function DocumentClass() { blockFace = new BlockFace(); blockFace.x = 85; blockFace.y = 160; addChild(blockFace); canvasBitmap = new Bitmap(); canvasBitmapData = new BitmapData(550, 400); canvasBitmapData.fillRect(canvasRectangle,0xFFDDDDDD) canvasBitmap.bitmapData = canvasBitmapData; canvasBitmap.x = 275; canvasBitmap.y = 0; addChild(canvasBitmap); //create new BitmapData to hold image of BlockFace var blockFaceBitmapData:BitmapData = new BitmapData(blockFace.width, blockFace.height); //draw an image of the BlockFace instance into this BitmapData blockFaceBitmapData.draw(blockFace); }
  1. The next step is to copy the pixels from this BitmapData object into the canvas's BitmapData object. The function for this is called copyPixels (appropriately enough) and requires three arguments:
    • The BitmapData object to copy from
    • A Rectangle that defines the region of that BitmapData to copy
    • A Point that defines where to position the copied pixels
  2. Create a Rectangle that's the same width and height as blockFace so that the entire image is copied.
  3. Define a Point to specify the desired location of the blitted image.
  4. Call the copyPixels() function on your canvas's BitmapData. Your DocumentClass() function will now look like this:
public function DocumentClass() { blockFace = new BlockFace(); blockFace.x = 85; blockFace.y = 160; addChild(blockFace); canvasBitmap = new Bitmap(); canvasBitmapData = new BitmapData(550, 400); canvasBitmapData.fillRect(canvasRectangle,0xFFDDDDDD) canvasBitmap.bitmapData = canvasBitmapData; canvasBitmap.x = 275; canvasBitmap.y = 0; addChild(canvasBitmap); var blockFaceBitmapData:BitmapData = new BitmapData(blockFace.width, blockFace.height); blockFaceBitmapData.draw(blockFace); //create a Rectangle that defines the region of the BlockFace BitmapData to copy var areaRectangle:Rectangle = new Rectangle(); areaRectangle.width = blockFace.width; areaRectangle.height = blockFace.height; //create a Point that defines where on the canvas the pixels of the BlockFace BitmapData should be copied to //(this will the same point on the canvas as the original BlockFace is on the stage) var destinationPoint:Point = new Point(blockFace.x, blockFace.y); //actually copy the pixels from the BlockFace BitmapData to the canvas BitmapData canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); }
  1. Add import flash.geom.Point up at the top of your code with the other import statements.
  2. Save your changes and test your movie; you'll see two faces (see Figure 3).
The left face is a movie clip, the right face is a pixel-by-pixel copy.
 
Figure 3. The left face is a movie clip; the right face is a pixel-by-pixel copy.
 
Animating the blitted image
This is a good start. However, since you blitted the image just once, only the left face animates. The blitted face doesn't animate for the same reason the screenshots in this tutorial don't animate: the pixels were copied at a specific moment, and they aren't being changed over time.
 
If you blit the face every frame, it will animate exactly like the movie clip. The easiest way to do that is to move all the blitting code to an ENTER_FRAME event handler:
 
  1. Import the Event class by adding the following line up at the top of the code with the other import statements:
import flash.events.Event;
  1. Add a new ENTER_FRAME event listener in the constructor by adding the following line just after the line that calls canvasBitmapData.copyPixels():
addEventListener(Event.ENTER_FRAME, onEnterFrame);
  1. Create the corresponding event handler and move all the blitting code into it (delete the matching lines from the DocumentClass()function):
public function onEnterFrame(evt:Event):void { var blockFaceBitmapData:BitmapData = new BitmapData(blockFace.width, blockFace.height); blockFaceBitmapData.draw(blockFace); var areaRectangle:Rectangle = new Rectangle(); areaRectangle.width = blockFace.width; areaRectangle.height = blockFace.height; var destinationPoint:Point = new Point(blockFace.x, blockFace.y); canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); }
  1. Run your SWF now, and you'll see both faces animate in unison. So far, so good—but there's a small problem. Each image is being blitted over the previous image.
  2. Add the following line right at the end of your onEnterFrame function:
blockFace.y = blockFace.y + 1;
  1. Save the change and test your movie (see Figure 4).
Both faces move down the screen, but the blitted image leaves a trail.
 
Figure 4. Both faces move down the screen, but the blitted image leaves a trail.
Because the blitted image is being copied on top of the old pixels each time, you can still see the old image beneath it. As explained in Renaun's article, you can fix this by simply repainting the whole canvas gray at the start of each frame.
 
  1. Use the same fillRect call you used earlier to paint the entire canvas gray; add the following line at the start of your onEnterFrame()function:
canvasBitmapData.fillRect(canvasRectangle, 0xFFDDDDDD);
Good, that fixes it. Now that you know it's not an issue anymore, you can remove the line that moves the face every frame.
 
  1. Delete blockFace.y = blockFace.y + 1; from onEnterFrame().
 
Creating multiple copies
How about blitting a couple more copies of the face to the canvas? All you need to do is duplicate the actual blitting code from your onEnterFrame()function and change the positions of the new faces:
 
public function onEnterFrame(evt:Event):void { canvasBitmapData.fillRect(canvasRectangle, 0xFFDDDDDD); blockFace.y = blockFace.y + 1; var blockFaceBitmapData:BitmapData = new BitmapData(blockFace.width, blockFace.height); blockFaceBitmapData.draw(blockFace); var areaRectangle:Rectangle = new Rectangle(); areaRectangle.width = blockFace.width; areaRectangle.height = blockFace.height; var destinationPoint:Point = new Point(blockFace.x, blockFace.y); canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); var blockFaceBitmapData:BitmapData = new BitmapData(blockFace.width, blockFace.height); blockFaceBitmapData.draw(blockFace); var areaRectangle:Rectangle = new Rectangle(); areaRectangle.width = blockFace.width; areaRectangle.height = blockFace.height; //put this blitted face in a different position to the others var destinationPoint:Point = new Point(blockFace.x + 10, blockFace.y - 120); canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); var blockFaceBitmapData:BitmapData = new BitmapData(blockFace.width, blockFace.height); blockFaceBitmapData.draw(blockFace); var areaRectangle:Rectangle = new Rectangle(); areaRectangle.width = blockFace.width; areaRectangle.height = blockFace.height; //put this blitted face in a different position to the others var destinationPoint:Point = new Point(blockFace.x + 20, blockFace.y + 120); canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); }
This is a clumsy way of doing it. Flash will give you a few warnings about duplicate variable definitions but it will compile a working SWF nonetheless (see Figure 5).
 
Figure 5: Three blitted copies of one movie clip, all animating.

 
Caching a movie clip

 
 
 
 
The method used to animate the three faces is messy and far from optimal, so in this section you'll learn how to cache these objects, saving the pixels from the original BlockFace movie clip so that you don't have to draw them to three BitmapData objects every single frame.
 
 
Removing unnecessary repetition
If you read through that duplicated code in the onEnterFrame()function, you'll see that there are two parts of the code that don't need to be copied and pasted.
 
The first is the section below, in which the BlockFace object gets drawn to the BitmapData object:
 
 
 
 
 
var blockFaceBitmapData:BitmapData = new BitmapData(blockFace.width, blockFace.height); blockFaceBitmapData.draw(blockFace);
The second part is the code that defines the rectangle of pixels to copy from the BlockFace object's BitmapData:
 
var areaRectangle:Rectangle = new Rectangle(); areaRectangle.width = blockFace.width; areaRectangle.height = blockFace.height;
Delete the second and third occurrences of these lines of code so that they are run only once per frame. This won't affect the SWF, and by removing them you can eliminate the Flash compiler warnings they generated. Also change the second and third instances of var destinationPoint:Point to simply destinationPoint.
 
The onEnterFrame()event handler should now look like this:
 
 
 
 
 
public function onEnterFrame(evt:Event):void { canvasBitmapData.fillRect(canvasRectangle, 0xFFDDDDDD); var blockFaceBitmapData:BitmapData = new BitmapData(blockFace.width, blockFace.height); blockFaceBitmapData.draw(blockFace); var areaRectangle:Rectangle = new Rectangle(); areaRectangle.width = blockFace.width; areaRectangle.height = blockFace.height; var destinationPoint:Point = new Point(blockFace.x, blockFace.y); canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); //remove the "var" from the front of this statement //(you've already declared this var a few lines above) destinationPoint = new Point(blockFace.x + 10, blockFace.y - 120); canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); destinationPoint = new Point(blockFace.x + 20, blockFace.y + 120); canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); }
In fact, you could even move the code concerning areaRectangle into the constructor, rather than having it run every single frame.
 
What else could be removed? Well, you have to repaint the canvas every frame to stop the trail effect, so that has to stay. Likewise destinationPoint has to be set once for each blitted face and must be updated every frame in case the original face moves, so that has to be kept too.
 
The rest of the code handles the actual copying of the pixels from the original face to the canvas and it has to be repeated. Obviously that can't be changed or you wouldn't see anything. Or can it?
 
Remember, that section of the code is in two parts—the pixels aren't copied directly from the face to the canvas. First, the face is drawn onto the BitmapData object as a bunch of pixels; second, the pixels are copied from this BitmapData object onto the canvas. The second part cannot be removed, but the first part could certainly be improved.
 
Instead of drawing the face to the BitmapData object every frame, you could create a number of BitmapData objects in the constructor and draw one frame of the face's animation to each of them. Then, all the onEnterFrame()function has to do is select the correct BitmapData object and copy the pixels from it to the canvas! That involves more work in the constructor function but it really cuts down on the work done each frame. Even better, it means you can remove the BlockFace movie clip, so Flash Player won't have to render it each frame and all animation will be handled via blitting.
 
 
Caching the first frame
As a test, try caching only the first frame to begin with:
 
  1. Move the following lines into the constructor function:
var blockFaceBitmapData:BitmapData = new BitmapData(blockFace.width, blockFace.height); blockFaceBitmapData.draw(blockFace);
Now you've cached the pixels of the first frame of the animation to blockFaceBitmapData; you're no longer drawing them from the BlockFace movie clip every frame.
 
  1. You will need to change blockFaceBitmapDatainto a global variable, of course, or the code in the onEnterFramefunction won't be able to access it.
  2. While you're at it, you may as well do the same thing with the lines that set up the areaRectangle.
  3. Remember to change areaRectangleto a global variable as well. Your code will then look like this:
package { import flash.display.MovieClip; import flash.display.BitmapData; import flash.display.Bitmap; import flash.geom.Rectangle; import flash.geom.Point; import flash.events.Event; public class DocumentClass extends MovieClip { public var blockFace:BlockFace; public var canvasBitmap:Bitmap; public var canvasBitmapData:BitmapData; public var canvasRectangle:Rectangle = new Rectangle(0,0,550,400); //made blockFaceBitmapData into a class-wide variable, so that both functions can access it public var blockFaceBitmapData:BitmapData; public function DocumentClass() { blockFace = new BlockFace(); blockFace.x = 85; blockFace.y = 160; addChild(blockFace); canvasBitmap = new Bitmap(); canvasBitmapData = new BitmapData(275, 400); canvasBitmapData.fillRect(canvasRectangle, 0xFFDDDDDD); canvasBitmap.bitmapData = canvasBitmapData; canvasBitmap.x = 275; canvasBitmap.y = 0; addChild(canvasBitmap); //these lines were moved from onEnterFrame to here //also, removed the "var" from the start of this next line blockFaceBitmapData = new BitmapData(blockFace.width, blockFace.height); blockFaceBitmapData.draw(blockFace); var areaRectangle:Rectangle = new Rectangle(); areaRectangle.width = blockFace.width; areaRectangle.height = blockFace.height; addEventListener(Event.ENTER_FRAME, onEnterFrame); } public function onEnterFrame(evt:Event):void { canvasBitmapData.fillRect(canvasRectangle, 0xFFDDDDDD); var destinationPoint:Point = new Point(blockFace.x, blockFace.y); canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); destinationPoint = new Point(blockFace.x + 10, blockFace.y - 120); canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); destinationPoint = new Point(blockFace.x + 20, blockFace.y + 120); canvasBitmapData.copyPixels(blockFaceBitmapData,areaRectangle,destinationPoint); } } }
The blitted faces will all appear (and they'll even move with the original face if you re-insert the line that moves it), but since you've only cached one frame, none of them animates. The next step, then, is to cache all of the frames.
 
 
Caching the entire movie clip
Rather than manually create dozens of individual BitmapData objects, you can use an array and a loop to automate the process:
 
  1. Create a new global array by inserting the highlighted line right after the line that defines the document class:
public class DocumentClass extends MovieClip { public var blockFaceArray:Array;
  1. Add the following line after the addChild(canvasBitmap);call:
blockFaceArray = new Array();
 
 
 
 
You'll use this array to hold a number of BitmapData objects, one for each frame of the BlockFace movie clip.
 
  1. Create a loop that iterates through all the frames in the BlockFace movie clip, draws each frame to a new BitmapData object (using the code you just moved into the constructor), and adds this object to the array. Here's what the code will look like:
 
 
 
 
public function DocumentClass() { blockFace = new BlockFace(); blockFace.x = 85; blockFace.y = 160; addChild(blockFace); canvasBitmap = new Bitmap(); canvasBitmapData = new BitmapData(275, 400); canvasBitmapData.fillRect(canvasRectangle, 0xFFDDDDDD); canvasBitmap.bitmapData = canvasBitmapData; canvasBitmap.x = 275; canvasBitmap.y = 0; addChild(canvasBitmap); //loop through all the frames in the BlockFace movie clip for ( var i:int = 1; i <= blockFace.totalFrames; i++ ) { //move the movie clip to the "i-th" frame blockFace.gotoAndStop(i); //these three lines have been moved into the loop from below blockFaceBitmapData = new BitmapData(blockFace.width, blockFace.height); blockFaceBitmapData.draw(blockFace); blockFaceArray.push(blockFaceBitmapData); } //reset the animation to its first frame blockFace.gotoAndPlay(1); //note that the three lines that draw an image of BlockFace into a BitmapData have been moved from here into the above loop areaRectangle.width = blockFace.width; areaRectangle.height = blockFace.height; addEventListener(Event.ENTER_FRAME, onEnterFrame); }
 
 
 
 
You now have an array in which each element is a BitmapData object containing a frame of animation. Next, you need to get these BitmapData objects back out of the array and blit them to the canvas.
 
  1. Add the following line to the start of your onEnterFrame()function:
 
 
 
 
var currentBlockFaceFrame:BitmapData = blockFaceArray[blockFace.currentFrame - 1];
 
 
 
 
It's very important to subtract 1 from the animation's current frame, since the first frame of an animation is numbered 1 but the first element of an array is numbered 0. Otherwise, when the animation reaches the last frame, there'll be no corresponding element in the array and you'll get an error.
 
  1. Change the three lines that call copyPixels()so that they reference this new BitmapData object; for example:
 
 
 
 
canvasBitmapData.copyPixels(currentBlockFaceFrame,areaRectangle,destinationPoint);
  1. Test the SWF (see Figure 6).
Figure 6: The three blitted faces are now animating from a cache in an array.
 
 
 
 
Success! All the faces animate.
 
 
Removing the MovieClip from the display list
Now that the blitted faces are animating from a cache, the original movie clip can be removed from the Stage and Flash Player won't have to render it anymore. However, there are a couple of references to blockFacein the onEnterFrame()function that you'll have to eliminate.
 
First, the code picks which element of the array to use based on the current frame of the movie clip, so you need to store this in another way:
 
  1. Create a global integer variable to hold the current frame by inserting the highlighted line right after the line that defines the document class:
 
 
 
 
public class DocumentClass extends MovieClip { public var currentBlitFrame:int = 0;
  1. Alter your onEnterFrame()function to use this new variable rather than blockFace.currentFrame:
public function onEnterFrame(evt:Event):void { //manually increase this current frame by 1 currentBlitFrame = currentBlitFrame + 1; //if this value goes past the end of the array, reset it to zero if ( currentBlitFrame >= blockFaceArray.length ) { currentBlitFrame = 0; } //grab the BitmapData that is stored in the element of the array corresponding to the currentBlitFrame variable var currentBlockFaceFrame:BitmapData = blockFaceArray[currentBlitFrame]; canvasBitmapData.fillRect(canvasRectangle, 0xFFDDDDDD); var destinationPoint:Point = new Point(blockFace.x, blockFace.y); //replace the reference to "blockFaceBitmapData" with one to the newly-created "currentBlockFaceFrame" canvasBitmapData.copyPixels(currentBlockFaceFrame,areaRectangle,destinationPoint); destinationPoint = new Point(blockFace.x + 10, blockFace.y - 120); //replace the reference to "blockFaceBitmapData" with one to the newly-created "currentBlockFaceFrame" canvasBitmapData.copyPixels(currentBlockFaceFrame,areaRectangle,destinationPoint); destinationPoint = new Point(blockFace.x + 20, blockFace.y + 120); //replace the reference to "blockFaceBitmapData" with one to the newly-created "currentBlockFaceFrame" canvasBitmapData.copyPixels(currentBlockFaceFrame,areaRectangle,destinationPoint); }
Note that all the destinationPointobjects are based on the location of blockFace. You can remove this reference by using exact numbers.
 
  1. Replace this:
var destinationPoint:Point = new Point(blockFace.x, blockFace.y);
with the following line:
 
var destinationPoint:Point = new Point(85, 160);
  1. Replace this:
destinationPoint = new Point(blockFace.x + 10, blockFace.y - 120);
with the following line:
 
destinationPoint = new Point(95, 40);
  1. Replace this:
destinationPoint = new Point(blockFace.x + 20, blockFace.y + 120);
with the following line:
 
destinationPoint = new Point(105, 280);
 
 
 
 
Finally, there are no references to blockFaceoutside the constructor.
 
  1. Delete the addChild(blockFace)line and add the following line at the very end of the constructor function (this proves that it is no longer used elsewhere):
 
 
 
 
blockFace = null;
  1. Test the SWF (see Figure 7).
Figure 7. The movie clip has been removed, and the blitted faces still appear.
All that's left to do now is move the canvas to its rightful place at the left edge of the screen:
 
  1. Replace canvasBitmap.x = 275;with canvasBitmap.x = 0;.
  2. If you want, you can change the canvas color to white by using the color code 0xFFFFFFFFinstead of 0xFFDDDDDD.
  3. You can also declare the blockFaceand blockFaceBitmapDatavariables in the constructor, since they no longer need to be accessed anywhere else.

 
Where to go from here

Congratulations! You've managed to manually render and animate a movie clip that was created in Flash by manipulating the individual pixels. OK, so the final result doesn't look like anything you couldn't have achieved using the standard display list, but you've learned a powerful technique that will enable you to improve performance when you need to create more complex animations.
 
There is much more to blitting than I've covered in this article. Not all situations will be as simple as the example presented here. For example, if you want to cache the animation of a movie clip with varying frame sizes, you'll need to figure out the largest width and height of all the frames and create your BitmapData object at this size. If your movie clip's registration point is not at the top-left corner, you'll need to pass a transform matrix to the draw() function of your BitmapData.
 
You lose some functionality when blitting, too. Rotating and scaling is tricky; usually the best option is to precache all the frames multiple times, at all the different rotations and scales you'll need. Consider also that the display list allows you to rearrange the z-order of objects, automatically stop/start/skip animations, use 9-slice scaling, and so on. If you need any of this functionality when blitting, you'll have to code it yourself.
 
On the other hand, blitting allows for some cool graphical effects. For instance, if you apply a 10% fade to your canvas every frame instead of repainting it with a blank background, that trail goes from being an annoying bug to a cool feature.
 
It's important to remember that blitting is just one tool in your toolbox. Sometimes it will turn out to be the best choice (especially when you need to render a large number of objects and move them around the screen) but sometimes the flexibility of the display list will make that the better choice instead.
 
Here are some great resources for further reading: