Icon Stuffing
Volume Number: 7
Issue Number: 7
Column Tag: HyperChat
Related Info: Quickdraw Resource Manager
Icon Stuffing XCMD
By Steven Fuchs, Stony Brook, NY
iConjured Up
Last year at a meeting of LIMUG, the Long Island Mac Users Group I was
introduced to a very useful utility stack called iConjurer by Tom Wimbish, which as
the name would suggest is a tool for creating icons. What iConjuror provides is an
environment from which the powerful painting tools contained in HyperCard can be
used to create icon sized pictures as well as a convenient method of storing them.
iConjuror’s shortcoming was that it provided no direct way to convert the pictures
HyperCard created into ICON resources. The workaround for this was to copy the image
of the icon onto the clipboard and then launch ResEdit, open the requested stack and
paste the picture into ResEdits ICON editor, which would convert the first 32 pixels in
each of the first 32 rows into an icon. Although once committed to muscular memory
this process could be accomplished quickly, it was decidedly un-Mac like, in other
words there had to be a better way. I decided that what was needed was an XCMD which
could take the image of an icon and create an ICON resource, either in the current stack
or in any desired stack. Hence the impetus behind the creation of the IconStuffer XCMD.
Working Overview
When a picture doesn’t tell a story
The first order of business was to make the data of the selected picture available
to the XCMD. While some sleuthing into HyperCards inner workings probably could
produce a direct method of obtaining the contents of the selection rectangle this is
undesirable for some very good reasons. In general it is by far the more difficult task,
and in any programming, Mac programming e specially and XCMD programing even
more so you should always look to avoid trouble. Secondly a method of this sort would
almost certainly not work with a different version of HyperCard and may not even work
between different machines. Therefore I chose to take the easier path and obtain this
information from the clipboard, after forcing the user to copy the image of the icon.
However easier does not mean easy, and accessing the clipboard for the icons image is no
exception. The clipboard stores its images in the form of a ‘PICT’ resource, Inside
Macintosh tells us that the format for a ‘PICT’ resource consists of the QuickDraw calls
necessary to recreate the image. The ‘ICON’ resource is simply a series of 1024 bits,
the ones and zeros of which represent the black and white pixels of the icons 32x32
rectangle. While much time could have been spent decoding the ins and outs of ‘PICT’
resources and learning how to convert these to icons, a much easier mechanism exists.
All that is involved is pulling a bit of a fast one on the operating system and QuickDraw.
Now You See It
All QuickDraw drawing takes place into a data structure known as a GrafPort.
When you create a window unbeknownst to you, you are creating a GrafPort. While the
Mac OS allows for the creation of GrafPorts on their own it is rarely used, and I will not
do that. What I do need to recreate is an important record of the GrafPort called
portBits. The portBits record is a bitmap which is the destination of all drawing by the
GrafPort.
This bitmap contains the ones and zeros which make up the black and white
pixels of the GrafPort in question. It is this data which can easily be converted into an
‘ICON’ resource. The important record in the bitmap is the BufferAddr record, which
is a pointer to the location at which the bits that make up the image begin. The
QuickDraw sleight of hand I used was to create my own bitmap and fool the GrafPort into
drawing into it. The data is now in a form suitable to my purposes and is ready to be
made into an icon.
Step by Step
Take a picture, please
Before proceeding some error checking must be done, I know, boring,boring,
boring. What must be, must be however and the first form of this takes place in the
function FillIconPointer. Using the toolbox function GetScrap we will determine
whether or not there is a ‘PICT’ resource on the clipboard at all. This is done by
passing a handle of arbitrary size, and telling it I would like the available ‘PICT’
resource only. The function returns the size of the resource as well as resizing the
handle and copying the ‘PICT’ resource into it.
If there was a ‘PICT’ resource in the scrap I need to allocate the memory
necessary for the icon. Since I know an ‘ICON’ measures 32x32 I am spared the step of
figuring out the size of the necessary bitmap. The space of the bitmap is allocated using
the NewPointer function. Since NewPointer accepts its size parameter in bytes and not
bits, and there are 8 bits in a byte, the size of the pointer should be 32x32/8 or 128.
I allocate this into the pointer BufferAddress and in a fit of safety consciousness, check
to be sure the allocation succeeded. Assuming that it did, I then assign the remaining
records of the structure OffMap. Record 1 of a bitmap, baseAddr is a pointer to the
address at which the bitmap image starts. Record 2 of a bitmap is rowBytes, or the
number of bytes wide the image is to be. Since an ICON is a fixed size I know that this
value will always be 32/8 or 4. In some other circumstance I would compute rowBytes
by dividing the width of the bit image by 8. Also an important note is that the Mac
operating system requires that all addresses be even, so to avoid a deadly crash the
value for rowBytes must always be even. Finally we set bounds, which is the rectangle
which encloses our bitmap or 32 x 4.
Now that we are fully prepared to fool QuickDraw only a few more precautions
remain. First and foremost is to remember the bitmap currently stored in portBits so
as to return things to their original state upon completion, very important for the
XCMD programmer. I obtain the existing portBits by calling GetPort and storing the
value of portBits into the variable OldMap. Then I use the toolbox call SetPortBits to
reassign any subsequent drawing in that GrafPort to my bitmap. I then call ClipRect to
insure that the entire image is drawn and EraseRect in order to clear any existing bits
from the BitMap. Then a simple call to DrawPicture will allow QuickDraw to fill in the
proper pixels of my bitmap for me. Finally a call to SetPortBits again will restore the
GrafPorts portBits, dispose of the handle to the PICT resource, and return as the result
of FillIconPointer the address at which the bitmap image of the icon begins.
How a bitmap becomes an ICON
The path to creating an ICON resource now becomes very simple since I now have
the data in the form I want it. Most of this remaining work takes place in the function
CreateNewIcon . First I create a handle of size 128, the fixed size of an icon. A handle is
necessary since the toolbox call AddResource demands a handle aand must be used to
attach a resorce to the resource file. I then invoke the toolbox call BlockMove to copy
the data from the bitmaps pointer to the handle. With the handle now prepared call
AddResource which attaches the handle to the resource fork and WriteResource which
makes it final by saving the resource to disk. Finally release the memory used by the
icon. Congratulations! You have just made an icon.