Kolorize
Volume Number: 7
Issue Number: 6
Column Tag: C Workshop
Related Info: Color Quickdraw
Kolorize Your B&W Application
By Kirk Chase, Anaheim, CA
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
Introduction
The introduction of the Mac II brought color to release the user from the shackles
of boring black and white displays. Now the user could use color to hilight, to draw, and
to entertain. His spreadsheets could now show that he was in the “red”; his text could
be hilighted in yellow; and his desktop could be blue as the sky.
What color brought to the developer was headaches. Palettes, color tables, and
PixMaps were new structures to be learned and manipulated. Color images brought a
tremendous increase in file and application sizes. Toolbox calls separated between
color and non-color interfaces and had to be dealt with. Calibration of color between
monitors and other output devices was, and still is, a great issue. And add to this the
problem/feature of allowing the user to set the number of colors of the screen on the
fly, and developers have their hands full. To deal with this problem, some developers
have published a color and non-color version of their product rather than try to deal
with the issues at the same time.
Dealing with color for most applications is not a great problem. Most
applications do not need “animating palettes” or “32-bit Color QuickDraw”. Most
applications need just a smidgen of color to liven up their displays (and sales) and to
remove the stain of being “non-color” in the Mac world. Most applications are not
paint or drawing programs. In fact, some applications require no user-interface at all
and run “quietly” in the background.
Still, it would be nice to have a little color. For those who remain in the black &
white world of programming, you may be asking yourself, “Is there something between
the mouse of B&W coding and the elephant of color coding?” There is. You don’t have to
eat the whole elephant at once. There is a middle ground to be found. It adds minimal
code to your application, but it is not without its limitations.
The Macintosh Has ALWAYS Had Color
The word “classic” has been used to name the newest low-cost, low-end
Macintosh computer line. The original “classic” QuickDraw has always had color
ability. Within the Grafport are two fields, fgColor and bkColor, that controls the
foreground color and background color through the toolbox calls ForeColor() and
BackColor(), respectively. Also defined are eight colors (black, white, red, green,
blue, cyan, magenta, and yellow).
The foreground color is initially set to black and the background color is initially
set to white. By setting these colors before doing your drawing, you will get color
drawing on all Macintoshes; the pen’s characteristics including pattern and mode work
as they always do.
Drawback or Feature?
One thing to remember while drawing using the classic QuickDraw color model is
the different monitor modes you will be in. The first instance is the black and white
monitor; in this instance, all colors, except white, are drawn in black. This means that
you should have white as either the background or foreground color; otherwise on B&W
monitors you will have black on black drawing (great for displaying top secret
information, but terrible otherwise).
A similar problem exists with color/monochrome monitors set to shallow color
depths. For example, on my color monitor set at four colors (less than the eight colors
of QuickDraw), green and blue appeared black; magenta and red were the same color;
yellow appeared white, and cyan remained the same. In grayscale, the same problem
existed with minor color variations due to the interpretation of color luminosity.
On top of this, there are a number of applications that change the color look-up
table that describes the various RGB percentages which in turn change the color’s
intensity. Also, different monitors have different values for colors. What this all
means is that you can not depend on being able to distinguish, say, black from blue and
yellow from white all the time.
I suppose you could check for the depth of the screen and values of the current
colors. But this would mean going into all the things that you didn’t want to go into in
the first place; you might as well use Color QuickDraw and its associated Toolbox
managers. A better way to go is to do your drawing with black and white monitors in
mind. This means keeping white as either the foreground or background colors when
using the other colors. Then, you should not depend on any color other than black and
white being present; you should give consideration to the fact that black and white might
be the only colors seen.
For example, let’s consider we are drawing a pair of dice for a game. We make
the die green, the dots blue, the die’s outline black, and the dots’ outlines red.
Regardless of whether you think this color combination is great, in black and white, it
looks like a black blob. So you change the dots’ outlines to white so you can distinguish
between the die and the dots. You may still have a problem because there may not be
enough white outline to quickly distinguish the dots on a B&W monitor. You need to
leave enough “white” space between colors to look okay in B&W. Another problem is
the “colorful” screen that is chock full of colors. Neat, but in B&W it looks like a
black hole.
These are the basics to classic Quickdraw. You only have eight colors. Make
white either the foreground or background color. Plan for black and white displays.
Kolorize
Now that you’ve got the basics down of ol’ classic QuickDraw. There are a few
things that you can do to step beyond this. I will talk about three things. The first is
getting more colors than the standard eight. The second involves colorizing areas of the
screen such as text. And the third involves colorizing controls. All these routines are
found in the source listing Kolorize.c and .h.
More Colors
When you are on a black and white Mac, the only colors you get are black and
white. Yet the desktop is gray and the scroll bars have a light-gray page region. Okay,
they are not true colors but dithering patterns to make them appear to be a certain
color (mainly a shade of gray).
Well, you can do the same thing with colors. The routine, PaintMixedRect(),
takes a rectangle and a palette of four colors and paints a multi-color rectangle on the
screen. If done correctly you can get gray using black and white, light green using
green and yellow, turquoise using green and blue, brown using green and red, and many
other colors using the standard colors.
PaintMixedRect() creates this illusion of many colors by using an array of four
dithering patterns, oqdMask4[]. Each bit in each pattern is unique within the four
patterns. If you were to stack the patterns on top of each other, combining them, you
would get a solid, black pattern, and no bit would be used twice.
To paint a pattern, you pass an array of four colors and the rectangle to
PaintMixedRect(). This routine sets the pen transfer mode to patOr. Then
PaintMixedRect() loops through the four colors painting the rectangle using the i-th
color and the i-th mask pattern. The first pass paints one quarter of the rectangle in
the first color and then loops back to do the rest in the other colors and patterns. The
reason the pen mode is set to patOr is so we don’t erase any of the pixels we previously
drew.
The dithering pattern used has been created to put up a gray pattern if you pass
black/white/black/white. If you pass black/white combinations in other orders you
can get different patterns such as vertical stripes (black/black/white/white) or
horizontal stripes (black/white/white/black). My pattern was hard coded, but you
could create a different set of dithering patterns.
Colorizing a Rectangle
The routine Kolorize() turns all the black pixels in a rectangle to a single color.
It does this by setting the forecolor to the color you pass in and then performing a
CopyMask() call using the same rectangle as the mask for CopyMask(). What this does
is re-paint that rectangle in the color you specify. By using the same rectangle as the
mask, you assure that only the original, black pixels are redrawn and the original,
white pixels are left alone.
Another routine, KolorizeMix(), takes Kolorize() and PaintMixRect() and
combines them. It uses the dithering pattern to draw only portions of the black pixels
in one of the colors you pass and then again for other colors and dithering patterns.
KolorizeMix() is a little trickier than Kolorize() and PaintMixedRect().
KolorizeMix() needs a couple of offscreen ports to do its job. Since the dithering, mask
patterns may clear some bits, we need a copy of the original area, copyPort, we are
working on and a work area, offPort. These port and bitmap manipulation routines are
fairly standard and you can get the source for them almost anywhere.
KolorizeMix() first makes a copy of the area we wish to colorize. Then it enters
a loop. First, it copies the copy onto the offscreen area. Second, with the i-th dithering
mask and proper pen mode, it clears out all bits that are not in the i-th color. Finally,
it sets the foreground color to the i-th color and does a CopyBits() call to transfer the
bits to the screen; CopyBits() automatically does the colorizing for you!
Color in Control
With Kolorize(), you should be able to colorize any rectangular area--including
controls. KolorizeCDEF() does just this for the standard button, checkbox, and radio
control. Simply pass in the control handle, the control type, and the button and
indicator color.
KolorizeCDEF() will first colorize the whole control in the button color using
the control’s rectangle. Then depending on the type of control it is and whether the
button and indicator colors are different, this routine will then kolorize the inside “x”
of the checkbox or the “•” of the radio button.
Controls are different than normal areas on the screen. They are active, and
respond to mouse clicks by hilighting themselves. The standard controls know nothing
of your colorizing attempts. They simply draw and hilight the controls in black and
white on a black and white screen. Therefore you need to re-colorize them in your
screen update routine and after the control receives a mouse click. When the control is
tracking the mouse (on a mouse click in a control), the control automatically is
drawn/hilighted in black until the mouse button is released and you re-colorize. I do
not find this “reverting to black” so terrible; it gives additional information on color
screens that a mouse down was detected in a control and tracking is being done.
Conclusion
Kolorize.c and the techniques given in this article will allow anyone to add color
to there interface. If you simply need a few colors, classic QuickDraw provides eight
for you, and this article explains how to “squeeze” out a few more without too much
extra work. Possible future enhancements would be differing dithering masks-perhaps
eight instead of four, colorizing scroll bars or other control types, extending color
tracking to controls, drawing TextEdit’s hilight in color, and there are a number of
other directions you could take.
Remember, color using classic QuickDraw is very rudimentary. There are
severe limitations such as the “any color on white or white on any color” problem and
the “white space” problem. In no way am I suggesting you never use Color QuickDraw.
Color QuickDraw provides a much richer environment for working with color. But, if
your application is not “color intensive”, you can add that little bit of color without too
many hassles with good, ol’ QuickDraw.
Listing: OQDKolorize.h
/************************************/
/* OQDKolorize.h
Prototypes for Offscreen.c */
/************************************/
extern Ptr NewBitMap(BitMap *bm, Rect *r);