abrash-black-book/39-01.md
2013-12-30 20:26:41 +11:00

136 lines
7.3 KiB
Markdown

Chapter 39\
Fast Convex Polygons {#Heading1}
---------------------
### Filling Polygons in a Hurry {#Heading2}
In the previous chapter, we explored the surprisingly intricate process
of filling convex polygons. Now we're going to fill them an order of
magnitude or so faster.
Two thoughts may occur to some of you at this point: "Oh, no, he's not
going to get into assembly language and device-dependent code, is he?"
and, "Why bother with polygon filling—or, indeed, any drawing
primitives—anyway? Isn't that what GUIs and third-party libraries are
for?"
To which I answer, "Well, yes, I am," and, "If you have to ask, you've
missed the magic of microcomputer programming." Actually, both questions
ask the same thing, and that is: "Why should I, as a programmer, have
any idea how my program actually works?"
Put that way, it sounds a little different, doesn't it?
GUIs, reusable code, portable code written entirely in high-level
languages, and object-oriented programming are all the rage now, and
promise to remain so for the foreseeable future. The thrust of this
technology is to enhance the software development process by offloading
as much responsibility as possible to other programmers, and by writing
all remaining code in modular, generic form. This modular code then
becomes a black box to be reused endlessly without another thought about
what actually lies inside. GUIs also reduce development time by making
many interface choices for you. That, in turn, makes it possible to
create quickly and reliably programs that will be easy for new users to
pick up, so software becomes easier to both produce and learn. This is,
without question, a Good Thing.
The "black box" approach does not, however, necessarily cause the
software itself to become faster, smaller, or more innovative; quite the
opposite, I suspect. I'll reserve judgement on whether that is a good
thing or not, but I'll make a prediction: In the short run, the
aforementioned techniques will lead to noticeably larger, slower
programs, as programmers understand less and less of what the key parts
of their programs do and rely increasingly on general-purpose code
written by other people. (In the long run, programs will be bigger and
slower yet, but computers will be so fast and will have so much memory
that no one will care.) Over time, PC programs will also come to be more
similar to one another—and to programs running on other platforms, such
as the Mac—as regards both user interface and performance.
Again, I am not saying that this is bad. It does, however, have major
implications for the future nature of PC graphics programming, in ways
that will directly affect the means by which many of you earn your
livings. Not so very long from now, graphics programming—all
programming, for that matter—will become mostly a matter of assembling
in various ways components written by other people, and will cease to be
the all-inclusively creative, mindbendingly complex pursuit it is today.
(Using legally certified black boxes is, by the way, one direction in
which the patent lawyers are leading us; legal considerations may be the
final nail in the coffin of homegrown code.) For now, though, it's still
within your power, as a PC programmer, to understand and even control
every single thing that happens on a computer if you so desire, to
realize any vision you may have. Take advantage of this unique window of
opportunity to create some magic!
Neither does it hurt to understand what's involved in drawing, say, a
filled polygon, even if you are using a GUI. You will better understand
the performance implications of the available GUI functions, and you
will be able to fill in any gaps in the functions provided. You may even
find that you can outperform the GUI on occasion by doing your own
drawing into a system memory bitmap, then copying the result to the
screen; for instance, you can do this under Windows by using the WinG
library available from Microsoft. You will also be able to understand
why various quirks exist, and will be able to put them to good use. For
example, the X Window System follows the polygon drawing rules described
in the previous chapter (although it's not obvious from the X Window
System documentation); if you understood the previous chapter's
discussion, you're in good shape to use polygons under X.
In short, even though doing so runs counter to current trends, it helps
to understand how things work, especially when they're very visible
parts of the software you develop. That said, let's learn more about
filling convex polygons.
### Fast Convex Polygon Filling {#Heading3}
In addressing the topic of filling convex polygons in the previous
chapter, the implementation we came up with met all of our functional
requirements. In particular, it met stringent rules that guaranteed that
polygons would never overlap or have gaps at shared edges, an important
consideration when building polygon-based images. Unfortunately, the
implementation was also slow as molasses. In this chapter we'll work up
polygon-filling code that's fast enough to be truly usable.
Our original polygon filling code involved three major tasks, each
performed by a separate function:
- Tracing each polygon edge to generate a coordinate list (performed
by the function **ScanEdge);**
- Drawing the scanned-out horizontal lines that constitute the filled
polygon (**DrawHorizontalLineList** ); and
- Characterizing the polygon and coordinating the tracing and drawing
(**FillConvexPolygon** ).
The amount of time that the previous chapter's sample program spent in
each of these areas is shown in Table 39.1. As you can see, half the
time was spent drawing and the other half was spent tracing the polygon
edges (the time spent in **FillConvexPolygon** was relatively
minuscule), so we have our choice of where to begin optimizing.
#### Fast Drawing {#Heading4}
Let's start with drawing, which is easily sped up. The previous
chapter's code used a double-nested loop that called a draw-pixel
function to plot each pixel in the polygon individually. That's a
ridiculous approach in a graphics mode that offers linearly mapped
memory, as does VGA mode 13H, the mode in which we're working. At the
very least, we could point a far pointer to the left edge of each
polygon scan line, then draw each pixel in that scan line in quick
succession, using something along the lines of **\*ScrPtr++ =
FillColor;** inside a loop.
However, it seems silly to use a loop when the x86 has an instruction,
**REP STOS**, that's uniquely suited to filling linear memory buffers.
There's no way to use **REP STOS** directly in C code, but it's a good
bet that the **memset** library function uses **REP STOS**, so you could
greatly enhance performance by using **memset** to draw each scan line
of the polygon in a single shot. That, however, is easier said than
done. The **memset** function linked in from the library is tied to the
memory model in use; in small (which includes Tiny, Small, or Medium)
data models **memset** accepts only near pointers, so it can't be used
to access screen memory. Consequently, a large (which includes Compact,
Large, or Huge) data model must be used to allow **memset** to draw to
display memory—a clear case of the tail wagging the dog. This is an
excellent example of why, although it is possible to use C to do
virtually anything, it's sometimes much simpler just to use a little
assembly code and be done with it.