QD Antialiasing Techniques
Volume Number: 13
Issue Number: 1
Column Tag: Tips & Graphics Workshop
Antialiasing with Color QuickDraw
By Martin James Murrett II, MacPants Software
Techniques used in AntiAliasMan to smooth the edges of
Text, Line, Ovals and other shapes
I have been interested in antialiasing for a long time. It is a process in which the edges
of a graphic are dithered with the background to produce a smoothing effect. My first
experiment took about a week to render on screen, and showed a very poorly
antialiased black circle over a white background. Since then, I have not only drawn
that circle even slower, but have also released two versions of AntiAliasMan, a C
library with a wide array of antialiasing functions, ranging from the basic circle to
antialiased text, rounded rectangles, and lines, in any color, with or without
disturbing the background. This article describes how the newest version of
AntiAliasMan (2.0) works.
The first and most important area that this article will cover, before the code, is the
area of antialiasing theory used in this code. An antialiasing effect can be achieved by
drawing a large image off-screen, then dithering it down to a small image. My first
two efforts used a method of enlarging the background, drawing a large image on it, and
dithering down the whole mess.
One day it occurred to me that I could write a fast dithering mechanism by taking four
char pointers, four rows at a time, and using them to get an index for a four-bit
off-screen GWorld. I finally decided to implement my theoretical dithering routine in
AntiAliasMan. Upon trying it out (once the bugs were gone), I realized that I was
getting a 0-16, not 0-15 index. This lingered in my head for a little while. Then, one
night (morning) at about 3 A.M., it occurred to me that I could mask the index with
0x08, shift the result three bits right, and subtract it from the original index to get a
0-15 index. This was incorrect. A value of 16 (0x10) has only one bit set, the fifth. I
realized this the next day, and I had a working dithering routine.
Because Apple likes you, they made a routine called CopyBits that will change a
grayscale image into a color image, complete with a mode parameter and some other
goodies. Without CopyBits, AntiAliasMan would not exist. This is how the four-bit
grayscale image is changed to text or ovals or whatever else you are antialiasing.
The uses for AntiAliasMan are limitless. Use it in all of your PowerMac applications,
regardless of what they do. Write your own LDEFs and MDEFs for antialiased lists and
menus. Write system extensions that give Word 6.0 a reason to go slowly, by patching
DrawString to antialias text when Word 6.0 is the active process. With its new
DitherMan dithering engine, AntiAliasMan is even practical for 680x0 series Macs.
General Stuff
I have only three rules: use CopyBits if at all practical, don't call SetRect, and don't
wear your pants inside out. Of these three, only the first two are enforced in the code. I
like CopyBits because it is ridiculously fast on a PowerMac, compared to any custom
pixel copying routine (if you don't have a PowerPC compiler). It is guaranteed to work
in the future, and your code automatically benefits from graphic accelerators. I hate
SetRect because it adds to code size while decreasing code speed. Worse yet, is occupies
a valuable A-trap spot (there are only 4,096 available). I am currently working on a
patch that will make SetRect useful.
The code that follows presents the sore of AntiAliasMan two file segments: all of
AntiAliasMan.h and AntiAliasManInit() from AntiAliasMan.c.
Listing 1: AntiAliasMan.h
#ifndef __ANTIALIASMAN__
#define __ANTIALIASMAN__
#ifndef __QDOFFSCREEN__
#include
#endif
#ifndef __QUICKDRAW__
#include
#endif
pascal void AntiAliasManInit( void );
pascal OSErrDrawAntiAliasManString( ConstStr255Param s );
pascal OSErrDrawAntiAliasManChar( short ch );
pascal OSErrStdAntiAliasManRRect( GrafVerb verb, Rect *r,
short ovalWidth, short ovalHeight );
pascal OSErrStdAntiAliasManOval( GrafVerb verb, Rect *r );
pascal OSErrStdAntiAliasManArc( GrafVerb verb, Rect *r,
short startAngle, short arcAngle );
pascal OSErrAntiAliasManLineTo( short h, short v );
pascal OSErrAntiAliasManLine( short dh, short dv );
/* these macros make it easier to call the various AntiAliasMan
functions, requiring less typing, and more
familiar names */
#define DrawAAString( s ) DrawAntiAliasManString( s )
#define StdAARRect( v, r, ow, oh )
StdAntiAliasManRRect( v, r, ow, oh )
#define StdAAOval( v, r ) StdAntiAliasManOval( v, r )
#define StdAAArc( v, r, sa, aa )
StdAntiAliasManArc( v, r, sa, aa )
#define FrameAntiAliasManOval( r )
StdAntiAliasManOval( frame, r )
#define PaintAntiAliasManOval( r )
StdAntiAliasManOval( paint, r )
#define EraseAntiAliasManOval( r )
StdAntiAliasManOval( erase, r )
#define InvertAntiAliasManOval( r )
StdAntiAliasManOval( invert, r )
#define FillAntiAliasManOval( r, p )
StdAntiAliasManOval( fill, r )
#define FrameAAOval( r )
StdAntiAliasManOval( frame, r )
#define PaintAAOval( r )
StdAntiAliasManOval( paint, r )
#define EraseAAOval( r )
StdAntiAliasManOval( erase, r )
#define InvertAAOval( r )
StdAntiAliasManOval( invert, r )
#define FillAAOval( r, p )
StdAntiAliasManOval( fill, r )
#define FrameAntiAliasManRoundRect( r, ow, oh )
StdAntiAliasManRRect( frame, r, ow, oh )
#define PaintAntiAliasManRoundRect( r, ow, oh )
StdAntiAliasManRRect( paint, r, ow, oh )
#define EraseAntiAliasManRoundRect( r, ow, oh )
StdAntiAliasManRRect( erase, r, ow, oh )
#define InvertAntiAliasManRoundRect( r, ow, oh )
StdAntiAliasManRRect( invert, r, ow, oh )
#define FillAntiAliasManRoundRect( r, ow, oh, p )
StdAntiAliasManRRect( fill, r, ow, oh )
#define FrameAARoundRect( r, ow, oh )
StdAntiAliasManRRect( frame, r, ow, oh )
#define PaintAARoundRect( r, ow, oh )
StdAntiAliasManRRect( paint, r, ow, oh )
#define EraseAARoundRect( r, ow, oh )
StdAntiAliasManRRect( erase, r, ow, oh )
#define InvertAARoundRect( r, ow, oh )
StdAntiAliasManRRect( invert, r, ow, oh )
#define FillAARoundRect( r, ow, oh, p )
StdAntiAliasManRRect( fill, r, ow, oh )
#define FrameAAArc( r, sa, aa )
StdAntiAliasManArc( frame, r, sa, aa )
#define PaintAAArc( r, sa, aa )
StdAntiAliasManArc( paint, r, sa, aa )
#define EraseAAArc( r, sa, aa )
StdAntiAliasManArc( erase, r, sa, aa )
#define InvertAAArc( r, sa, aa )
StdAntiAliasManArc( invert, r, sa, aa )
#define FillAAArc( r, sa, aa, p )
StdAntiAliasManArc( fill, r, sa, aa )
#define FrameAntiAliasManArc( r, sa, aa )
StdAntiAliasManArc( frame, r, sa, aa )
#define PaintAntiAliasManArc( r, sa, aa )
StdAntiAliasManArc( paint, r, sa, aa )
#define EraseAntiAliasManArc( r, sa, aa )
StdAntiAliasManArc( erase, r, sa, aa )
#define InvertAntiAliasManArc( r, sa, aa )
StdAntiAliasManArc( invert, r, sa, aa )
#define FillAntiAliasManArc( r, sa, aa, p )
StdAntiAliasManArc( fill, r, sa, aa )
#define AALineTo( h, v ) AntiAliasManLineTo( h, v )
#define AALine( dh, dv ) AntiAliasManLine( dh, dv )
#endif
Listing 2: AntiAliasMan.c: Macros, AntiAliasManInit, and
DitherMan
#include "AntiAliasMan2.h
#ifndef nil