12.07.2015 Views

wu'ed in haste; fried, stewed at leisure

wu'ed in haste; fried, stewed at leisure

wu'ed in haste; fried, stewed at leisure

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

chapter 42<strong>wu'ed</strong> <strong>in</strong> <strong>haste</strong>; <strong>fried</strong>, <strong>stewed</strong> <strong>at</strong> <strong>leisure</strong>


When I read through SIGGRAPH proceed<strong>in</strong>gs and other st<strong>at</strong>e-of-the-art computergraphicsm<strong>at</strong>erial, all too often I feel like I’m d<strong>in</strong><strong>in</strong>g <strong>at</strong> a four-star restaurant withtwo-year-old triplets and an empty wallet. We’re talk<strong>in</strong>g <strong>in</strong>credibly <strong>in</strong>appropri<strong>at</strong>e technologyfor PC graphics here. Sure, I say to myself as I read about an antialias<strong>in</strong>gtechnique, th<strong>at</strong> sounds wonderful-if I had 24bpp color, and dedic<strong>at</strong>ed hardware todo the process<strong>in</strong>g, and all day to wait to gener<strong>at</strong>e one image. Yes, I th<strong>in</strong>k, th<strong>at</strong> is agood way to do hidden surface removal-<strong>in</strong> a system with hardware z-buffer<strong>in</strong>g. Mostof the stuff <strong>in</strong> the journal Computer Ofaphics is rivet<strong>in</strong>g, but, alas, pretty much uselesson PCs. When an x86 has to do all the work, speed becomes the overrid<strong>in</strong>g parameter,especially for real-time graphics.Liter<strong>at</strong>ure th<strong>at</strong>’s applicable to fast PC graphics is hard enough to f<strong>in</strong>d, but wh<strong>at</strong> we’dreally like is above-average image quality comb<strong>in</strong>ed with terrific speed, and there’salmost no liter<strong>at</strong>ure of th<strong>at</strong> sort around. There is some, however, and you folks areright on top of it. For example, alert reader Michael Chapl<strong>in</strong>, of San Diego, wrote tosuggest th<strong>at</strong> I might enjoy the l<strong>in</strong>e-antialias<strong>in</strong>g algorithm presented <strong>in</strong> Xiaol<strong>in</strong> Wu’sarticle, “An Efficient Antialias<strong>in</strong>g Technique,” <strong>in</strong> the July 1991 issue of Computer Guphics.Michael was dead-on right. This is a gre<strong>at</strong> algorithm, comb<strong>in</strong><strong>in</strong>g excellentantialiased l<strong>in</strong>e qualitywith speed th<strong>at</strong>’s close to th<strong>at</strong> of non-antialiased Bresenham’sl<strong>in</strong>e draw<strong>in</strong>g. This is the sort of algorithm th<strong>at</strong> makes you want to go out and write awire-frame anim<strong>at</strong>ion program, just so you can see how good those smooth l<strong>in</strong>eslook <strong>in</strong> motion. Wu antialias<strong>in</strong>g is a wonderful example of wh<strong>at</strong> can be accomplishedon <strong>in</strong>expensive, mass-market hardware with the proper programm<strong>in</strong>g perspective.In short, it’s a splendid example of appropri<strong>at</strong>e technology for PCs.Wu Antialias<strong>in</strong>gAntialias<strong>in</strong>g, as we’ve been discuss<strong>in</strong>g for the past few chapters, is the process ofsmooth<strong>in</strong>g l<strong>in</strong>es and edges so th<strong>at</strong> they appear less jagged. Antialias<strong>in</strong>g is partly anaesthetic issue, because it makes images more <strong>at</strong>tractive. It’s also partly an accuracyissue, because it makes it possible to position and draw images with effectively moreprecision than the resolution of the display. F<strong>in</strong>ally, it’s partly a fl<strong>at</strong>-out necessity, toavoid the horrible, crawl<strong>in</strong>g, jagged edges of temporal alias<strong>in</strong>g when perform<strong>in</strong>ganim<strong>at</strong>ion.The basic premise of Wu antialias<strong>in</strong>g is almost ridiculously simple: As the algorithm stepsone pixel unit <strong>at</strong> a time along the major (longer) axis of a l<strong>in</strong>e, draws it the two pixelsbracket<strong>in</strong>g the l<strong>in</strong>e along the m<strong>in</strong>or axis <strong>at</strong> each po<strong>in</strong>t. Each of the two bracket<strong>in</strong>gpixels is drawn with a weighted fraction of the full <strong>in</strong>tensity of the draw<strong>in</strong>g color, withthe weight<strong>in</strong>g for each pixel equal to one m<strong>in</strong>us the pixel’s distance along the m<strong>in</strong>oraxis from the ideal l<strong>in</strong>e. Yes, it’s a mouthful, but Figure 42.1 illustr<strong>at</strong>es the concept.The <strong>in</strong>tensities of the two pixels th<strong>at</strong> bracket the l<strong>in</strong>e are selected so th<strong>at</strong> they alwayssum to exactly 1; th<strong>at</strong> is, to the <strong>in</strong>tensity of one fully illum<strong>in</strong><strong>at</strong>ed pixel of the draw<strong>in</strong>gcolor. The presence of aggreg<strong>at</strong>e full-pixel <strong>in</strong>tensity means th<strong>at</strong> <strong>at</strong> each step, the l<strong>in</strong>e776 Chapter 42


has the same brightness it would have if a s<strong>in</strong>gle pixel were drawn <strong>at</strong> precisely thecorrect loc<strong>at</strong>ion. Moreover, thanks to the distribution of the <strong>in</strong>tensity weight<strong>in</strong>g, th<strong>at</strong>brightness is centered <strong>at</strong> the ideal l<strong>in</strong>e. Not co<strong>in</strong>cidentally, a l<strong>in</strong>e drawn with pixelpairs of aggreg<strong>at</strong>e s<strong>in</strong>gle-pixel <strong>in</strong>tensity, centered on the ideal l<strong>in</strong>e, is perceived bythe eye not as ajagged collection of pixel pairs, but as a smooth l<strong>in</strong>e centered on theideal l<strong>in</strong>e. Thus, by weight<strong>in</strong>g the bracket<strong>in</strong>g pixels properly <strong>at</strong> each step, we canreadily produce wh<strong>at</strong> looks like a smooth l<strong>in</strong>e <strong>at</strong> precisely the right loc<strong>at</strong>ion, r<strong>at</strong>herthan the jagged p<strong>at</strong>tern of l<strong>in</strong>e segments th<strong>at</strong> non-antialiased l<strong>in</strong>e-draw<strong>in</strong>g algorithmssuch as Bresenham’s (see Chapters 35,36, and 37) trace out.You might expect th<strong>at</strong> the implement<strong>at</strong>ion of Wu antialias<strong>in</strong>g would fall <strong>in</strong>to twodist<strong>in</strong>ct areas: trac<strong>in</strong>g out the l<strong>in</strong>e (th<strong>at</strong> is, f<strong>in</strong>d<strong>in</strong>g the appropri<strong>at</strong>e pixel pairs todraw) and calcul<strong>at</strong><strong>in</strong>g the appropri<strong>at</strong>e weight<strong>in</strong>gs for each pixel pair. Not so, however.The weight<strong>in</strong>g calcul<strong>at</strong>ions <strong>in</strong>volve only a few shifts, XORS, and adds; for all practicalpurposes, trac<strong>in</strong>g and weight<strong>in</strong>g are rolled <strong>in</strong>to one step-and a very fast step it is.How fast is it? On a 33-MHz 486 with a fast VGA, a good but not maxed-out assemblyimplement<strong>at</strong>ion of Wu antialias<strong>in</strong>g draws a more than respectable 5,000 150-pixellongvectors per second. Th<strong>at</strong>’s especially impressive consider<strong>in</strong>g th<strong>at</strong> about 1,500,000Wu‘ed <strong>in</strong> Haste; Fried, Stewed <strong>at</strong> Leisure 777


actual pixels are drawn per second, mean<strong>in</strong>g th<strong>at</strong> Wu antialias<strong>in</strong>g is draw<strong>in</strong>g <strong>at</strong> around50 percent of the maximum memory bandwidth-half the fastest theoretically possibledraw<strong>in</strong>g speed-of an AT-bus VGA. In short, Wu antialias<strong>in</strong>g is about as fast anantialiased l<strong>in</strong>e approach as you could ever hope to f<strong>in</strong>d for the VGA.Trac<strong>in</strong>g and Intensity <strong>in</strong> OneHorizontal, vertical, and diagonal l<strong>in</strong>es do not require Wu antialias<strong>in</strong>g because theypass through the center of every pixel they meet; such l<strong>in</strong>es can be drawn with fast,special-case code. For all other cases, Wu l<strong>in</strong>es are traced out one step <strong>at</strong> a time alongthe major axis by means of a simple, fixed-po<strong>in</strong>t algorithm. The move along them<strong>in</strong>or axis with respect to a one-pixel move along the major axis (the l<strong>in</strong>e slope forl<strong>in</strong>es with slopes less than 1, l/slope for l<strong>in</strong>es with slopes gre<strong>at</strong>er than 1) is calcul<strong>at</strong>edwith a s<strong>in</strong>gle <strong>in</strong>teger divide. This value, called the “error adjust,” is stored as a fixedpo<strong>in</strong>tfraction, <strong>in</strong> 0.16 form<strong>at</strong> (th<strong>at</strong> is, all bits are fractional, and the decimal po<strong>in</strong>t isjust to the left of bit 15). An error accumul<strong>at</strong>or, also <strong>in</strong> 0.16 form<strong>at</strong>, is <strong>in</strong>itialized to 0.Then the first pixel is drawn; no weight<strong>in</strong>g is needed, because the l<strong>in</strong>e <strong>in</strong>tersects itsendpo<strong>in</strong>ts exactly.Now the error adjust is added to the error accumul<strong>at</strong>or. The error accumul<strong>at</strong>or <strong>in</strong>dic<strong>at</strong>eshow far between pixels the l<strong>in</strong>e has progressed along the m<strong>in</strong>or axis <strong>at</strong> anygiven step; when the error accumul<strong>at</strong>or turns over, it’s time to advance one pixelalong the m<strong>in</strong>or axis. At each step along the l<strong>in</strong>e, the major-axis coord<strong>in</strong><strong>at</strong>e advancesby one pixel. The two bracket<strong>in</strong>g pixels to draw are simply the two pixels nearest thel<strong>in</strong>e along the m<strong>in</strong>or axis. For <strong>in</strong>stance, if X is the current major-axis coord<strong>in</strong><strong>at</strong>e andY is the current m<strong>in</strong>or-axis coord<strong>in</strong><strong>at</strong>e, the two pixels to be drawn are (X,Y) and(X,Y+l). In short, the deriv<strong>at</strong>ion of the pixels <strong>at</strong> which to draw <strong>in</strong>volves noth<strong>in</strong>gmore complic<strong>at</strong>ed than advanc<strong>in</strong>g one pixel along the major axis, add<strong>in</strong>g the erroradjust to the error accumul<strong>at</strong>or, and advanc<strong>in</strong>g one pixel along the m<strong>in</strong>or axis whenthe error accumul<strong>at</strong>or turns over.So far, noth<strong>in</strong>g special; but now we come to the true wonder of Wu antialias<strong>in</strong>g. Weknow which pair of pixels to draw <strong>at</strong> each step along the l<strong>in</strong>e, but we also need togener<strong>at</strong>e the two proper <strong>in</strong>tensities, which must be <strong>in</strong>versely proportional to distancefrom the ideal l<strong>in</strong>e and sum to 1, and th<strong>at</strong>’s a potentially timeconsum<strong>in</strong>goper<strong>at</strong>ion. Let’s assume, however, th<strong>at</strong> the number of possible <strong>in</strong>tensity levels to beused for weight<strong>in</strong>g is the value NumLevels = 2” for some <strong>in</strong>teger n, with the m<strong>in</strong>imumweight<strong>in</strong>g (0 percent <strong>in</strong>tensity) be<strong>in</strong>g the value 2”-1, and the maximumweight<strong>in</strong>g (100 percent <strong>in</strong>tensity) be<strong>in</strong>g the value 0. Given th<strong>at</strong>, lo and behold, themost significant n bits of the error accumul<strong>at</strong>or select the proper <strong>in</strong>tensity value forone element of the pixel pair, as shown <strong>in</strong> Figure 42.2. Better yet, 2“-1 m<strong>in</strong>us the<strong>in</strong>tensity of the first pixel selects the <strong>in</strong>tensity of the other pixel <strong>in</strong> the pair, becausethe <strong>in</strong>tensities of the two pixels must sum to 1; as it happens, this result can be ohta<strong>in</strong>ed simply by flipp<strong>in</strong>g the n least-significant bits of the first pixel’s value. All this778 Chapter 42


Sample Wu Antialias<strong>in</strong>gThe true test of any antialias<strong>in</strong>g technique is how good it looks, so let's have a look <strong>at</strong>Wu antialias<strong>in</strong>g <strong>in</strong> action. List<strong>in</strong>g 42.1 is a C implement<strong>at</strong>ion ofWu antialias<strong>in</strong>g.List<strong>in</strong>g 42.2 is a sample program th<strong>at</strong> draws a variety of Wu-antialiased l<strong>in</strong>es, followedby non-antialiased l<strong>in</strong>es, for comparison. List<strong>in</strong>g 42.3 conta<strong>in</strong>s DrawPixel() and &Mode()functions for mode 13H, the VGA's 320x200 256-color mode. F<strong>in</strong>ally, List<strong>in</strong>g 42.4 isa simple, non-antialiased l<strong>in</strong>edraw<strong>in</strong>g rout<strong>in</strong>e. L<strong>in</strong>k these four list<strong>in</strong>gs together andrun the result<strong>in</strong>g program to see both Wu-antialiased and non-antialiased l<strong>in</strong>es.LISTING 42.2 L42-2.C/* Sample l<strong>in</strong>e-draw<strong>in</strong>g program to demonstr<strong>at</strong>e Wu antialias<strong>in</strong>g. Also draws* non-antialiased l<strong>in</strong>es for comparison.* Tested with Borland C++ <strong>in</strong> C compil<strong>at</strong>ion mode and the small model.*/B<strong>in</strong>cl ude #<strong>in</strong>clude void SetPalette(struct WuColor *):extern void DrawWuL<strong>in</strong>e(<strong>in</strong>t. <strong>in</strong>t. <strong>in</strong>t. <strong>in</strong>t. <strong>in</strong>t. <strong>in</strong>t. unsigned <strong>in</strong>t):extern void DrawL<strong>in</strong>e(<strong>in</strong>t. <strong>in</strong>t. <strong>in</strong>t. <strong>in</strong>t. <strong>in</strong>t):extern void SetMode(void):extern <strong>in</strong>t ScreenWidthInPixels: /* screen dimension globals */extern <strong>in</strong>t ScreenHeightInPixels:#def<strong>in</strong>e NUM-WU-COLORS 2 /* # of colors we'll do antialiased draw<strong>in</strong>g with */struct WuColor I /* describes one color used for antialias<strong>in</strong>g */<strong>in</strong>t BaseColor: /* # of start of palette <strong>in</strong>tensity block <strong>in</strong> DAC */i nt NumLevel s : /* # of <strong>in</strong>tensity levels */<strong>in</strong>t IntensityBits: /* IntensityBits - log2 NumLevels */<strong>in</strong>t MaxRed: /* red component of color <strong>at</strong> full <strong>in</strong>tensity */<strong>in</strong>t MaxGreen: /* green component of color <strong>at</strong> full <strong>in</strong>tensity */<strong>in</strong>t MaxBlue: /* blue component of color <strong>at</strong> full <strong>in</strong>tensity */1:enum {WU-BLUE-0. WU-WHITE-11: /* draw<strong>in</strong>g colors*/struct WuColor WuColorsCNUM~WU~COLORSl - /* blue and white */((192, 32, 5. 0. 0. Ox3F). {224. 32. 5. Ox3F. Ox3F. Ox3F1};void ma<strong>in</strong>0I<strong>in</strong>t CurrentColor. i;union REGS regset:782 Chapter 42/* Draw Wu-antialiased l<strong>in</strong>es <strong>in</strong> all directions */SetModeO:SetPalette(WuCo1ors):for (i-5: i


1for (i-0; i


1for (i-0; i


* Non-antialiased l<strong>in</strong>e drawer.* (XO.YO).(Xl.Yl) - l<strong>in</strong>e to draw, Color*/- color <strong>in</strong> which to drawvoid DrawL<strong>in</strong>e(<strong>in</strong>t XO. <strong>in</strong>t YO, <strong>in</strong>t X1. <strong>in</strong>t Y1. <strong>in</strong>t Color)Iunsigned long ErrorAcc. ErrorAdj;<strong>in</strong>t DeltaX. DeltaY. XDir. Temp;/* Make sure the l<strong>in</strong>e runs top to bottom */if (YO > Y1) ITemp - YO; YO - Y1: Y1 - Temp;Temp - XO; X0 - X1; X1 - Temp:IDrawPixel(X0. YO. Color); /* draw the <strong>in</strong>itial pixel */if ((DeltaX - X1 - XO) >- 0) {XDir - 1;1 else IXDir - -1;DeltaX - -DeltaX; /* make DeltaX positive */Iif ((DeltaY - Y 1 - YO) - 0) /* done if only one po<strong>in</strong>t <strong>in</strong> the l<strong>in</strong>e */if (DeltaX - 0) return;1ErrorAcc - 0x8000: /* <strong>in</strong>itialize l<strong>in</strong>e error accumul<strong>at</strong>or to .5. so we can/* Is t his an X-major or Y-major l<strong>in</strong>e? */advance when we get halfway to the next pixel */if (DeltaY > DeltaX) {/* Y-major l<strong>in</strong>e; calcul<strong>at</strong>e 16-bit fixed-po<strong>in</strong>t fractional part of apixel th<strong>at</strong> X advances each time Y advances 1 pixel */ErrorAdj - ((((unsigned 1ong)DeltaX > 1:/* Draw all pixels between the first and last */do IErrorAcc +- ErrorAdj; /* calcul<strong>at</strong>e error for this pixel */if (ErrorAcc & -0xFFFFL) I/* The error accumul<strong>at</strong>or turned over. so advance the X coord */X0 +- XDir;ErrorAcc &- OxFFFFL; /* clear <strong>in</strong>teger part of result */IYO++: /* Y-major. so always advance Y */DrawPixel(X0. YO, Color):I while (--DeltaY);return:1/* It's an X-major l<strong>in</strong>e: calcul<strong>at</strong>e 16-bit fixed-po<strong>in</strong>t fractional part of apixel th<strong>at</strong> Y advances each time X advances 1 pixel */ErrorAdj - ((((unsigned 1ong)DeltaY > 1:/* Draw all rema<strong>in</strong><strong>in</strong>g pixels */do IErrorAcc +- ErrorAdj; /* calcul<strong>at</strong>e error for this pixel */if (ErrorAcc & -0xFFFFL) I/* The error accumul<strong>at</strong>or turned over, so advance the Y coord */YO++:ErrorAcc &- OxFFFFL; /* clear <strong>in</strong>teger part of result */IX0 +- XDir; /* X-major. so always advance X */DrawPixel(X0. YO, Color):I while (--DeltaX);Wu'ed <strong>in</strong> Haste; Fried, Stewed <strong>at</strong> Leisure 785


List<strong>in</strong>g 42.1 isn’t particularly fast, because it calls Drawpixel() for each pixel. On theother hand, Drawpixel() makes it easy to try out Wu antialias<strong>in</strong>g <strong>in</strong> a variety of modes;just adapt the code <strong>in</strong> List<strong>in</strong>g 42.3 for the 256-color mode you want to support. Forexample, List<strong>in</strong>g 42.5 shows code to draw Wu-antialiased l<strong>in</strong>es <strong>in</strong> 640x480 256-colormode on SuperVGAs built around the Tseng Labs ET4000 chip with <strong>at</strong> least 512Kofdisplay memory <strong>in</strong>stalled. It’s well worth check<strong>in</strong>g out Wu antialias<strong>in</strong>g <strong>at</strong> 640x480.Although antialiased l<strong>in</strong>es look much smoother than normal<strong>in</strong>es <strong>at</strong> 320x200 resolution,they’re far from perfect, because the pixels are so big th<strong>at</strong> the eye can’t blendthem properly. At 640x480, however, Wu-antialiased l<strong>in</strong>es look fabulous; from a coupleof feet away, they look as straight and smooth as if they were drawn with a ruler.LISTING 42.5 142-5.C/* Mode set and pixel-draw<strong>in</strong>g functions for the 640x480 256-color mode of* Tseng Labs ET4000-based SuperVGAs.* Tested with Borland C++ <strong>in</strong> C compil<strong>at</strong>ion mode and the small model.*I#i ncl ude I* Screen dimension globals. used <strong>in</strong> ma<strong>in</strong> program to scale */<strong>in</strong>t ScreenWidthInPixels - 640;<strong>in</strong>t ScreenHeightInPixels - 480:/* ET4000 640x480 256-color draw pixel function. *Ivoid DrawPixel(<strong>in</strong>t X. <strong>in</strong>t Y. <strong>in</strong>t Color)t#def<strong>in</strong>e SCREENKSEGMENT OxAOOO#def<strong>in</strong>e GC-SEGMENT-SELECT Ox3CD /* ET4000 segment (bank) select regunsigned char far *ScreenPtr:unsigned <strong>in</strong>t Bank:unsigned long BitmapAddress;/* full bitmap address of pixel, as measured from address 0 to OxFFFFF */BitmapAddress - (unsigned long) Y * ScreenWidthInPixels + X:/* Bank # is upper word of bitmap addr *IBank - BitmapAddress >> 16:I* Upper nibble is read bank #, lower nibble is write bank i/ *Ioutp(GC-SEGMENTKSELECT, (Bank


of the block is programmable, but must be a power of two. The more <strong>in</strong>tensity levels,the better. Wu says th<strong>at</strong> 32 <strong>in</strong>tensities are enough; on my system, eight and even fourlevels looked pretty good. I found th<strong>at</strong> gamma correction, which gives l<strong>in</strong>early spaced<strong>in</strong>tensity steps, improved antialias<strong>in</strong>g quality significantly. Fortun<strong>at</strong>ely, we can programthe palette with gamma-corrected values, so our draw<strong>in</strong>g code doesn't have todo any extra work.List<strong>in</strong>g 42.1 isn't very fast, so I implemented Wu antialias<strong>in</strong>g <strong>in</strong> assembly, hard-codedfor mode 13H. The implement<strong>at</strong>ion is shown <strong>in</strong> full <strong>in</strong> List<strong>in</strong>g 42.6. High-speed graphicscode and fast VGAs go together like peanut butter and jelly, which is to say verywell <strong>in</strong>deed; the assembly implement<strong>at</strong>ion ran more than twice as fast as the C codeon my 486. Enough said!LISTING 42.6 L42-6.ASM; C near-callable function to draw an antialiased l<strong>in</strong>e from; (XO.YO) to (X1,YI). <strong>in</strong> mode 13h. the VGA's standard 320x200 256-color; mode. Uses an antialias<strong>in</strong>g approach published by Xiaol<strong>in</strong> Wu <strong>in</strong> the July; 1991 issue of Computer Graphics. Requires th<strong>at</strong> the palette be set up so; th<strong>at</strong> there are NumLevels <strong>in</strong>tensity levels of the desired draw<strong>in</strong>g color,; start<strong>in</strong>g <strong>at</strong> color BaseColor (100% <strong>in</strong>tensity) and followed by (NumLevels-1); levels of evenly decreas<strong>in</strong>g <strong>in</strong>tensity, with color (BaseColor+NumLevels-1); be<strong>in</strong>g 0% <strong>in</strong>tensity of the desired draw<strong>in</strong>g color (black). No clipp<strong>in</strong>g is; performed <strong>in</strong> DrawWuL<strong>in</strong>e. Handles a maximum of 256 <strong>in</strong>tensity levels per; antialiased color. This code is suitable for use <strong>at</strong> screen resolutions,; with l<strong>in</strong>es typically no more than 1K long; for longer l<strong>in</strong>es, 32-bit error; arithmetic must be used to avoid problems with fixed-po<strong>in</strong>t <strong>in</strong>accuracy.; Tested with TASM.; C near-callable as:; void DrawWuL<strong>in</strong>e(<strong>in</strong>t XO. <strong>in</strong>t YO, <strong>in</strong>t X1. <strong>in</strong>t Y1. <strong>in</strong>t Basecolor.<strong>in</strong>t NumLevels. unsigned <strong>in</strong>t 1ntensityBit.s);SCREEN-WIDTH-IN-BYTES equ 320 ;# of bytes from the start of one scan l<strong>in</strong>e; to the start of the nextSCREEN-SEGMENT equ OaOOOh ;segment <strong>in</strong> which screen memory resides; Parameters passed <strong>in</strong> stack frame.parms strucdw 2 dup (?) ;pushed BP and return addressX0 dw ? ;X coord<strong>in</strong><strong>at</strong>e of l<strong>in</strong>e start po<strong>in</strong>tYO dw ? ;Y coord<strong>in</strong><strong>at</strong>e of l<strong>in</strong>e start po<strong>in</strong>tX1 dw ? ;X coord<strong>in</strong><strong>at</strong>e of l<strong>in</strong>e end po<strong>in</strong>tY1 dw ? ;Y coord<strong>in</strong><strong>at</strong>e of l<strong>in</strong>e end po<strong>in</strong>tBaseColor dw ? ;color # of first color <strong>in</strong> block used for;antialias<strong>in</strong>g. the 100% <strong>in</strong>tensity version of the;draw<strong>in</strong>g colorNumLevels dw ? ;size of color block, with BaseColor+NumLevels-I; be<strong>in</strong>g the 0% <strong>in</strong>tensity version of the draw<strong>in</strong>g color; (maximum NumLevels - 256)IntensityBits dw ?:log base 2 of NumLevel s: the # of bits used to; describe the <strong>in</strong>tensity of the draw<strong>in</strong>g color.; 2**IntensityBits--NumLevels; (maximum IntensityBits - 8)parms endsWu'ed <strong>in</strong> Haste; Fried, Stewed <strong>at</strong> Leisure 787


.modelsmall.code; Screen dimension globals, used <strong>in</strong> ma<strong>in</strong> program to scale.- ScreenWidthInPixels dw 320- ScreenHeightInPixels dw 200.codepublic -DrawWuL<strong>in</strong>e- DrawWuL<strong>in</strong>e proc nearpush bp ;preserve caller's stack framemov bp.sp ;po<strong>in</strong>t to local stack framepush si ;preserve C's register variablespush dipush ds ;preserve C's default d<strong>at</strong>a segmentcl d ;make str<strong>in</strong>g <strong>in</strong>structions <strong>in</strong>crement their po<strong>in</strong>ters; Make sure the l<strong>in</strong>e runs top to bottom.mov si.Cbpl.XOmov ax.Cbpl.YOcmp ax.[bp].Yl ;swap endpo<strong>in</strong>ts if necessary to ensure th<strong>at</strong>jna NoSwap ; YO EGMENTds .dx ;po<strong>in</strong>t DS to the screen segmentdx.SCREEN_WIDTH-IN-BYTESdx ;YO * SCREEN-WIDTH-IN-BYTES yields the offset; of the start of the row start the <strong>in</strong>itial; pixel is onsi ,ax ;po<strong>in</strong>t DS:SI to the <strong>in</strong>itial pixelal.byte ptr Cbp1.BaseColor ;color with which to draw[sil.aldraw the <strong>in</strong>itial pixelmovmovsubJnsnegnegDel taXSet:bx.1cx.[bp].Xlcx.[bpl.XODel taXSetcxbxXDir - 1; assume DeltaX >- 0;DeltaX; is it >- l?;yes. move left->right. all set;no. move right->left;make DeltaX positive;XDir - -1; Special-case horizontal, vertical, and diagonal l<strong>in</strong>es, which require no; weight<strong>in</strong>g because they go right through the center of every pixel.mov dx.Cbpl.Ylsub dx.Cbpl.YO ;DeltaY; is it O?jnz NotHorz ;no. not horizontal;yes. is horizontal, special caseand bx.bx ;draw from left->right?jns DoHorz ;yes. all setstd ;no. draw right->leftDoHorz:lea di.[bx+sil ;po<strong>in</strong>t DI to next pixel to drawmov ax.ds788 Chapter 42


mov es.ax :po<strong>in</strong>t ES:DI to next pixel to drawmov al.byte ptr [bp].BaseColor :color with which to draw:CX - DeltaX <strong>at</strong> this po<strong>in</strong>trep stosb :draw the rest of the horizontal l<strong>in</strong>ecl d :restore default direction fjmp Done ;and we're donealign 2NotHorz:and cx.cxjnz NotVertmov a1 .byte ptr [bp].BaseColorVert Loop :add si.SCREEN-WIDTH-IN-BYTESmov [sil.aldec dxjnz VertLoopjmp Done:is DeltaX O?:no, not a vertical l<strong>in</strong>e:yes. is vertical, special case;color with which to draw:po<strong>in</strong>t to next pixel to draw:draw the next pixel: --Del taY:and we're donealign 2NotVert :cmp cx,dx ;DeltaX - DeltaY?jnz NotDiag :no, not diagonal:yes. is diagonal, special casemov a1,byte ptr [bpl.BaseColor ;color with which to drawDiagLoop:lea si .[si+SCREEN-WIDTH-IN-BYTES+bx];advance to next pixel to draw by; <strong>in</strong>crement<strong>in</strong>g Y and add<strong>in</strong>g XDir to Xmov [si 1 .a1 :draw the next pixeldec dx :--Del taYjnz DiagLoopjmp Done ;and we're done: L<strong>in</strong>e is not horizontal, diagonal, or vertical.align 2NotDiag:: Is this an X-major or Y-major l<strong>in</strong>e?cmp dx.cxjb XMajor :it's X-major: It's a Y-major l<strong>in</strong>e. Calcul<strong>at</strong>e the 16-bit fixed-po<strong>in</strong>t fractional part of a: pixel th<strong>at</strong> X advances each time Y advances 1 pixel, trunc<strong>at</strong><strong>in</strong>g the result: to avoid overrunn<strong>in</strong>g the endpo<strong>in</strong>t along the X axis.xchg dx.cx :DX - DeltaX. CX - DeltaYsub ax.ax ;make DeltaX 16.16 fixed-po<strong>in</strong>t value DX:AX <strong>in</strong>div cx ;AX - (DeltaX


mov cx.8 :CL - # of bits by which to shiftsub cx.Cbpl.1ntensityBits : ErrorAcc to get <strong>in</strong>tensity (8 level: <strong>in</strong>stead of 16 because we work only: with the high byte of ErrorAcc)mov ch.byte ptr [bpl.NumLevels ;mask used to flip all bits an <strong>in</strong>dec ch : <strong>in</strong>tensity , weight<strong>in</strong>gproduc<strong>in</strong>g: result (1 - <strong>in</strong>tensity weight<strong>in</strong>g)mov bp.BaseColor[bp] :***stack frame not available***;***from now on ***xchg bp.ax ;BP - ErrorAdj. AL - BaseColor.: AH - scr<strong>at</strong>ch register: Draw all rema<strong>in</strong><strong>in</strong>g pixels.YMajorLoop:add dx.bp :calcul<strong>at</strong>e error for next pixeljnc NoXAdvance :not time to step <strong>in</strong> X yet:the error accumul<strong>at</strong>or turned over,;so advance the X coordadd si .bx :add XDir to the pixel po<strong>in</strong>terNoXAdvance:add si.SCREEN-WIDTH-IN-BYTES ;Y-major, so always advance Y; The IntensityBits most significant bits of ErrorAcc give us the <strong>in</strong>tensity; weight<strong>in</strong>g for this pixel. and the complement of the weight<strong>in</strong>g for the: paired pixel.mov ah.dh :msb of ErrorAccshr ah.cl :Weight<strong>in</strong>g - ErrorAcc >> IntensityShift:add ah.al : BaseCol or + Weight<strong>in</strong>gmov [si 1 ,ah :DrawPixel(X. Y. BaseColor + Weight<strong>in</strong>g):mov ah.dh :msb of ErrorAccshr ah.cl :Weight<strong>in</strong>g - ErrorAcc >> IntensityShift:xor ah.ch :Weight<strong>in</strong>g A Weight<strong>in</strong>gComplementMaskadd ah.al :BaseColor + (Weight<strong>in</strong>g A Weight<strong>in</strong>gComplementMask)mov [si+bx] ,ah :DrawPixel(X+XDir. Y.: BaseColor + (Weight<strong>in</strong>g A Weight<strong>in</strong>gComplementMask));dec di :--Del taYjnz YMajorLoopjmp Done :we're done with this l<strong>in</strong>e: It's an X-major l<strong>in</strong>e.align 2XMajor:: Calcul<strong>at</strong>e the 16-bit fixed-po<strong>in</strong>t fractional part of a pixel th<strong>at</strong> Y advances: each time X advances 1 pixel, trunc<strong>at</strong><strong>in</strong>g the result to avoid overrunn<strong>in</strong>g: the endpo<strong>in</strong>t along the X axis.sub ax.ax :make DeltaY 16.16 fixed-po<strong>in</strong>t DX:AX value <strong>in</strong>790 Chapter 42div cx :AX - (DeltaY


mov cx.8sub cx.Cbp1.IntensityBitsmov ch.byte ptr [bpl.NumLevelsdec chmov bp,BaseColor[bplxchg bp.ax: Draw all rema<strong>in</strong><strong>in</strong>g pixelsXMajorLoop:add dx,bpjnc NoYAdvanceadd si.SCREEN_WIDTH-IN-BYTESNoYAdvance:add si .bx;CL - I/ of bits by which to shift: ErrorAcc to get <strong>in</strong>tensity level (8: <strong>in</strong>stead of 16 because we work only: with the high byte of ErrorAcc);mask used to flip all bits <strong>in</strong> an: <strong>in</strong>tensity weight<strong>in</strong>g, produc<strong>in</strong>g: result (1 - <strong>in</strong>tensity weight<strong>in</strong>g):***stack frame not available***:***fromnow on***:BP - ErrorAdj. AL - BaseColor.: AH - scr<strong>at</strong>ch register:calcul<strong>at</strong>e error for next pixel;not time to step <strong>in</strong> Y yet:the error accumul<strong>at</strong>or turned over,: so advance the Y coord:advance Y:X-major. so add XDir to the pixel po<strong>in</strong>ter: The Intensityeits most significant bits of ErrorAcc give us the <strong>in</strong>tensity: weight<strong>in</strong>g for this pixel, and the complement of the weight<strong>in</strong>g for the: paired pixel.mov ah.dh :msb of ErrorAccshr ah.cl :Weight<strong>in</strong>g - ErrorAcc >> IntensityShift:add ah,al :Basecolor + Weight<strong>in</strong>gmov Csi1,ah :DrawPixel(X. Y. BaseColor + Weight<strong>in</strong>g):mov ah.dh :msb of ErrorAccshr ah.cl :Weight<strong>in</strong>g - ErrorAcc >> IntensityShift:xor ah.ch :Weight<strong>in</strong>g A Weight<strong>in</strong>gComplementMaskadd ah.al :Basecolor + (Weight<strong>in</strong>g A Weight<strong>in</strong>gComplementMask)mov [si+SCREEN-WIDTH-IN-BYTES].ah:DrawPixel(X. Y+SCREEN-WIDTH-IN-BYTES,: BaseColor + (Weight<strong>in</strong>g A Weight<strong>in</strong>gComplementMask)):dec di :--0eltaXjnz XMajorLoopDone:POP dspop dipop siPOP bPret-0rawWuL<strong>in</strong>e endpend;we’re done with this l<strong>in</strong>e;restore C’s default d<strong>at</strong>a segment:restore C’s register variables:restore caller’s stack frame;doneNotes on Wu Antialias<strong>in</strong>gWu antialias<strong>in</strong>g can be applied to any curve for which it’s possible to calcul<strong>at</strong>e <strong>at</strong>each step the positions and <strong>in</strong>tensities of two bracket<strong>in</strong>g pixels, although the implement<strong>at</strong>ionwill generally be nowhere near as efficient as it is for l<strong>in</strong>es. However, Wu’sarticle <strong>in</strong> Computer Ofaphicsdoes describe an efficient algorithm for draw<strong>in</strong>g antialiasedcircles. Wu also describes a technique for antialias<strong>in</strong>g solids, such as filled circles andpolygons. Wu’s approach biases the edges of filled objects outward. Although this isno good for adjacent polygons of the sort used <strong>in</strong> render<strong>in</strong>g, it’s certa<strong>in</strong>ly possible toWu‘ed <strong>in</strong> Haste; Fried, Stewed <strong>at</strong> Leisure 791


design a more accur<strong>at</strong>e polygon-antialias<strong>in</strong>g approach around Wu’s basic weight<strong>in</strong>gtechnique. The results would not be quite so good as more sophistic<strong>at</strong>ed antialias<strong>in</strong>gtechniques, but they would be much faster.In general, the results obta<strong>in</strong>ed by Wu antialias<strong>in</strong>g are only so-so, by theoreticalmeasures, Wu antialias<strong>in</strong>g amounts to a simple boxfilterplaced over a fuced-po<strong>in</strong>tstep approxim<strong>at</strong>ion of a l<strong>in</strong>e, and th<strong>at</strong> process <strong>in</strong>troduces a good deal of devi<strong>at</strong>ionfrom the ideal. On the other hand, Wu notes th<strong>at</strong> even a IOpercent error <strong>in</strong> <strong>in</strong>tensitydoesn ’t lead to noticeable loss of image quality, and for Wu-antialiased l<strong>in</strong>esup to IKpixels <strong>in</strong> length, the error is under 1 Opercent. Ifit looks good, it is goodandit looks good.With a l6bit error accumul<strong>at</strong>or, fixed-po<strong>in</strong>t <strong>in</strong>accuracy becomes a problem for Wuantialiasedl<strong>in</strong>es longer than 1K. For such l<strong>in</strong>es, you should switch to us<strong>in</strong>g 32-biterror values, which would let you handle l<strong>in</strong>es of any practical length.In the list<strong>in</strong>gs, I have chosen to trunc<strong>at</strong>e, r<strong>at</strong>her than round, the error-adjust value.This <strong>in</strong>creases the <strong>in</strong>tensity error of the l<strong>in</strong>e but guarantees th<strong>at</strong> fixed-po<strong>in</strong>t <strong>in</strong>accuracywon’t cause the m<strong>in</strong>or axis to advance past the endpo<strong>in</strong>t. Overrunn<strong>in</strong>g theendpo<strong>in</strong>t would result <strong>in</strong> the draw<strong>in</strong>g of pixels outside the l<strong>in</strong>e’s bound<strong>in</strong>g box, andpotentially even <strong>in</strong> an <strong>at</strong>tempt to access pixels off the edge of the bitmap.F<strong>in</strong>ally, I should mention th<strong>at</strong>, as published, Wu’s algorithm draws l<strong>in</strong>es symmetrically,from both ends <strong>at</strong> once. I haven’t done this for a number of reasons, not leastof which is th<strong>at</strong> symmetric draw<strong>in</strong>g is an <strong>in</strong>efficient way to draw l<strong>in</strong>es th<strong>at</strong> span bankson banked Super-VGAs. Bank<strong>in</strong>g aside, however, symmetric draw<strong>in</strong>g is potentiallyfaster, because it elim<strong>in</strong><strong>at</strong>es half of all calcul<strong>at</strong>ions; <strong>in</strong> so do<strong>in</strong>g, it cuts cumul<strong>at</strong>iveerror <strong>in</strong> half, as well.With or without symmetrical process<strong>in</strong>g, Wu antialias<strong>in</strong>g be<strong>at</strong>s <strong>fried</strong>, <strong>stewed</strong> chickenhands-down. Trust me on this one.792 Chapter 42

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!