142 lines
8 KiB
Markdown
142 lines
8 KiB
Markdown
------------------------ --------------------------------- --------------------
|
||
[Previous](46-02.html) [Table of Contents](index.html) [Next](47-01.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
#### Masked Images {#Heading4}
|
||
|
||
Masked images are rendered by drawing an object’s pixels through a mask;
|
||
pixels are actually drawn only where the mask specifies that drawing is
|
||
allowed. This makes it possible to draw nonrectangular objects that
|
||
don’t improperly interfere with one another when they overlap. Masked
|
||
images also make it possible to have transparent areas (windows) within
|
||
objects. Masked images produce far more realistic animation than do
|
||
rectangular images, and therefore are more desirable. Unfortunately,
|
||
masked images are also considerably slower to draw—however, a good
|
||
assembly language implementation can go a long way toward making masked
|
||
images draw rapidly enough, as illustrated by this chapter’s code.
|
||
(Masked images are also known as *sprites*; some video hardware supports
|
||
sprites directly, but on the PC it’s necessary to handle sprites in
|
||
software.)
|
||
|
||
Masked images make it possible to render scenes so that a given image
|
||
convincingly appears to be in front of or behind other images; that is,
|
||
so images are displayed in *z-order* (by distance). By consistently
|
||
drawing images that are supposed to be farther away before drawing
|
||
nearer images, the nearer images will appear in front of the other
|
||
images, and because masked images draw only precisely the correct pixels
|
||
(as opposed to blank pixels in the bounding rectangle), there’s no
|
||
interference between overlapping images to destroy the illusion.
|
||
|
||
In this chapter, I’ve used the approach of having separate, paired masks
|
||
and images. Another, quite different approach to masking is to specify a
|
||
transparent color for copying, and copy only those pixels that are not
|
||
the transparent color. This has the advantage of not requiring separate
|
||
mask data, so it’s more compact, and the code to implement this is a
|
||
little less complex than the full masking I’ve implemented. On the other
|
||
hand, the transparent color approach is less flexible because it makes
|
||
one color undrawable. Also, with a transparent color, it’s not possible
|
||
to keep the same base image but use different masks, because the mask
|
||
information is embedded in the image data.
|
||
|
||
#### Internal Animation {#Heading5}
|
||
|
||
I’ve added another feature essential to producing convincing animation:
|
||
*internal animation*, which is the process of changing the appearance of
|
||
a given object over time, as distinguished from changing only the
|
||
*location* of a given object. Internal animation makes images look
|
||
active and alive. I’ve implemented the simplest possible form of
|
||
internal animation in Listing 46.1—alternation between two images—but
|
||
even this level of internal animation greatly improves the feel of the
|
||
overall animation. You could easily increase the number of images cycled
|
||
through, simply by increasing the value of **InternalAnimateMax** for a
|
||
given entity. You could also implement more complex image-selection
|
||
logic to produce more interesting and less predictable
|
||
internal-animation effects, such as jumping, ducking, running, and the
|
||
like.
|
||
|
||
#### Dirty-Rectangle Management {#Heading6 align="center"}
|
||
|
||
As mentioned above, dirty-rectangle animation makes it possible to
|
||
access display memory a minimum number of times. The previous chapter’s
|
||
code didn’t do any of that; instead, it copied all portions of every
|
||
dirty rectangle to the screen, regardless of overlap between rectangles.
|
||
The code I’ve presented in this chapter goes to the other extreme,
|
||
taking great pains never to draw overlapped portions of rectangles more
|
||
than once. This is accomplished by checking for overlap whenever a
|
||
rectangle is to be added to the dirty list. When overlap with an
|
||
existing rectangle is detected, the new rectangle is reduced to between
|
||
zero and four nonoverlapping rectangles. Those rectangles are then again
|
||
considered for addition to the dirty list, and may again be reduced, if
|
||
additional overlap is detected.
|
||
|
||
A good deal of code is required to generate a fully nonoverlapped dirty
|
||
list. Is it worth it? It certainly can be, but in the case of Listing
|
||
46.1, probably not. For one thing, you’d need larger, heavily overlapped
|
||
objects for this approach to pay off big. Besides, this program is
|
||
mostly in C, and spends a lot of time doing things other than actually
|
||
accessing display memory. It also takes a fair amount of time just to
|
||
generate the nonoverlapped list; the overhead of all the looping,
|
||
intersecting, and calling required to generate the list eats up a lot of
|
||
the benefits of accessing display memory less often. Nonetheless, fully
|
||
nonoverlapped drawing can be useful under the right circumstances, and
|
||
I’ve implemented it in Listing 46.1 so you’ll have something to refer to
|
||
should you decide to go this route.
|
||
|
||
There are a couple of additional techniques you might try if you want to
|
||
wring maximum performance out of dirty-rectangle animation. You could
|
||
try coalescing rectangles as you generate the dirty-rectangle list. That
|
||
is, you could detect pairs of rectangles that can be joined together
|
||
into larger rectangles, so that fewer, larger rectangles would have to
|
||
be copied. This would boost the efficiency of the low-level copying
|
||
code, albeit at the cost of some cycles in the dirty-list management
|
||
code.
|
||
|
||
You might also try taking advantage of the natural coherence of animated
|
||
graphics screens. In particular, because the rectangle used to erase an
|
||
image at its old location often overlaps the rectangle within which the
|
||
image resides at its new location, you could just directly generate the
|
||
two or three nonoverlapped rectangles required to copy both the erase
|
||
rectangle and the new-image rectangle for any single moving image. The
|
||
calculation of these rectangles could be very efficient, given that you
|
||
know in advance the direction of motion of your images. Handling this
|
||
particular overlap case would eliminate most overlapped drawing, at a
|
||
minimal cost. You might then decide to ignore overlapped drawing between
|
||
different images, which tends to be both less common and more expensive
|
||
to identify and handle.
|
||
|
||
#### Drawing Order and Visual Quality {#Heading7}
|
||
|
||
A final note on dirty-rectangle animation concerns the quality of the
|
||
displayed screen image. In the last chapter, we simply stuffed dirty
|
||
rectangles into a list in the order they became dirty, and then copied
|
||
all of the rectangles in that same order. Unfortunately, this caused all
|
||
of the erase rectangles to be copied first, followed by all of the
|
||
rectangles of the images at their new locations. Consequently, there was
|
||
a significant delay between the appearance of the erase rectangle for a
|
||
given image and the appearance of the new rectangle. A byproduct was the
|
||
fact that a partially complete—part old, part new—image was visible long
|
||
enough to be noticed. In short, although the pixels ended up correct,
|
||
they were in an intermediate, incorrect state for a sufficient period of
|
||
time to make the animation look wrong.
|
||
|
||
This violated a fundamental rule of animation: *No pixel should ever be
|
||
displayed in a perceptibly incorrect state*. To correct the problem,
|
||
I’ve sorted the dirty rectangles first by Y coordinate, and secondly by
|
||
X coordinate. This means the screen updates from to draw a given image
|
||
should be drawn nearly simultaneously. Run the code from the last
|
||
chapter and then this chapter; you’ll see quite a difference in
|
||
appearance.
|
||
|
||
Avoid the trap of thinking animation is merely a matter of drawing the
|
||
right pixels, one after another. Animation is the art of drawing *the
|
||
right pixels at the right times* so that the eye and brain see what you
|
||
want them to see. Animation is a lot more challenging than merely
|
||
cranking out pixels, and it sure as heck isn’t a purely linear process.
|
||
|
||
------------------------ --------------------------------- --------------------
|
||
[Previous](46-02.html) [Table of Contents](index.html) [Next](47-01.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
* * * * *
|
||
|
||
Graphics Programming Black Book © 2001 Michael Abrash
|