Dissolve
Volume Number: 2
Issue Number: 6
Column Tag: Graphics Lab: Asm
Wizzo Shows Dissolve Effects 
By Chris Yerga, Berkely, CA, MacTutor Contributing Editor
Fair Warning
Welcome to the second installment of the Graphic Lab. In this column we will
explore the Mac's graphic capablilities and try to exploit them for all their worth. But
I must warn prospective readers: This column is not for the so-called "power-users
or anyone else who bought their Mac to print out mailing labels. If you own a numeric
keypad, this one isn't for you. This column is for people who'd rather watch a
spaceship fly around on their screen than boot up Excel, given the choice. This column
is for programmers who look forward to designing the title graphics for their
applications, not the I/O drivers. This column is for those who never turn off the
animation option in Switcher. So you've been warned. Everything beyond this
paragraph will be pure frivolity. Let's go!
Crimes of Graphics
This second installment will deal with two crimes: one bad, and one good. First
the bad one. Many of the people I've spoken with don't fully understand the potential of
the Mac's graphics. They're blown away by some of the things they see, but they are
convinced that the techniques are so involved that such feats are beyond their grasp. As
a result, we haven't seen a lot of programs that push the Mac as far as they could. The
good crime is one that we will commit, and one that will help us understand some basic
principles of QuickDraw and graphics in general.
This is the crime of theft. We are going to use a Desk Accessory called BitNapper
published in last month's column to steal graphics from other applications. Then we
will show some title animation using those stolen bit maps in a fun little animation demo
called Wizzo. The BitNapper DA is listed in last month's Graphics Lab column. With it,
we can cut BitMaps from any application that supports desk accessories, or we can use
it to cut our own graphics from MacPaint. BitNapper saves the stolen BitMap to disk as
an MDS source file which allows us to install them as resources into our applications
much like we have done with icons and the icon converter program published previously
in Vol. 2 No.1 of MacTutor.
To use the BitNapper, install it into the system file on the disk with the
application whose graphics you want to pilfer. When the picture you want is on the
screen, select the BitNapper. It will install its own menu into the menu bar. Now
select "Steal Bits" from its menu. Position the upper left hand corner of the selection
rectangle at the upper left hand corner of the BitMap you want to steal. Now drag down
to the lower right hand corner. The BitNapper will invert those bits within the
selected rectangle. Release the button and the rest is self explanatory. The word
constraint option will not be needed until later. It forces the selected BitMap to have
left and right sides that coincide with word boundaries, which is sometimes useful. The
BitNapper source, published last month, is also available on the source code disk #8
from MacTutor's mail order store.
What the good book has to say
Since we don't want to be complete outlaws, we will begin with some standard
QuickDraw info taken straight from Inside Macintosh. The main QD data structure we
will concern ourselves with is the BitMap. The BitMap is a rectangular arrangement of
bits that describes some image. As a matter of fact, the Mac screen itself is a BitMap.
Lets look at the structure of a BitMap.
From figure #1 we can see that the actual bit image of the BitMap is not a part of
the BitMap data structure. Rather, there is a pointer to the bit image. This is because
bit images, such as that of the Mac screen, tend to be quite large. The way that
BitMaps are defined allows several different BitMaps use the same bit image. This is
useful, as many times BitMaps only differ in their bounds rectangles. Another item of
note is the fact that the rowBytes value should always be even. The reason for this is
shown in figure #2. It makes sure that the beginning of each row of data, or each
scanline in the case of the Mac screen, occurs on a word boundary. This allows us to
access the rows using word or long sized instructions, which generally makes life
simpler.
Peaceful coexistence
Now that we have grabbed a chunk of graphics from our favorite application,
what shall we do next? How do we get our application to access the BitMap with ease?
The answer is to keep the BitMap in the resource fork of the application. In the
resource file for our application we do something like:
Resource 'GNRL' 135
Include MyPicture.BMAP
Where MyPicture.BMAP is the filename of the BitMap that you saved with
BitNapper. The ID number can be whatever you want. Although I was tempted to use my
own resource type, I decided to go by the book and use GNRL, which Apple considers
legal. To facilitate the use of BitMaps in resources, I have written a few utilities
which simplify things a bit.
The first utility is a routine called GetBitMap, which loads a BitMap in from
resources. It looks for an ID number in D0 and returns a handle to the BitMap in A2.
The handle allows the BitMap to be relocatable in memory, pr eventing heap
fragmentation, but creating a problem. The way that BitNapper stores the data is
shown in figure #3. Since our resource BitMaps can move around in memory behind
our backs, we can never be sure if the basAddr pointer actually points to the bit image
that follows it.
The answer is a routine called LockBitMap. It locks the BitMap in memory and
correctly sets the basAddr pointer according to the current position of the BitMap in
memory. It takes a handle to the BitMap in A2 and returns a pointer to the BitMap in
A3. Call LockBitMap just before you start using the BitMap. If memory is sparse, try
to avoid allocating memory when there are locked BitMaps in memory. This will
sidestep any heap fragmentation problems.
After you are done working with a BitMap, call UnLockBitMap to allow the
memory manager to relocate the BitMap as it sees fit. UnlockBitMap takes a handle to
the BitMap in A2. But be sure to lock it down again before using it.
The final routine is KillBitMap which, given a handle to the BitMap in A2,
releases the memory occupied by the BitMap. If you don't want to kill your application
as well, be sure not to use the handle after killing the BitMap.
More than one way to skin a BitMap
So we've stolen a BitMap, linked it into our resource fork, and are holding onto
it by the handle. Now lets get it on the screen. This month's source code contains a
couple examples of alternate ways to display a BitMap.
These routines fall into two general categories: ones that employ patterns and
ones that employ regions. Lets start with the pattern based copies.
Wizzo Shows Bit Map Animation
Our Wizzo program shows how we can read in the stolen bit maps created with
BitNapper and display it on the screen with some dissolve effects. Wizzo has two pattern
based routines for use in titling or other dramatic drawing of the bit map. The two
examples of pattern based routines are FadeIn and FadeOut.
FadeIn Shows Dissolve Effect
FadeIn takes a BitMap handle in A2 and dissolves it slowly onto the screen. The
top,left corner of the destination is passed to FadeIn in D3,D4. First it locks the
BitMap in memory. Then it makes a duplicate copy of the BitMap.
In the main loop it copies the source BitMap to the destination BitMap (which is
off the screen). It then sets the pen mode to notPatBic. In this mode, any time a
pattern is drawn on the BitMap, it performs a logical AND with the BitMap's current
bit image. Figure #4 should make this more clear. The application has a table of 18
patterns of increasing darkness. In each iteration, a pattern is drawn over the entire
duplicate BitMap, which at this point contains a copy of the source BitMap. Now we
have a copy of the source BitMap in which only those bits set in our current pattern are
set. I know...confusing, but the illustration is more clear.
After this, the duplicate is copied to the screen. Then the process begins again
with a slightly darker pattern, until finally we have an all-black pattern which copies
the entire BitMap. FadeIn then unlocks the source BitMap and disposes of the memory
it allocated for the duplicate BitMap.
FadeOut does the opposite, as you may have guessed. Except that FadeOut only
requires that you pass it a pointer to a rectangle in A4. It dissolves the bits enclosed
within the rectangle on the screen. When it returns, the rectangle will be completely
white.
FadeOut simply sets the pen mode to notPatBic and repeatedly does a _PaintRect
with successively lighter patterns. It works from the end of our pattern list to the
beginning.
These are fairly simple examples. Other possibilities are patterns of diagonal
lines which move in barbershop-polelike fashion. Or perhaps altternating
checkerboard patterns. Experiment with different variations.
A two-edged sword
The next set of copy routines are region based. They facilitate the use of
QuickDraw's ability to clip graphics to an arbitraty region. The problem that arises
here is that QuickDraw, as David Letterman might say, is "just too darn powerful." It
can do all sorts of fabulous calculations with regions, but it requires great sacrifices
in speed. When any kind of region calculations are involved, QuickDraw bogs down.
There are certain solutions, but in some cases it is better to write your own
application-specific routines which are frightfully optimized for your specific case.
Examples of this will come in future issues. Stay tuned, campers.
Our region based routines, OpenRight and OpenOut repeatedy call _CopyBits with
maskRgns that reveal more and more of the BitMap with each iteration. If you are not
aware of it, _CopyBits allows the caller to pass it a region to which the copied bits will
be clipped. OpenRight starts with a rectangular region which clips all but the leftmost
vertical row of bits, and expands the region to the right until the entire BitMap is
copied. OpenOut starts with a region that clips all but the centermost bit of the BitMap
and expands outward in all directions until the entire BitMap is copied. Both of these
routines use the routine _RectRgn which creates a rectangular region, given a