Safe Dissolve
Volume Number: 6
Issue Number: 12
Column Tag: Programmer's Forum
QuickDraw-safe Dissolve 
By Mike Morton, Honolulu, HI
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
A QuickDraw-safe Dissolve
I have sinned against you.
It was a long time ago. It was a brief dalliance. But it’s time to set things right.
On a hot August night in 1984, I was sitting in a basement with a 128K Mac and a
Lisa running the Workshop development system. I had read Inside Macintosh about as
far as the QuickDraw section on bitmaps and then bogged down. I didn’t want to learn
about DITLs or the segment loader or any of that high-level junk. I was impatient and
wanted some instant gratification. It seemed like you could get neat effects by directly
manipulating screen memory, bypassing QuickDraw for considerable gains in speed.
So I wrote a subroutine called “DissBits”, modeled on the QuickDraw “CopyBits”
call. It copied one bit image to another, moving the pixels one at a time in
pseudo-random order. The resulting effect was a smooth dissolve, a “fade” in video
lingo.
Persistence of vision
DissBits has popped up here and there over the years, which is pleasing and
embarrassing. It’s embarrassing because Apple has been telling people from the outset
not to assume things about the depth of the screen, and DissBits does that -- it won’t
work in color, or with multiple monitors. I’ve even had it crash in the middle of a job
interview.
The subroutine stubbornly tries to evolve with the times: John Love’s MacTutor
series on graphics includes an updated version which handles multi-bit pixels -- but
still has problems when the target spans multiple monitors. MacroMind has
apparently also produced a color version, though they haven’t been very forthcoming
about how they did it. Someone (I have no idea who) has produced a version for some
IBM monitors. And in more general form, the algorithm is included in the recent
compendium Graphics Gems (ed. Andrew Glassner, Academic Press, 1990).
Cleanliness is next to impossible?
Can you get the esthetics of a smooth dissolve without sneaking behind
QuickDraw’s back and breaking all the compatibility rules? Well, to some degree, yes.
This article presents a set of subroutines collectively called DissMask. This approach
to dissolving images onto the screen directly manipulates an offscreen bitmap, but
operates on the visible screen using only QuickDraw. While Apple can continue to
define new layouts for screens, the structure of a one-deep bitmap is unlikely to
change.
This technique isn’t good for fading large areas -- you can adjust the speed to
some degree, but you’ll rarely want to use this for a full-screen fade. Still, it’s an
instructive look at how closely the speed of a purist solution can approach that of a
trickier, hardware-specific solution. It’s also immensely simpler than the original
code, since it’s largely coded in C (the original was in assembler), and it solves a
fundamentally smaller problem.
Through a mask, darkly
The new method copies the source image to the screen with CopyMask.
(CopyMask was introduced with the Mac Plus ROM, so you’ll need to test that it’s
present if you want your application to run on very old machines.) CopyMask
transfers a selected portion of a source image to a destination image, using a “mask”
bitmap to control which pixels are transferred. The dissolve is accomplished by doing
a series of CopyMask calls, with more and more black pixels set in the mask. The final
CopyMask is with a 100%-black mask ( which, come to think of it, could be replaced
by a CopyBits call).
The part of the code which most closely resembles the original dissolve is a
function called dissMaskNext, which adds more black pixels to the mask bitmap. It’s
much simpler than the dissolve code, though, since it only sets bits and doesn’t copy
them. In addition, it works on a bitmap whose bounds are powers of two, and that
reduces clipping done in the loop. In fact, the loop is just eight instructions per pixel
for small images, but after each new round of adding black pixels, the CopyMask call
consumes a lot of time, so in most cases the time for this “original” code is negligible.
How many pixels do you add to the mask between each CopyMask? That’s up to
you. Adding too few pixels between copies will make the dissolve too slow and (if your
image is large enough) may contribute to flicker. Adding too many pixels between
copies will keep the dissolve from being smooth, since too many pixels will appear at a
time. For large images, there may be no happy medium between these two, e specially
if other things slow down the CopyMask call: stretching, a target which spans multiple
monitors, or a slow CPU.
That ol’ black (and white) magic
What are those magic constants in the array in dissMask.c? The heart of the
dissolve is a strange algorithm which produces all integers from 1 through 2n-1 in
pseudorandom order. Here’s a glimpse of how it works. (If you want a more detailed
discussion, see the December 1985 MacTutor [reprinted in Best of MacTutor, Vol. 1],
the November 1986 Dr. Dobb’s Journal, or the above-mentioned Graphics Gems.)
Consider this strange little loop:
/* 1 */
int value = 1;
do
{ if (value & 1)
value = (value >> 1) ^ MASK;
else value = (value >> 1);
} while (value != 1);