To "blit" is to copy bits from one part of a computer's graphical memory to another part. This technique deals directly with the pixels of an image, and draws them directly to the screen, which makes it a very fast rendering technique that's often perfect for fast-paced 2D action games.
Blitting in Practice!
Most gamedev platforms support some form of blitting; here I'll use regular Flash and AS3 for a web-friendly demo.
First we need to know how to create a surface in graphical memory. In Flash, we can do so like this:
var bmd:BitmapData = new BitmapData(550, 500);
This creates a rectangle made up of 550 times 500 pixels - i.e. dots of colour. We can then display these pixels by passing this pixel data to a bitmap object and adding it to the screen:
var bitmap:Bitmap = new Bitmap(bmd); addChild(bitmap); //add to the screen using Flash's display list
This will simply display a 550x500px white rectangle, since white is the default colour for pixels in a new
Now let's suppose we want to draw a background onto this rectangle. We can do this by copying the pixels of our background image directly into the existing
BitmapData object, rather than adding a new image to the screen.
//not shown: importing an image into the backgroundImage object. var backgroundRectangle:Rectangle = new Rectangle(); backgroundRectangle.x = 0; backgroundRectangle.y = 0; backgroundRectangle.width = backgroundImage.width; backgroundRectangle.height = backgroundImage.height; bmd.copyPixels(backgroundImage, backgroundRectangle, new Point(0,0));
In the above code, we imported an image into the
backgroundImage object (this could be a JPEG photograph, an image from the user's webcam, a procedurally generated background - it doesn't matter), and then defined a rectangle that outlined an area of this image (in this case, we've just outlined the entire image).
Then, we copied the pixels from this rectangular area of the image into the existing 550x500px
BitmapData object from before - the
new Point(0,0) just says that we should start at the co-ordinates
(0,0) (i.e. the top left corner) of the 550x500px
Assuming the imported image is 550x500px as well, this means it will completely cover our plain white
BitmapData. Now, since our
bmp Bitmap is linked to this
BitmapData, we'll see the image appear on the screen!
We can then add another image on top of this. Let's say that
faceImage is a 75x75px picture of a face, with a transparent background. We can simply do this:
//not shown: importing an image into the faceImage object. var faceRectangle:Rectangle = new Rectangle(); faceRectangle.x = 0; faceRectangle.y = 0; faceRectangle.width = faceImage.width; faceRectangle.height = faceImage.height; bmd.copyPixels(faceImage, faceRectangle, new Point(Math.random() * 550, Math.random()*500));
It's almost the same code as before: the pixels from the face image get copied onto the
BitmapData, on top of the existing pixels that were copied from the background image. The big difference is, we copy the face to a random position, rather than
Here's how this looks in action:
It's important to mention that this face image is not simply "layered" on top of the background image; the pixels of the (originally plain white)
BitmapData were each individually changed to match the pixels of the background image, and then a 75x75px area of pixels were changed again to match the pixels of the face image. When you reset the demo above, all of the pixels in the 550x500px
BitmapData are changed to match the pixels of the background image again.
Demonstrating the Speed
Because the computer is directly changing the individual pixels of the bitmap, rather than attempting to keep track of several different layers, this method is incredibly fast. In the demo below, I've added some simple physics, and am blitting hundreds of images per second.
Although blitting is already very fast, there are things we can do to make it even faster.
First, we can lock the bitmap before making any changes to the
BitmapData that it is linked to.
While the bitmap is unlocked, it will attempt to render all of the changes made to the
BitmapData as those changes are made - so if we blit 100 images in a single loop, then the computer has to deal with rendering all those changes, one after the other, in a very short space of time.
Instead, we can lock the bitmap, blit the 100 objects to the
BitmapData, and then unlock the bitmap; the bitmap won't change while it is locked, even though the
BitmapData changes 100 times. That means that the computer only has to re-render the bitmap once per loop, rather than 100 times - much faster!
A second optimisation technique is to reduce the number of pixels that we blit at any one time. For example, let's suppose we blit a background image, and then blit a face image on top of that, and then we want to remove the face and go back to a blank background. (Remember, the images aren't layered; the pixels of the face have been copied over the pixels of the background.)
The most straightforward way to do this is to simply blit the entire 550x500px background image onto the
BitmapData again, covering up everything that was there before.
But here's an alternative: we can figure out what area of the screen the face image is covering up, and then blit a rectangular section of the background over that area! This technique is known as dirty rectangles.
Blitting is often a great choice of rendering method for 2D action games as it provides a fast way to make lots of individual changes to the screen very quickly, due to manipulating the pixels directly.
It's often less memory-intensive than other methods of rendering, too, since less graphical information needs to be stored - everything's in one single bitmap.
Bear in mind that this speed and power usually comes at the price of convenience - for instance, if you want to make a foreground object shrink to nothing, then you can't simply alter its
height values; you have to redraw the background on top of it and then redraw the object slightly smaller, over and over again.