Color Icons
Volume Number: 5
Issue Number: 10
Column Tag: Programmer's Forum
Understanding Color Icons 
By Steve and Patricia Sheets, Herdon, VA
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Understanding, Creating and Using Color Icons
Traditional Quickdraw contains numerous data structures. Bit Images, BitMaps,
Patterns, and Icons are all graphic concepts that a Macintosh programmer uses in
order to manipulate Quickdraw. Most of these graphic concepts are layered in
construction. For example, in order to understand how to create and manipulate Icons,
a Mac programmer needs to know about Bitmaps.
The topic of this column is the Color Icon data structure. The same layered
approach that is used in traditional Quickdraw is also used in Color Quickdraw. Thus in
order to understand Color Icons, this article will explain about Pixel Images, Pixel
Bitmaps, and Color Look-up tables. Once the Color Icon’s structure is defined, this
article will discuss the simple usage of Color Icons in Menus and Dialogs. Finally,
creating Color Icons using ResEdit templates will be covered.
Bit Images and Bitmaps
In Quickdraw, the basis of all graphic data structures is the Bit Image. The Bit
Image is a portion of memory that represents an arbitrary black and white display.
Each bit of the image represents a pixel of the display. If a Bit is set, the pixel is
black. If it is unset, the pixel is white. The pixels are laid out left to right, then top to
bottom. The Pixel in the upper left corner is defined in Bit 1, the Pixel to the right of
it is defined in Bit 2 and so on. Notice that a Bit Image has no built-in definition.
There is no explanation of the horizontal or vertical dimensions of the image.
Non-color Macintosh (Mac 128K, Mac 512K, Mac Plus, Mac SE) screens are
represented as Bit Images.
The Quickdraw Bitmap is the data structure that represents an exact black and
white display. A Bitmap contains a pointer to the Bit Image, the row width of that Bit
Image, and the coordinates of that Image. The coordinates of the Bit Image is defines as
a boundary rectangle. The upper left coordinate of the rectangle is the first pixel of
the Bitmap. In most cases, this is defined as 0,0. While it is easiest to work with
Bitmaps laid out this way, the upper left coordinate could be any value. The row width
defines the the number of pixels that are on one row of the Bit Image. This is given in
Bytes of memory, not pixels. Also, the row width must be an even number of Bytes.
Thus a BitMap that had 32 pixels in a row would have a byte with at least 4 bytes
(8*4). The row width could be larger, that would just mean some of the bits of the
Image were not being used. For example, a Bitmap that had only 30 pixels in a row
would still require a row width of 4 bytes. The 31st & 32nd bits would not be used.
{1}
Bitmap = RECORD
baseAddr: Ptr;
rowBytes: Integer;
bounds: Rect;
END;
Pix Images and PixMaps
Color Quickdraw’s equivalent of a Bit Image is the Pixel Image. The Pixel Image
is a portion of memory that represents a display, possibly a color display. Each Pixel
on the display is represented by one or more bits in the memory. The number of bits
that is required for a single pixel is called the depth of the Image. Pixel Images are
similar to Bit Images in that the size and row width of a Pixel Image is not defined.
Also undefined is the depth of the image, the exact lay out of the bits in memory, and
the conversion of the bits of a pixel to colors. All these values are defined in a Pixel
Map.
The Pixel Map is Color Quickdraw’s equivalent of a Bit Map. Like a Bitmap, it
contains a pointer to the Image (Pixel in this case), the row width (in bytes) of the
Image, and the dimensions of the rectangle. However the Pixel Map is more
complicated than that.
{2}
PixelMap = RECORD
baseAddr: Ptr;
rowBytes: Integer;
bounds: Rect;
pmVersion: Integer;
packType: Integer;
packSize: LongInt;
hRex: Fixed;
vRes: Fixed;
pixelType: Integer;
pixelSize: Integer;
cmpCount: Integer;
cmpSize: Integer;
planeBytes: LongInt;
pmTable: CTabHandle;
pmReserved: LongInt;
END;
PixMapPtr = ^PixMap;
PixelMapHandle = ^PixmapPtr;
A PixMap is usually manipulated as a handle. Most of the data structures and
routines that work with PixMaps, use or pass handles to the PixMap. For this reason,
the 3 high bits of the row width field are used as flags. The high bit of the row width
field must be set (1). This indicates that this data structure is a PixMap, not a
BitMap. The next two bits are reserved for future use. For now, they must be unset
(0).
Beyond the three normal Bitmap variables, the PixMap has twelve other
variables. The pmVersion field contains the version number of Color Quickdraw.
Currently this is set to 0. The packType field is used to define the Packing Algorithm
that the PixMap uses on the bits. Again, this is normally set to 0 since, as of now,
Color Quickdraw does not support a PixMap packing algorithm. The packSize field
contains the number of bytes of the packed image. It is set to 0 if no packing algorithm
is used. The hRes and vRes fields contain the horizontal and vertical resolution of the
PixMap in pixels per inch. Currently all Mac screens are 72 DPI, thus the hRes and
vRes settings of the Pixmap are 72.
The next few fields define the exact layout of the bits in the PixMap’s Pixel
Image. The pixelType field defines the format. PixelType should be set to 0 to indicate
Chunky. A value of 1 indicates “Chunky/Planar” format and a value of 2 indicates
“Planar”. The first and most important format is the “Chunky” format. In a Chunky
Pixel Image, all of a row’s pixels are stored consecutively. Thus for a Pixel Image
with the depth of 4, the first 4 bits of memory represent the first Pixel.
Originally, Chunky format was the only format that Color Quickdraw could use;
all Mac // video cards used “Chunky”. However, with the introduction of video cards
with more than 8 bits per pixel, the other formats were also used. The Planar and
Chunky/Planar formats divide the Pixel Image in memory into separate sections.
These sections, called planes, usually represent different color components of a shade.
For example, a 24 Bit card might divide the memory up into Red, Green and Blue
portions, or components, of memory. Hopefully, this column will be discussing more
about 32-Bit Quickdraw in the future. For this article, only Chunky Format will be
explored.
For Chunky Format, it is important to realize that for every pixel on the screen
there is a group of consecutive bits in memory. This group of bits contains an integer
value, representing the color of the pixel being displayed. For example, a PixMap with
a depth of 4 would have 4 bits for every pixel in the image. Since 4 bits can contain
16 distinct integer values (0 thru 15), that PixMap could contain 16 different colors
at one time. A 8 bit PixMap could contain up to 256 distinct colors.
The pixelSize field of the PixMap defines the depth of the PixMap and it’s Pixel
Image. The pixelSize must be in powers of 2 for Chunky format (ie. 1, 2, 4, 8). The
next field, cmpCount, defines the number of color components (planes). The cmpSize
field contains the number of bits per each color component. The planeBytes field gives
the offset from one color plane to the next. Since Chunky format has only 1 component,
the cmpCount field is set to 1, the cmpSize field should match the pixelSize field, and
the planeBytes field should be set to 0 (indicating no offset). Jumping ahead one field,
the pmReserved field is exactly that: reserved for future expansion and currently set
to 0.
The last field of a PixMap to be explained is the pmTable field. So far, the layout
and dimensions of the PixMap have been defined. However, there has been no mention
of how the value of a set of bits is converted into a RGB color. Such a conversion is
defined in a Color Table data structure. The pmTable field contains a handle to a Color
Table data structure.
Color Tables
So far in this article, the terms ‘screen’ and ‘PixMap display’ have been used
interchangeably. This is not exactly correct. All video display devices are Graphic
Devices. Graphic Devices have numerous data structures associated with them,
including a device driver, Color GrafPort, a Color Table and a PixMap. The Graphic
Device’s PixMap is the entire viewable area of the screen. This is similar to the way
traditional Quickdraw has a screenbit variable that is a Bitmap of the entire screen.
Other PixMaps that contain portions of the screen must point to the same data as the
Graphic Device’s PixMap and must share the same Color Table. However that Color
Table is owned by the Graphic Device, not any PixMap. Offscreen PixMaps can also be
created. These PixMaps would point to a different Pix Image than the Graphic Device.
Each PixMap could have it’s own Color Table or share a Graphic Device’s Color Table.
More will be discussed in the next article about the advantages of each method. For
now, realize that there is a difference in layout between a Color Table owned by a
PixMap and one owned by a Graphic Device.
Both PixMaps or Graphic Devices use the Color Table data structure to convert
the bit values of pixels into some RGB color. To do this, a Color Table contains a list of
colors, defined in the Color Spec data structure. Each Color Spec contains a RGB color
field, defined using the RGBColor data structure which specifies the Red, Green and
Blue portions of the color. Each Color Spec also has a number associated with it. Thus
if the Color Table has 16 colors, there are 16 numbers associated with it. If the bits
that are associated with a specific pixel of a PixelMap are equal to the number of some
Color Spec, then that Pixel has that associated RGB value. If the PixMap was part of a
Graphic Device, the display would show that RGB color on the Graphic Device. Where
the number that is associated with each Color Spec comes from will be explained below.
When some pixels of a PixMap are copied onto another PixMap, each pixel on the
source PixMap is converted into the RGB value. Then the closest matching RGB value is
found on the destination PixMap. The associated value is then placed in the destination
PixMap in the correct bits. Thus PixMaps having various depths and colors can be
copied from one and another. While it makes things simpler and faster if the PixMaps
share the same Color Table, it is not necessary. Color Quickdraw calculates the correct
bit values for all the pixels even if they do not share the same Color Table.
{3}
RGBColor = RECORD
red: Integer;
green: Integer;
blue: Integer;
END;
ColorSpec = RECORD
value: Integer;
rgb: RGBColor;
END;
ColorTable = RECORD
ctSeed: LongInt;
ctFlags: Integer;
ctSize: INTEGER;
ctTable: ARRAY [0..0] OF ColorSpec;
END;
CTabPtr = ^ColorTable;
CTabHandle = ^CTabPtr;
The Color Table data structure consists of a handle to a variable size record. The
first field, the ctSeed field, is the version identifier number used internal by Color
Quickdraw. It keeps track of changes to the Color Table. Everytime the Table is
changed, the ctSeed needs to be reset using the Color Manager GetCTSeed function. If
Color Quickdraw changes the Color Table, it will reset the field. If an application does
the changes, it must reset the field. When creating PixMaps, this field is set to 0.
The second field of the Color Table is the ctFlags field. As mentioned above, some
Color Tables are owned by Graphic Devices, while others can be owned by simple
offscreen PixMaps. If the PixMap owns the Color Table, this field must be set to 0. If
the Graphic Device owns the PixMap, the high bit of the field will always be set. The
rest of the bits of the field will be used as flags by the Graphic Device Manager.
The next field of the PixMap is the ctSize field. It is the number of RGB colors
that are defined in the Color Table. The number of colors equals the value of the field
minus one, thus a setting of 1 indicates 2 Colors, while a setting of 255 indicates 256
colors. For a Graphic Device, this number needs to be a power of 2. For a PixMap,
this number can be any positive value. Remember that the bit depth of the PixMap, not
the ctSize field, defines the maximum number of colors for a Pixel Image.
The last portion of the Color Table contains a variable size array of Color Spec
data structures. The RGB field contains the exact Red, Green and Blue components of
the Color Spec. The number that is associated with that Color Spec is dependent on who
owns the Color Table. Graphic Devices use the position of the Color Spec (zero count)
in order to find the number. Thus the first Color Spec would have the number 0, the
next Color Spec would have the number 1 and so on. In that case, the value field of the
Color Spec is used internally by the Color Device manager. Color Tables that are owned
by PixMaps use the value field to determine what the number of the Color Spec is.
Each value field of the Color Table should then have a unique number. While the
contents of the value fields can be in any order (first Color Spec, value 9, next Color
Spec, value 3, next one, value 23, and so on), it is recommended that PixMaps follow
the Color Devices method of numbering the Color Specs (first Color Spec, value 0, next
Color Spec, value 1, next one, value 2, and so on). This makes it easier to visualize
the image.
Graphic Devices have very special rules about the Color Tables associated with
them. As mentioned above, the number of colors must be a power of 2, and match the
bit depth of the Graphic Device. The first color must be white (RGB value
$FFFF,$FFFF,$FFFF) and the last color must be black (RGB value $0,$0,$0). Color
Tables used with PixMaps are much more unrestricted in their constructions. There
can be any number of Color Specs and they may be in any order. Just remember that
the PixMap that points to the Color Table can only use the first N number of colors
where N is dependent on the depth of the Pix Image (depth 4, 16 colors, depth 8, 256).
It is a good rule of thumb to have the first Color Spec be white and the last one be black.
Again, this makes it easier to visualize the image.
Icons and Color Icons
Now that an Image, a Bitmap, a Pixel Image and a PixMap have been discussed,
traditional Icons and Color Icons can be explained. Under Quickdraw, an Icon is a Bit
Image of a very specific size. The dimensions of an Icon are the always the same. An
Icon is a 32 bit by 32 bit black and white image. It’s Row width is 4 bytes. Since the
dimensions are always the same, the Icon data structure consists of a handle to the Bit
Image portion of the Icon.
Under normal Quickdraw, there are two types of Icons; icons stored in ‘ICON’
resources and icons stored in ‘ICN#’ resources. The handle of a ‘ICON’ type Icon
contains only the Bit Image. Since the Row Width is 4 bytes and the vertical size is
32, the size of such an Icon handle is 128 bytes. When an ‘ICON’ type Icon is drawn
on the screen, the entire Bit Image is transferred (all 32 by 32 pixels) to the screen.
An ‘ICN#’ icon’s handle contains the Bit Image of the Icon followed by the Bit
Mask of the Image. Thus it is twice the size, or 256 bytes. If the Bit Image portion
explains what is to be drawn, the Bit Mask portion explains where. Thus each pixel
has 2 bits associated with it, an Image bit and a Mask bit. When an ‘ICN#’ type Icon is
drawn on the screen, only the pixels of the Bit Image, whose corresponding Mask bits
are set, are then transferred to the screen.
For example, imagine an ‘ICN#’ type of Icon consisting of a 10 by 10 frame. The
Bit Image portion contains a hollow square. The pixels in the frame are set (black).