Oct 98 Getting Started
Volume Number: 14
Issue Number: 10
Column Tag: Getting Started
by Dave Mark and Dan Parks Sydow
How a Mac program generates smooth, flicker-free
animation
After learning the basics of programming, the new programmer invariably raises the
question of how to go about including animated effects in his or her application. This is
true regardless of the programmer's system of choice, but it's especially the case
among those interested in programming the Mac. From its inception, the Macintosh has
been the machine that brought computers and graphics together for the masses. Mac
users love to see animation, and Mac programmers love to include animation in their
programs. Now that previous columns have covered the basics of the Macintosh
Toolbox, it's time to move on to the basics of animation. This column covers black and
white animation using QuickDraw bitmaps. A couple of columns down the road (after
we cover Color QuickDraw), we'll revisit this month's technique and see how to adapt
it for use in color animation.
The Wrong Way to Animate
If you've ever attempted to write an arcade-style game, you've probably tried your
hand at moving an object over a background. If you found that the moving object
obscured the background as it moved across it you were certainly less then thrilled.
The same holds true if the object appeared to flicker incessantly as it traveled its path.
If you're very familiar with these failed animation methods (we all went through the
process as we learned how to do it right!), you'll be pleased to find out that while the
results were less than ideal, the methods were close to being correct. As such, taking a
brief look at the incorrect ways of performing animation serve as a worthwhile
prelude to a discussion of how to properly perform computer animation.
A programmer attempting animation for the first time often does so by drawing a
background picture in a window and then drawing a foreground object atop the
background. Invariably, the attempt at animation then involves repeatedly offsetting
the foreground object a bit and redrawing it. While the foreground object will appear
to move across the background, the result is unacceptable - a part of each previously
drawn image remains exposed, leaving a blurry trail behind the moving object.
Figure 1 illustrates this for a cartoon vacuum moving over a gray background (hey,
every game doesn't have to be a shoot 'em up, right?).
Figure 1. The result of animating without regard for clearing the background.
Having seen the folly of his ways, the inexperienced animator next tries to remedy the
situation by redrawing the background image so as to cover any old foreground image
remnant. The foreground image is then offset a bit and redrawn over the fresh
background. This corrects the problem of foreground image "leftovers," but introduces
the new problem of flicker. When the background image is redrawn in order to cover
up the foreground image, it obscures the entire drawing. Taking a snapshot of a few
"frames" of this type of animation results in the three views of one window shown in
Figure 2. Looking from left to right at this figure reveals that the object has moved
along a bit, but in between the movement the user will notice the momentarily
blanking of the window.
Figure 2. Clearing the background between foreground redrawing creates flicker.
The Offscreen Bitmap Solution
As you know from previous Getting Started columns, most programs deal with
repainting a window's content by responding to updateEvts generated by the Window
Manager. When an area of a window that was previously obscured needs to be redrawn,
the Window Manager adds the newly revealed area to the window's update region and
generates an updateEvt for the window. The technique of relying on update events to
refresh a window's content area works fine for programs that don't need to incorporate
smooth, fast, animated effects. When it comes to animation, the problem with this
approach is that update events take time. It takes time for the Window Manager to
calculate the update region and it takes time to post an event. More importantly, it
takes time for your program to respond to an update event. If your program is busy
responding to another event, the update event might sit in the queue for a while,
leaving the window undrawn until you get around to fixing it.
When you've got a rocket ship shooting across a planet's surface, you don't want to
leave any holes in the planet, waiting for your program to respond to an update event.
You want to fill in the holes in real time, just like the System does when it handles
your cursor.
Fortunately, the previously discussed problem of flicker and the just-mentioned
problem of slow updating are both easily overcome by employing a programming
technique known as offscreen bitmap animation. A bitmap is one way to represent a
graphical object. On the Mac, a bitmap is a representation of a monochrome (black and
white) image. The map is a grid of pixels, with each pixel considered either on or off.
In specifying the state of each pixel (which in memory is represented by a single bit)
in a given area, you define a single image, or picture. After defining a bitmap, your
next task is to animate it. That is, your program needs to smoothly move it over a
background in a window.
Your Mac's cursor is an example of a bitmap, and the cursor's movement is a perfect
instance of bitmap animation. As the cursor moves around the screen, it appears to
float over the background without flickering. Consider the sequence of pictures in
Figure 3.
Figure 3. The movement of the cursor provides an example of smooth animation.
The leftmost part of Figure 3 shows an arrow cursor partially obscuring a hard
drive icon. Once the cursor moves, it leaves an area of the hard drive icon undrawn -
as shown in the center part of the figure. As shown in the rightmost part of Figure 3,
before this "hole" gets noticed, the system fills it back up with its previous contents.
Implementing the Offscreen Bitmap Technique
An offscreen bitmap is a bitmap that is drawn in memory, but does not appear on
screen. To achieve an animated effect, a total of three offscreen bitmaps are needed.
One of these bitmaps holds a background image - the image over which a foreground
image will appear to traverse. A second bitmap holds the foreground image. The third
offscreen bitmap is a combination of the previous two offscreen bitmaps. This
combined, or master, bitmap serves as a mixing board of sorts. Only after the
foreground and background are combined in the master bitmap does anything get
displayed in a window for the user to see. What gets displayed is a copy of the master
bitmap. As shown in Figure 4, it is the only one of the three offscreen bitmaps that
gets copied from memory to a window.
Figure 4. Using offscreen bitmaps in memory to create one "frame" of an animated
sequence.
Animation begins by copying the background bitmap to the mixer, then copying the
foreground bitmap to the same mixer. The mixer bitmap is then copied to a window.
This combining of the foreground and background bitmaps, and the subsequent
displaying of the result in a window, is performed repeatedly within a loop. In each
pass through the loop there should be a slight shift in the position of the foreground
image relative to the background image. The result of executing this loop is that the
foreground image appears to be moving. Animation is achieved. And because the new
image that's displayed in each pass through the loop is created behind the scene in
memory rather than in view of the user on-screen, there is a minimum of flicker.
In this month's program we create three offscreen bitmaps to accomplish the animated
effect of a hand moving smoothly across a window. As the user drags the mouse, the
hand follows. The hand appears to track the cursor, floating along on top of the gray
background. Figure 5 shows what the user of the BitMapper program sees.
Figure 5. The BitMapper window.
The floating hand is our foreground bitmap image. The framed gray pattern is the
background bitmap image. As you move the mouse, the hand appears to float over the
gray background, just like a cursor. What the user is seeing is the mixer bitmap - the
copied version of the offscreen bitmap that represents the combining of the foreground
hand bitmap with the background gray bitmap.
Creating the BitMapper Resources
To get under way, move into your CodeWarrior development folder and create a folder
named BitMapper. Launch ResEdit and create a new resource file named
BitMapper.rsrc inside the BitMapper folder.
You need to create two PICT resources - one to serve as the foreground image and one to
act as the background image. In the BitMapper source code we'll be referencing these
resources by IDs of 128 and 129, so make sure you assign those values to the
pictures. In particular, give the background PICT an ID of 128 and the foreground
PICT an ID of 129. You don't have to use the same pictures we're using, but you do have
to make sure that PICT 128 is larger than PICT 129 so that the background is larger
than the foreground!
If you've got a graphics program like ClarisDraw, CorelDraw, or Canvas, create your
background by drawing a nice frame, then pasting another image inside it. Copy the
whole thing to the clipboard, then paste it inside ResEdit. For the foreground, you'll
want something relatively small. Use whatever image you like, but be sure to make it
resource ID 129. Note that both images should be black and white only, and not color
or grayscale. You can use a color image, but all colored pixels will be translated to