Initial conversion to markdown
This commit is contained in:
parent
a96a114403
commit
5e10794a24
362 changed files with 67353 additions and 0 deletions
163
39-03.md
Normal file
163
39-03.md
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
------------------------ --------------------------------- --------------------
|
||||
[Previous](39-02.html) [Table of Contents](index.html) [Next](39-04.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
Listing 39.2 shows a C implementation of integer edge tracing. Vertical
|
||||
and diagonal lines, which are trivial to trace, are special-cased. Other
|
||||
lines are broken into two categories: Y-major (closer to vertical) and
|
||||
X-major (closer to horizontal). The handlers for the Y-major and X-major
|
||||
cases operate on the principle of similar triangles: The number of X
|
||||
pixels advanced per scan line is the same as the ratio of the X delta of
|
||||
the edge to the Y delta. Listing 39.2 is more complex than the original
|
||||
floating point implementation, but not painfully so. In return for that
|
||||
complexity, Listing 39.2 is more than 80 times faster at scanning
|
||||
edges—and, as just mentioned, it’s actually more accurate than the
|
||||
floating point code.
|
||||
|
||||
Ya gotta love that integer arithmetic.
|
||||
|
||||
**LISTING 39.2 L39-2.C**
|
||||
|
||||
/* Scan converts an edge from (X1,Y1) to (X2,Y2), not including the
|
||||
point at (X2,Y2). If SkipFirst == 1, the point at (X1,Y1) isn’t
|
||||
drawn; if SkipFirst == 0, it is. For each scan line, the pixel
|
||||
closest to the scanned edge without being to the left of the
|
||||
scanned edge is chosen. Uses an all-integer approach for speed and
|
||||
precision. */
|
||||
|
||||
#include <math.h>
|
||||
#include “polygon.h”
|
||||
|
||||
void ScanEdge(int X1, int Y1, int X2, int Y2, int SetXStart,
|
||||
int SkipFirst, struct HLine **EdgePointPtr)
|
||||
{
|
||||
int Y, DeltaX, Height, Width, AdvanceAmt, ErrorTerm, i;
|
||||
int ErrorTermAdvance, XMajorAdvanceAmt;
|
||||
struct HLine *WorkingEdgePointPtr;
|
||||
|
||||
WorkingEdgePointPtr = *EdgePointPtr; /* avoid double dereference */
|
||||
AdvanceAmt = ((DeltaX = X2 - X1) > 0) ? 1 : -1;
|
||||
/* direction in which X moves (Y2 is
|
||||
always > Y1, so Y always counts up) */
|
||||
|
||||
if ((Height = Y2 - Y1) <= 0) /* Y length of the edge */
|
||||
return; /* guard against 0-length and horizontal edges */
|
||||
|
||||
/* Figure out whether the edge is vertical, diagonal, X-major
|
||||
(mostly horizontal), or Y-major (mostly vertical) and handle
|
||||
appropriately */
|
||||
if ((Width = abs(DeltaX)) == 0) {
|
||||
/* The edge is vertical; special-case by just storing the same
|
||||
X coordinate for every scan line */
|
||||
/* Scan the edge for each scan line in turn */
|
||||
for (i = Height - SkipFirst; i-- > 0; WorkingEdgePointPtr++) {
|
||||
/* Store the X coordinate in the appropriate edge list */
|
||||
if (SetXStart == 1)
|
||||
WorkingEdgePointPtr->XStart = X1;
|
||||
else
|
||||
WorkingEdgePointPtr->XEnd = X1;
|
||||
}
|
||||
} else if (Width == Height) {
|
||||
/* The edge is diagonal; special-case by advancing the X
|
||||
coordinate 1 pixel for each scan line */
|
||||
if (SkipFirst) /* skip the first point if so indicated */
|
||||
X1 += AdvanceAmt; /* move 1 pixel to the left or right */
|
||||
/* Scan the edge for each scan line in turn */
|
||||
for (i = Height - SkipFirst; i-- > 0; WorkingEdgePointPtr++) {
|
||||
/* Store the X coordinate in the appropriate edge list */
|
||||
if (SetXStart == 1)
|
||||
WorkingEdgePointPtr->XStart = X1;
|
||||
else
|
||||
WorkingEdgePointPtr->XEnd = X1;
|
||||
X1 += AdvanceAmt; /* move 1 pixel to the left or right */
|
||||
}
|
||||
} else if (Height > Width) {
|
||||
/* Edge is closer to vertical than horizontal (Y-major) */
|
||||
if (DeltaX >= 0)
|
||||
ErrorTerm = 0; /* initial error term going left->right */
|
||||
else
|
||||
ErrorTerm = -Height + 1; /* going right->left */
|
||||
if (SkipFirst) { /* skip the first point if so indicated */
|
||||
/* Determine whether it’s time for the X coord to advance */
|
||||
if ((ErrorTerm += Width) > 0) {
|
||||
X1 += AdvanceAmt; /* move 1 pixel to the left or right */
|
||||
ErrorTerm -= Height; /* advance ErrorTerm to next point */
|
||||
}
|
||||
}
|
||||
/* Scan the edge for each scan line in turn */
|
||||
for (i = Height - SkipFirst; i-- > 0; WorkingEdgePointPtr++) {
|
||||
/* Store the X coordinate in the appropriate edge list */
|
||||
if (SetXStart == 1)
|
||||
WorkingEdgePointPtr->XStart = X1;
|
||||
else
|
||||
WorkingEdgePointPtr->XEnd = X1;
|
||||
/* Determine whether it’s time for the X coord to advance */
|
||||
if ((ErrorTerm += Width) > 0) {
|
||||
X1 += AdvanceAmt; /* move 1 pixel to the left or right */
|
||||
ErrorTerm -= Height; /* advance ErrorTerm to correspond */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Edge is closer to horizontal than vertical (X-major) */
|
||||
/* Minimum distance to advance X each time */
|
||||
XMajorAdvanceAmt = (Width / Height) * AdvanceAmt;
|
||||
/* Error term advance for deciding when to advance X 1 extra */
|
||||
ErrorTermAdvance = Width % Height;
|
||||
if (DeltaX >= 0)
|
||||
ErrorTerm = 0; /* initial error term going left->right */
|
||||
else
|
||||
ErrorTerm = -Height + 1; /* going right->left */
|
||||
if (SkipFirst) { /* skip the first point if so indicated */
|
||||
X1 += XMajorAdvanceAmt; /* move X minimum distance */
|
||||
/* Determine whether it’s time for X to advance one extra */
|
||||
if ((ErrorTerm += ErrorTermAdvance) > 0) {
|
||||
X1 += AdvanceAmt; /* move X one more */
|
||||
ErrorTerm -= Height; /* advance ErrorTerm to correspond */
|
||||
}
|
||||
}
|
||||
/* Scan the edge for each scan line in turn */
|
||||
for (i = Height - SkipFirst; i-- > 0; WorkingEdgePointPtr++) {
|
||||
/* Store the X coordinate in the appropriate edge list */
|
||||
if (SetXStart == 1)
|
||||
WorkingEdgePointPtr->XStart = X1;
|
||||
else
|
||||
WorkingEdgePointPtr->XEnd = X1;
|
||||
X1 += XMajorAdvanceAmt; /* move X minimum distance */
|
||||
/* Determine whether it’s time for X to advance one extra */
|
||||
if ((ErrorTerm += ErrorTermAdvance) > 0) {
|
||||
X1 += AdvanceAmt; /* move X one more */
|
||||
ErrorTerm -= Height; /* advance ErrorTerm to correspond */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*EdgePointPtr = WorkingEdgePointPtr; /* advance caller’s ptr */
|
||||
}
|
||||
|
||||
### The Finishing Touch: Assembly Language {#Heading6}
|
||||
|
||||
The C implementation in Listing 39.2 is now nearly 20 times as fast as
|
||||
the original, which is good enough for most purposes. Still, it requires
|
||||
that one of the large data models be used (for **memset** ), and it’s
|
||||
certainly not the fastest possible code. The obvious next step is
|
||||
assembly language.
|
||||
|
||||
Listing 39.3 is an assembly language version of
|
||||
**DrawHorizontalLineList** . In actual use, it proved to be about 36
|
||||
percent faster than Listing 39.1; better than a poke in the eye with a
|
||||
sharp stick, but just barely. There’s more to these timing results than
|
||||
meets that eye, though. Display memory generally responds much more
|
||||
slowly than system memory, especially in 386 and 486 systems. That means
|
||||
that much of the time taken by Listing 39.3 is actually spent waiting
|
||||
for display memory accesses to complete, with the processor forced to
|
||||
idle by wait states. If, instead, Listing 39.3 drew to a local buffer in
|
||||
system memory or to a particularly fast VGA, the assembly implementation
|
||||
might well display a far more substantial advantage over the C code.
|
||||
|
||||
------------------------ --------------------------------- --------------------
|
||||
[Previous](39-02.html) [Table of Contents](index.html) [Next](39-04.html)
|
||||
------------------------ --------------------------------- --------------------
|
||||
|
||||
* * * * *
|
||||
|
||||
Graphics Programming Black Book © 2001 Michael Abrash
|
||||
Loading…
Reference in a new issue