TweetFollow Us on Twitter

History of Icons
Volume Number:8
Issue Number:5
Column Tag:Pascal Workshop
Related Info: Resource Manager Picture Utilities Quickdraw
Color Quickdraw

Icon Family

The history of icons, from 1984 until now

By Steve Sheets, MacTutor Regular Contributing Author

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

When the Macintosh was originally introduced, one of the major new concepts that it presented was the notion of an “icon.” The Macintosh was the first mass marketed computer system that used the concept of an icon extensively. Users learned, for the first time, how to work in an Icon-based environment. While the concept of an icon has not changed much for the user, for the programmer the technical aspect of an icon has changed with each new volume of Inside Mac.

This article is intended to be an explanation of how Icons are programmed on all Macintosh platforms. The information will start with the simplest definition of Icons (ICON & ICN#), such as those used by the original Macintosh 128K, and continue on to explain the more complicated color icon structures (cicn), and then will explain the new and poorly documented icon family structures (icl8, icl4, ics#, ics4, ics8). Various routines will be given that will function on all versions of the Macintosh and will provide Update, Selection and Mousedown detection capability.

For the purpose of this article, an icon is defined as a graphic entity on the screen that you can directly manipulate using the mouse. Icons can be clicked on, moved around, thrown out, and redesigned. Most of these functions are so simple to do that even my two year old daughter can perform the manipulations. Icons form the basis of user friendliness in action, and are essential for the direct-manipulation type of interface the Macintosh uses.

Original Black & White Icons

Originally the Macintosh was a black and white machine only, so all icons were strictly black and white images, exactly 32 by 32 pixels in size. The number of bytes it takes to display a row of a black and white icon image is commonly referred to as the Rowbytes. Since each pixel could be represented as a bit (set or one equals black, clear or zero equals white), a 32 pixel row could be represented with 4 bytes of memory (i.e., it’s Rowbytes value is 4). Thus the entire 32 line icon takes up 128 bytes of memory. The data structure containing an icon usually is manipulated as a Handle structure. Such a handle uses the ‘ICON’ Restype when the handle is stored in a resource file or copied to the scrapbook.

{ 1 }

 type
 Ticon = ARRAY[1..4,1..32] of 0..255;
 TiconPtr = ^Ticon;
 TiconHdl = ^TiconPtr;

The following is what this kind of icon looks like. Also displayed is a blown up version of the same icon so you can see the individual pixels.

Figure 1

The following routine will draw a black and white icon in the current GrafPort:

{2}

 procedure DrawBWIcon (theIcon: Handle;
 thePoint: Point);
 var
 myRect: Rect;
 myBitMap: BitMap;
 myPort: GrafPtr;
 begin
 if theIcon <> nil then
 if theIcon^ <> nil then begin
 Hlock(theIcon);
 with myBitMap, bounds do begin
 baseaddr := StripAddress(theIcon^);
 rowbytes := 4;
 topleft := thePoint;
 right := left + 32;
 bottom := top + 32;
 end;

 GetPort(myPort);

 CopyBits(myBitMap, myPort^.portBits, myBitMap.bounds,
 myBitMap.bounds, srcCopy, nil);

 Hunlock(theIcon);
 end;
 end;

The procedure given always draws the icon in the current GrafPort so that given a specified point, the pixel is drawn so that the top left pixel is that point. This is fairly arbitrary. The routine could be written so that the point given defines the center of the icon. The routine could also be written so that the icon is drawn in a given (possibly offscreen and/or non-current) GrafPort or BitMap. Any method that is used is fine, as long as all routines use the same method. All routines provided in this article draw in the current Grafport at a specified point.

The one problem with this type of icon is that the black and white image is copied exactly over the existing GrafPort. All white pixels are copied exactly to the GrafPort regardless of whether or not the pixel is part of the image being displayed. This can cause problems when you draw an icon on top of a non-white background. For example, imagine some icons drawn on various backgrounds.

Figure 2

When drawing the document icon, you would be drawing the document image as well as the white area around it. While this would be correct on a white background, when you draw on any other background, this is not the image you want to show. You want only the document portion to appear. Thus you need additional information to explain which pixels to transfer. There needs to be a way to distinguish which of the pixels of the icon actually needs to be drawn. Unless the icon represents a perfect 32 by 32 square pixel image, pixels of the icon may not need to be transferred.

To solve this problem, the ‘ICN#’ (commonly pronounced ‘Icon number’) data structure was also developed. This structure can contain one or more 32 by 32 black and white icons. Each icon is identical to the ‘ICON’ structure defined above. While the ‘ICN#’ can contain any number of icons, in practice, it contains two icons used to display a single image. The first icon is the black and white image; the second icon is the mask of this image.

 type
 TiconArray = array[0..0] of Ticon;
 TiconArrayPtr = ^TiconArray;
 TiconArrayHdl = ^TiconArrayPtr;

A mask is a second icon map that provides additional drawing information for the first icon map (the image). All the black pixels of a mask represent spots where the image should be drawn, and the white pixels represented spots where the background should be seen. A mask is always associated with some image (be it black and white or color).

The following is an icon and it’s mask.

Icon Mask

Figure 3

Since the mask is provided, the icon can be drawn on top of any background (black, white, gray, etc.). The following shows how the icon would appear. Notice that only the pixels defined by the mask, not the entire 32 by 32 area, are affected.

Figure 4

The following routine draws an icon with mask at a given point:

{3}

 procedure DrawIconMask (theIcon: Handle;
 thePoint: Point;
 theFlag: BOOLEAN);
 var  myIconBitMap, myMaskBitMap: BitMap;
 myPort: GrafPtr;
 begin
 if theIcon <> nil then
 if theIcon^ <> nil then begin
 Hlock(theIcon);
 with myIconBitMap, bounds do begin
 baseaddr := StripAddress(theIcon^);
 rowbytes := 4;
 topleft := thePoint;
 right := left + 32;
 bottom := top + 32;
 end;

 with myMaskBitMap, bounds do begin
 baseaddr := Ptr(ord4(StripAddress(theIcon^))+$80);
 rowbytes := 4;
 topleft := thePoint;
 right := left + 32;
 bottom := top + 32;
 end;

 GetPort(myPort);

 CopyBits(myMaskBitMap, myPort^.portBits,
 myMaskBitMap.bounds, myMaskBitMap.bounds,
 srcBic, nil);
 CopyBits(myIconBitMap, myPort^.portBits,
 myIconBitMap.bounds, myIconBitMap.bounds,
 srcOr, nil);

 if theFlag then begin
 BitClr(Ptr(HiliteMode), pHiliteBit);
 CopyBits(myMaskBitMap, myPort^.portBits,
 myMaskBitMap.bounds, myMaskBitMap.bounds,
 srcXor, nil);
 end;

 Hunlock(theIcon);
 end;
 end;

The flag parameter indicates whether the icon should be draw selected (inverted) or not. If an icon is already drawn from a given point, then the following routine can be used to reverse the selection of the image.

{4}

 procedure SelectIconMask (theIcon: Handle;
 thePoint: Point);
 var  mySize, myRowbytes, myMaskOffset: INTEGER;
 myMaskBitMap: BitMap;
 myPort: GrafPtr;
 begin
 if theIcon <> nil then
 if theIcon^ <> nil then begin
 Hlock(theIcon);

 with myMaskBitMap, bounds do begin
 baseaddr := Ptr(ord4(StripAddress(theIcon^))+$80);
 rowbytes := 4;
 topleft := thePoint;
 right := left + 32;
 bottom := top + 32;
 end;

 GetPort(myPort);

 BitClr(Ptr(HiliteMode), pHiliteBit);
 CopyBits(myMaskBitMap, myPort^.portBits,
 myMaskBitMap.bounds, myMaskBitMap.bounds,
 srcXor, nil);
 Hunlock(theIcon);
 end;
 end;

One last routine is needed to manipulate the ‘ICN#’ structure. The following is the routine to determine if a spot is inside the mask of the icon (i.e., a mouse down at this point would fall within the icon mask) at a given point. In the following routine, thePoint is the position the icon is drawn from, while theSpot is the pixel to check. Instead of just checking if theSpot is inside the entire 32 by 32 icon area, this methods checks to see if theSpot is on top of the actual image as defined by the mask.

{5}

 function PtInIconMask (theIcon: Handle;
 thePoint, theSpot: Point): BOOLEAN;
 var  myRect: Rect;
 myFlag: BOOLEAN;
 myPtr: Ptr;
 begin
 myFlag := FALSE;

 if theIcon <> nil then
 if theIcon^ <> nil then begin
 with myRect do begin
 topleft := thePoint;
 right := left + 32;
 bottom := top + 32;
 end;

 if PtInRect(theSpot, myRect) then begin
 Hlock(theIcon);
 theSpot.v := theSpot.v - thePoint.v;
 theSpot.h := theSpot.h - thePoint.h;
 myPtr := POINTER(ORD4(StripAddress(theIcon^)) 
 + $80 + (theSpot.v * 4));

 myFlag := BitTst(myPtr, theSpot.h);
 HUnlock(theIcon);
 end;
 end;

 PtInIconMask := myFlag;
 end;

The ‘ICON’ (icon without mask) data structure is used mainly by the Mac ROM calls in Menus, Alerts and Dialogs. The ‘ICN#’ (icon with mask) data structure is used by Mac Finder to display icons in the Finder. It is also the data structure most commonly used by programmers. While many developer tools exist to create ‘ICON’ and ‘ICN#’, the preferred tool is Apple’s ResEdit utility which contains very good bit level icon editors. Icons created and used in a program are almost always stored in the resource file of the application as resource types ‘ICON’ and ‘ICN#’. The programmer uses the resource manager (usually ROM call GetResource) to load the icons into memory.

Color Icons

With the introduction of the Macintosh II, color was added to Macintosh programming. Obviously a color version of the black and white icon was needed. The Color Icon data structure was created, along with the resource representation of it, the ‘cicn’ resource. However there are many differences between the Color Icon and ‘ICON’ or ‘ICN#”. First, the Color Icon can be of any pixel size, not just 32 by 32, nor does it need to be square (100 by 16 is possible). Also, the Color Icon structure contains two icon images (a color image and a black and white image) and one icon mask. All three portions have identical dimensions. Finally, unlike the ‘ICON’ or ‘ICN#’ structures, the Color Icon structure is a complex handle. It actually is a handle to data, which includes handles to other data structures. The actual format of the Color Icon structure is as follows.

{6}

 type
 CIcon = record
 iconPMap: PixMap;
 iconMask:BitMap;
 iconBMap:BitMap;
 iconData:Handle;
 iconMaskData:array[0..0] of Integer;
 end;
 CIconPtr = ^CIcon;
 CIconHdl = ^CIconHdl;

The first portion of the data structure is the iconPMap, a PixMap (the color version of a bitmap). It contains almost all the information to draw a color image of the icon. The actual pixel data that the color image uses is stored in a handle in the iconData field. Besides many other fields, the iconPMap contains a handle to the Color Lookup Table that describes exactly which colors the pixels can use. The Color Lookup Table can be any normal bit depth. The bit depth is the number of bits needed to describe one pixel (1, 2, 4, 8, 16, 24). The most common bit depth for Color Icons is either four or eight (to match the majority of the color cards available for the Mac II). A four bit table contains up to 16 colors, while an eight bit table contains up to 256 colors.

While the iconPMap field contains the color image of the icon, the iconMask field (a bitmap) contains all the information about the mask and the iconBMap field (another bitmap) contains all the information about a black and white version of the icon. The actual bit data indicated by the two bitmaps is stored at the end of the CIcon structure in iconMaskData (a variable size field); first the mask bits, then the black and white icon bits. Remember that the images do not have to be 32 by 32 pixels.

Like ‘ICON’ and ‘ICN#’, a Color Icon is best created by using the color icon portion of ResEdit. Versions 2.0 and above of ResEdit include ‘cicn’ resource editors. However, since the Color Icon structure is a complex one (handles containing handles), a GetResource call is not enough to load the structure into memory. The ‘cicn’ resource is a template; portions of the template are loaded into separate portions (handles) of the Color Icon data structure. Thus, new Quickdraw routines were added to create the color icon. Additional routines were also added to draw and dispose of a Color Icon:

{7}

 function GetCIcon(id: Integer):CIconHandle;

 procedure DisposCIcon(theIcon: CIconHandle);

 procedure PlotCIcon(theRect: Rect; theIcon:CIconHandle);

GetCIcon creates a Color Icon using the ‘cicn’ resource of the given ID number (if it is there). It is important to note that a new Color Icon is created every time GetCIcon is called in a program. Unlike GetResource, which if called repeatedly with the same ID number, will return the same handle, GetCIcon uses the ‘cicn’ resource as a template in creating a complete new Color Icon (with new copies of the Color Lookup Table handle).

DisposCIcon will release from memory the bytes stored in a Color Icon structure. This includes the main handle as well as all secondary handles. It is important that this routine is never called unless the Color Icon has its own copy of the Color Lookup Table, since this routine disposes of this handle also.

The PlotCIcon will draw the Color Icon at the defined position. Notice that a rectangle, not a point, is defined. Similar to Copybits (which routine PlotCIcon actually calls), the image of the icon will be shrunk and/or expand in the vertical and/or horizontal position to fit the rectangle. Since the Color Icon has a mask, only the mask portion of the image is drawn.

Unfortunately, Apple did not provide a simple ROM routine to handle selecting the Color Icon. The following routine uses PlotCIcon to draw a Color Icon at the given point (since in most cases, Color Icons are not shrunk or expanded) in either unhilited or hilited mode:

{8}

 procedure DrawColorIcon (theIcon: CIconHandle;
 thePoint: Point;
 theFlag: Boolean);
 vamyRect: Rect;
 myHDim, myVDim, myBWRowBytes: INTEGER;
 myMaskBitMap: BitMap;
 myPort: GrafPtr;
 begin
 if theIcon <> nil then
 if theIcon^ <> nil then begin
 with theIcon^^.iconBMap, bounds do begin
 myHDim := right - left;
 myVDim := bottom - top;
 myBWRowBytes := rowbytes;
 end;
 with myRect do begin
 topleft := thePoint;
 right := left + myHDim;
 bottom := top + myVDim;
 end;

 PlotCIcon(myRect, theIcon);

 if theFlag then begin
 Hlock(Handle(theIcon));

 with myMaskBitMap do begin
 baseaddr := @theIcon^^.iconMaskData[0];
 rowbytes := myBWRowBytes;
 bounds := myRect;
 end;

 GetPort(myPort);

 BitClr(Ptr(HiliteMode), pHiliteBit);
 CopyBits(myMaskBitMap, myPort^.portBits,
 myRect, myRect, srcXor, nil);

 Hlock(Handle(theIcon));
 end;
 end;
 end;

Except for using a CIconHandle, the parameters and uses of this routine are identical to DrawIconMask.

A brief explanation of Color Hiliting is needed when examining this routine. In a black and white world, selecting (hiliting) an image usually involved inverting it. All black pixels turn white, and white pixels turn black. If the image has a mask, only the pixels in the mask are inverted. When Color Quickdraw was introduced, a color version of hiliting was also provided. When color objects are hilited, the pixels that are the background color (usually white) are changed with the hilite color (defined by the user in the Control Panel), while the pixels that start out using the hilite color become the background color. When an image becomes unhilited, the pixels are reversed. For example, if a black and white icon is hilited in a color environment where the hilite color is blue, all the white pixels in the icon become blue. These new blue pixels return to white when the icon is unhilited. Apple provides a very simple, but clever method to invoke this hilite mode. To hilite an item, a program first needs to make the following call:

 const
 HiliteMode = $938;
 pHiliteBit = 0;

 BitClr(Ptr(HiliteMode),pHiliteBit);

After this call is made, the routine needs to make an Invert ROM call (InvertRect, InvertRgn, InvertArc, etc.) or an srcXor draw mode call (Copybits, etc.). Doing this will invert the image using the Color Hiliting method described above. After the drawing, the HilitBit is reset, so future drawing is done in normal mode. Apple was clever in that the above line of code does exactly nothing in a black and white Quickdraw world. It is perfectly safe to include this bit of code in Black and White hilite routines (if you look, you will see it is in the black and white routine provided in this article). Thus ‘ICON’ and ‘ICN#’ will always show their hiliting correctly, regardless of whether the computer is color or not. The following art displays various icons hilited on different backgrounds using both the black and white method and the newer color method:

Figure 5

Now that a Color Icon can be drawn, a routine that can change the selection of an already drawn Color Icon is needed:

{9}

 procedure SelectColorIcon (theIcon: CIconHandle;
 thePoint: Point);
 var  myHDim, myVDim, myBWRowBytes: INTEGER;
 myMaskBitMap: BitMap;
 myPort: GrafPtr;
 begin
 if theIcon <> nil then
 if theIcon^ <> nil then begin
 Hlock(Handle(theIcon));

 with theIcon^^.iconBMap, bounds do begin
 myHDim := right - left;
 myVDim := bottom - top;
 myBWRowBytes := rowbytes;
 end;

 with myMaskBitMap, bounds do begin
 baseaddr := @theIcon^^.iconMaskData[0];
 rowbytes := myBWRowBytes;
 topleft := thePoint;
 right := left + myHDim;
 bottom := top + myVDim;
 end;

 GetPort(myPort);

 BitClr(Ptr(HiliteMode), pHiliteBit);
 CopyBits(myMaskBitMap, myPort^.portBits,
 myMaskBitMap.bounds, myMaskBitMap.bounds,
 srcXor, nil);

 Hlock(Handle(theIcon));
 end;
 end;

Also a routine to detect if a spot is inside the mask of the Color Icon is needed:

{10}

 function PtInColorIcon (theIcon: CIconHandle;
 thePoint, theSpot: Point): Boolean;
 var  myRect: Rect;
 myFlag: BOOLEAN;
 myPtr: Ptr;
 myHDim, myVDim, myBWRowBytes: INTEGER;
 begin
 myFlag := FALSE;

 if theIcon <> nil then
 if theIcon^ <> nil then begin
 Hlock(Handle(theIcon));

 with theIcon^^.iconBMap, bounds do begin
 myHDim := right - left;
 myVDim := bottom - top;
 myBWRowBytes := rowbytes;
 end;

 with myRect do begin
 topleft := thePoint;
 right := left + 32;
 bottom := top + 32;
 end;

 if PtInRect(theSpot, myRect) then begin
 Hlock(Handle(theIcon));
 theSpot.v := theSpot.v - thePoint.v;
 theSpot.h := theSpot.h - thePoint.h;
 myPtr := POINTER(ORD4(StripAddress
 (@theIcon^^.iconMaskData[0]))
 + (theSpot.v * myBWRowBytes));

 myFlag := BitTst(myPtr, theSpot.h);
 HUnlock(Handle(theIcon));
 end;

 Hlock(Handle(theIcon));
 end;

 PtInColorIcon := myFlag;
 end;

‘cicn’ Format Problems

Color Icons were introduced with the Macintosh II computer. As programmers made more and more use of color, several problems involving memory usage and the ‘cicn’ resource data structure became apparent. Color Icons are memory hogs; they are larger than they really need to be for most uses. First, in order to support icons of all sizes and pixel depths, the color icon data structure has to be elaborate (which translates to large). Too much memory is used to provide this flexibility, since the vast majority of all icons are 32 by 32 pixels in size. The one common exception to this rule is 16 by 16 icons (ex.: small view of Files from Finder), but even this common exception is not used often. As for pixel depths, 4 & 8 bit pixels are the only depths the vast majority of Color Icons use. A color icon that only used these sizes and pixel depths would not have the overhead that the ‘cicn’ resource contains.

The second memory usage problem is the Color LookUp Tables (CLUTs) of a Color Icon. Each Color Icon maintains it’s own CLUT. If a program uses 100 different Color Icons, each with the identical CLUT, the program still has to create 100 identical, but separate CLUTs (one for each icon). This situation is even worse when one realizes, in order to display the best looking icon, the Apple Standard 16 CLUT or Apple Standard 256 CLUT (list of colors used by a Standard 4 bit or 8 bit Apple Color Card) are used for almost all icons. There do not need to be so many copies of the same table, especially when this table is usually the same for all applications. Again, because the ‘cicn’ resource is so versatile, it uses up more memory than a simpler model might use.

The third memory wastage has to do with how many copies of a single Color Icon are in memory at a given time. Since each call to GetCIcon returns a newly created copy of a Color Icon (based on the ‘cicn’ resource template), it is possible that different portions of code in a program each have their own copy of the same icon. Unlike the GetResource call (used by programs that draw ‘ICN#’), there is no single call that returns the same Color Icon structure, whether or not that icon was already in memory. Also, once a Color Icon is in memory, there is no way to make the data structure purgeable (unloadable from memory in low-memory conditions). The ‘ICON’ and ‘ICN#’ resource can be made purgeable. The first time a black and white icon needs to be drawn, a GetResource call is made to load the icon into memory. After the drawing is done, the handle is not released, instead it is set to be purgeable. If enough free memory exists on the machine, the handle stays in memory. When the icon is about to be drawn again, a call to GetResource insures that the handle already in memory is used. Later when a low-memory condition occurs, the icon is unloaded by the operating system. When the program needs to draw them again, the GetResource call returns the handles whether or not the handles have been purged. The ‘cicn’ format can not duplicate this very efficient method of using memory. A Color Icon loaded into memory by a GetCIcon call is there until the application unloads it, regardless of whether the icon is needed at that moment or not.

One last problem with handling Color Icons as ‘cicn’ format has to do with trying to display an image at the image’s best. A single ‘cicn’ resource (with a single ID) has a given CLUT and a given size. If the icon is drawn at smaller than normal size (most cases, 32 by 32 image drawn as a smaller, 16 by 16 image), the image becomes compressed. It does not look as good as an icon designed to be 16 by 16. Likewise, an icon created with a 256 color palette does not look as good when it is drawn on a 16 color monitor. This problem can be solved by having duplicate ‘cicn’ resources, one for the same icon at different pixel depths and image sizes, but then more memory is used up. Much of the ‘cicn’ format for resources of the same image at different pixel depths is identical, especially the mask of the color icons which would be the same.

Icon Family

To help with these memory problems, Apple defined an alternative method of dealing with the Color Icon structure, the Icon Family. By only working with icons that are either 32 by 32 pixels or 16 by 16 pixels, and at either 1 bit (black and white), 4 bit (16 color) or 8 bit (256 color) modes, much of the redundant information inherent in the ‘cicn’ structure is eliminated.

An Icon family consists of up to 6 different resources that define one icon, at different sizes and pixel depths. The resource types used are ‘ICN#’ (the traditional 32 bit black and white icon with mask), ‘ics#’ (a new 16 by 16 pixel black and white icon with mask), ‘icl8’ (a new 8 bit 32 by 32 pixel image), ‘icl4’ (a new 4 bit 32 by 32 pixel image), ‘ics8’ (a new 8 bit 16 by 16 pixel image) and ‘ics4’ (a new 4 bit 16 by 16 pixel image).

The ‘ICN#’ resource has already been explained in this article; it defined a black and white, 32 by 32 pixel image. The new ‘ics#’ resource is laid out identically, except it manipulates 16 by 16 bit images. Since a 16 bit image requires 2 bytes per row, the data structure is 32 bytes per image. There are two images, the icon and the mask, for a total of 64 bytes. Thus the most common icon sizes have specific resource types to represent the same black and white image (32 by 32 or 16 by 16).

The other four new resources do similar functions for the color versions of the image. Each resource defines only the pixel data for each image. For example, the ‘ics4’ defines the pixel images for the 16 by 16 (small) 4 bit color icon. Each 4 consecutive bits of data represents one of the 16 available colors. Since 16 times 4 bits equals 8 bytes (64 bits), the rowbytes of this pixel image is 8. The entire handle should be 128 bytes. The other three icons are organized similarly.

Notice that these four icons only describe the pixels (pixel 1 is color 10, pixel 2 is color 7, and so on). There is no CLUT table to define what the RGB value is for each color. There must be an implied CLUT for the Icon Family. Fortunately since almost all icons are created using the Standard Apple CLUTs, this is not a problem. The routines provided below assume either the 4 bit or 8 bit Standard Apple CLUTS were used by the code to draw the the Icon Family. ResEdit, the most common tool to create icons, has an option to allow you to create Icon Families using the Standard Apple CLUTs.

We have the specific size, specific pixel depth, the color pixels, and a CLUT, the only other information that is necessary to define a color icon is the black and white version and themask portion of the color icon. That data is not far off though, in the black and white icon resource (‘ICN#’ or ‘ics#’). A color resource, along with the corresponding black and white resource is enough to define a color icon.

Thus, to draw a black and white icon in either 32 by 32 version or 16 by 16 version, you would need only the ‘ICN#’ resource or the ‘ics#’ respectively. To draw a color icon, you would need both the black and white resource, and the correct color resource. For example, if you wish to display a 16 by 16, 8 bit color icon, both the ‘ics#” and the ‘ics8’ resources are needed.

Apple did not provide new Quickdraw routines to draw using the Icon Family. However, given the Icon Family resources, it is fairly easy to on-the-fly create a color icon data structure. All the various values of the Color Icon data structure (bounds, rowbytes, pixel depths) can be set, and the actual image can be plugged in. Then a call to PlotCIcon can be made to do the actual drawing. If you would consider all the possible ways of drawing an icon family as a set, you could create the following type of declaration. This set is useful when describing which icon in an icon family you wish to draw.

 type IconStyle = (BW32, BW16, EightBit32, EightBit16, FourBit32, FourBit16);

The following routine draws an instance of an Icon Family (based on theIconStyle, theBWIcon and theColorIcon) at a given point (thePoint), possibly hilited (if theFlag is TRUE). If theIconStyle mode is black and white (BW32 or BW16), then theColorIcon can be set to NIL. The routine makes a few assumptions. First, the routine assumes theBWIcon and theColorIcon handles are the correct ones for the given theIconStyle mode. Second, it assumes the theIconStyle is the correct one for the current screen. Finally, the routine assumes that if a color mode is given, Color Quickdraw is available.

{11}

 procedure DrawIconFamily (theIconStyle: IconStyle;
 theBWIcon: Handle;
 theColorIcon: Handle;
 thePoint: Point;
 theFlag: BOOLEAN);
 var  myDim, myBWRowbytes, myBWSize: INTEGER;
 myRect: Rect;
 myIconBitMap, myMaskBitMap: BitMap;
 myPort: GrafPtr;
 myCLUT: CTabHandle;
 myColorIcon: CIconHandle;
 myPtr1, myPtr2: Ptr;
 begin
{Check to see if Handle exists}

 if theBWIcon <> nil then
 if theBWIcon^ <> nil then begin
 Hlock(theBWIcon);

Setup variables based on type of Icon}

 if theIconStyle in [bw16, Eightbit16, Fourbit16] then
 begin
 myBWRowbytes := 2;
 myDim := 16;
 end
 else begin
 myBWRowbytes := 4;
 myDim := 32;
 end;

{Setup Mask Bitmap}

 with myMaskBitMap do begin
 myBWSize := myBWRowbytes * myDim;
 baseaddr := Ptr(ord4(StripAddress(theBWIcon^))
 + myBWSize);
 rowbytes := myBWRowbytes;
 SetRect(bounds, 0, 0, myDim, myDim);
 end;

{Setup bounding rectangle}

 with myRect do begin
 topleft := thePoint;
 right := left + myDim;
 bottom := top + myDim;
 end;

{Grab current grafport (for bitmap}

 GetPort(myPort);

{Check type of mode}

 if theIconStyle in [bw16, bw32] then begin
{If mode is BW, set Icon bitmap}

 with myIconBitMap do begin
 baseaddr := StripAddress(theBWIcon^);
 rowbytes := myBWRowbytes;
 SetRect(bounds, 0, 0, myDim, myDim);
 end;

{White out Mask Bitmap}
 CopyBits(myMaskBitMap, myPort^.portBits, 
 myMaskBitMap.bounds, myRect, srcBic, nil);
{Set Icon bitmap}
 CopyBits(myIconBitMap, myPort^.portBits, 
 myIconBitMap.bounds, myRect, srcOr, nil);
 end
{If mode is color, make sure color}
{handle is not nil.}
 else if theColorIcon <> nil then begin
{Get CLUT.}

 if theIconStyle in [EightBit16, EightBit32] then
 myCLUT := GetCTable(8)
 else
 myCLUT := GetCTable(4);
 
 if myCLUT <> nil then begin

{Allocate memory for Color Icon}
 myColorIcon := CIconHandle(NewHandle(SIZEOF(CIcon)
  + myBWSize));
 
 if myColorIcon <> nil then begin
{Load in fields}
 with myColorIcon^^ do begin
 with iconPMap do begin
 baseAddr := nil;
 SetRect(bounds, 0, 0, myDim, myDim);
 case theIconStyle of
 EightBit16: begin
 pixelSize := 8;
 rowbytes := $8010;
 end;

 EightBit32: begin
 pixelSize := 8;
 rowbytes := $8020;
 end;

 FourBit16: begin
 pixelSize := 4;
 rowbytes := $8008;
 end;

 FourBit32: begin
 pixelSize := 4;
 rowbytes := $8010;
 end;
 otherwise
 end;
 pmVersion := 0;
 packType := 0;
 packSize := 0;
 hRes := 72;
 vRes := 72;
 pixelType := 0;
 cmpCount := 1;
 cmpSize := pixelSize;
 planebytes := 0;
 pmReserved := 0;
 end;
 with iconMask do begin
 rowbytes := myBWRowbytes;
 baseaddr := nil;
 SetRect(bounds, 0, 0, myDim, myDim);
 end;
 iconBMap := iconMask;
 end;

{Load in CLUT & pixel data}

 myColorIcon^^.iconData := theColorIcon;
 myColorIcon^^.iconPMap.pmTable := myCLUT;

{Copy in BW image and mask}

 myPtr1 := POINTER(ORD4(StripAddress(theBWIcon^))
 + myBWSize);
 myPtr2 := @myColorIcon^^.iconMaskData[0];
 BlockMove(myPtr1, myPtr2, myBWSize);
 myPtr1 := StripAddress(theBWIcon^);
 myPtr2 := POINTER(ORD4(StripAddress(
 @myColorIcon^^.iconMaskData[0])) + myBWSize);
 BlockMove(myPtr1, myPtr2, myBWSize);
{Plot Icon}
 PlotCIcon(myRect, myColorIcon);

 DisposHandle(Handle(myColorIcon));
 end;
 DisposCTable(myCLUT);
 end;
 end;
 end;
end;

The best approach might be for the application to check the pixel depth of the screen at startup. Then the application should load in the correct resources based on this information, and use the resources in calls to DrawIconFamily.

Hopefully the reader can see the advantage of using the Icon Family resources instead of the ‘cicn’ resource. Not only are the actual icons smaller in byte size, but an application can decide at startup which size and pixel depth it wants to use, and only load the Icon Family resources that are needed. The above mentioned method of keeping the resource purgeable for more efficient memory usage can be used with all the Icon Family resources.

To complete the discussion of Icon Families, routines are needed to handle changing the selection of an icon, and detecting if a position is on top of the icon. The following routines provide this. In both of the routines, regardless of whether the black and white mode, 4 bit mode, or 8 bit mode is used, only the black and white icon resource (to be exact, the mask portion) is needed:

{12}

 procedure SelectIconFamily (theIconStyle: IconStyle;
 theBWIcon: Handle;
 thePoint: Point);
 var  myDim, myBWRowbytes, myBWSize: INTEGER;
 myRect: Rect;
 myMaskBitMap: BitMap;
 myPort: GrafPtr;
 begin
{Check to see if Handle exists}

 if theBWIcon <> nil then
 if theBWIcon^ <> nil then begin

{Setup variables based on type of Icon}

 if theIconStyle in [bw16, Eightbit16, Fourbit16] then
 begin
 myBWRowbytes := 2;
 myDim := 16;
 end
 else begin
 myBWRowbytes := 4;
 myDim := 32;
 end;

{Setup Mask Bitmap}

 with myMaskBitMap do begin
 myBWSize := myBWRowbytes * myDim;
 baseaddr := Ptr(ord4(StripAddress(theBWIcon^))
 + myBWSize);
 rowbytes := myBWRowbytes;
 SetRect(bounds, 0, 0, myDim, myDim);
 end;

{Setup bounding rectangle}

 with myRect do begin
 topleft := thePoint;
 right := left + myDim;
 bottom := top + myDim;
 end;

{Grab current grafport (for bitmap}

 GetPort(myPort);

{Invert the mask bitmap}

 BitClr(Ptr(HiliteMode), pHiliteBit);
 CopyBits(myMaskBitMap, myPort^.portBits,
 myMaskBitMap.bounds, myRect, srcXor, nil);

 Hunlock(theBWIcon);
 end;
 end;

 function PtInIconFamily (theIconStyle: IconStyle;
 theBWIcon: Handle;
 thePoint, theSpot: Point): BOOLEAN;
 var  myDim, myBWRowbytes, myBWSize: INTEGER;
 myRect: Rect;
 myFlag: BOOLEAN;
 myPtr: Ptr;
 begin
 myFlag := FALSE;

{Check to see if Handle exists}

 if theBWIcon <> nil then
 if theBWIcon^ <> nil then begin
 Hlock(theBWIcon);

{Setup variables based on type of Icon}

 if theIconStyle in [bw16, Eightbit16, Fourbit16] then
 begin
 myBWRowbytes := 2;
 myDim := 16;
 end
 else begin
 myBWRowbytes := 4;
 myDim := 32;
 end;
 myBWSize := myBWRowbytes * myDim;

{Setup bounding rectangle}

 with myRect do begin
 topleft := thePoint;
 right := left + myDim;
 bottom := top + myDim;
 end;

{Check if spot in bounding rect}

 if PtInRect(theSpot, myRect) then begin
{If so, setup math to look at specific}
{row of pixels.}
 Hlock(theBWIcon);
 theSpot.v := theSpot.v - thePoint.v;
 theSpot.h := theSpot.h - thePoint.h;
 myPtr := POINTER(ORD4(StripAddress(theBWIcon^)) 
 + myBWSize + (theSpot.v * myBWRowbytes));

{Check if spot touching set pixel (bit)}

 myFlag := BitTst(myPtr, theSpot.h);
 HUnlock(theBWIcon);
 end;
 end;

 PtInIconFamily := myFlag;
 end;

As always, by far the best method to create Icon Families is to use ResEdit. ResEdit 2.1, in particular, has excellent tools to create the icons. ResEdit 2.1 also includes tools to bundle an Icon Family with a file (usually an Application) so that that Finder displays it’s own icon (color when running under new System 7.0).

The Icon Family was not officially introduced with a new version of the Macintosh or Color Quickdraw. It was actually rather poorly documented with the alpha versions of 7.0, but that version of 7.0 did not display the icons. Beta version of ResEdit (2.0b2 and above) allowed users to create these resources. Then various Color Desktop utilities started appearing. These utilities used the Icon Family of resources to display color icons from the Finder. Officially, System 7.0 may be the first Apple approved use of Icon Families. As of now, few programs use the Icon Family to draw color icons, but hopefully this will change as their advantages become apparent.

Example Code

The following program, IconTester, uses the IconFamily routines provided here to display 3 different icons. The code demonstrates drawing the various icons, as well as making them purgeable when they are not needed (providing more efficient use of memory). The program gives examples on how to handle mousedown events, to see if they are falling within an icons mask. If so, the icon becomes selected or unselected.

{IconTester.p  - Steve Sheets, 1991}
{Sample program to display Icon}
{Family resources.  This program will}
{display 3 different Icon Families in all}
{the various modes.  If any of the icons}
{are clicked, that icon will become hilited.}

program IconTester;
const
{Icon position constants}

 kRow1 = 50;
 kRow2 = 120;
 kRow3 = 190;

 kColumn1 = 10;
 kColumn2 = 60;
 kColumn3 = 110;
 kColumn4 = 160;
 kColumn5 = 210;
 kColumn6 = 260;
 kColumn7 = 310;
 kColumn8 = 360;

{resource ID of Icon Families}

 kApplicationIcon = 500;
 kCheckerDocIcon = 501;
 kMoofIcon = 502;

{Window Size Constants}

 kHSize = 300;

 kVColorSize = 230;
 kVBWSize = 90;

{Number of Selection Flags needed}

 kNumFlags = 18;

{Globals}
vargIconWindow: WindowPtr;
 gHaveColor, gDone: BOOLEAN;
 gFlags: array[1..kNumFlags] of BOOLEAN;

{Checks if Color Quickdraw Exists on this machine.}

function ColorQDExists: boolean;
const ROM85Loc = $28E;
 TwoHighMask = $C000;
type  WordPtr = ^INTEGER;
begin
 ColorQDExists := (BitAnd(WordPtr(ROM85Loc)^, TwoHighMask) 
 = 0);
end;

{Prepares the program by setting up variables}
{and creating a window (possibly Color window).}
{No icons are loaded here.}

procedure SetUp;
varmyRect: Rect;
 myStr: Str255;
 myCount: INTEGER;
begin
 gHaveColor := ColorQDExists;

 myStr := ‘Icon Family Demo’;
 with myRect, screenbits do begin
 top := 40;
 left := (bounds.right - bounds.left - kHSize) div 2;
 right := left + kHSize;
 end;
 if gHaveColor then begin
 myRect.bottom := myRect.top + kVColorSize;
 gIconWindow := NewCWindow(nil, myRect, myStr, TRUE,
 noGrowDocProc, POINTER(-1), TRUE, 0)
 end
 else begin
 myRect.bottom := myRect.top + kVBWSize;
 gIconWindow := NewWindow(nil, myRect, myStr, TRUE,
 noGrowDocProc, POINTER(-1), TRUE, 0);
 end;

 gDone := FALSE;
 for myCount := 1 to kNumFlags do
 gFlags[myCount] := FALSE;
 SetCursor(Arrow);
end;

{***INSERT ColorFamily Routines Here***}

{Updates the Icon image window.  Draws}
{some text information, then draws all}
{the icons.}

procedure UpdateIconWindow;

{Given an icon style, position, resource ID}
{number and selection flag number (number of}
{item in array), draws that icon.  It does this}
{by loading in the correct resources, and calling}
{DrawIconFamily.  When it is done, the resources}
{are set to unpurgeable (allow them to be freed}
{if memory space is an issue).}

 procedure DrawMyIcon (theIconStyle: IconStyle;
 theH, theV: INTEGER;
 theIconID: INTEGER;
 theFlagNum: INTEGER);
 var  myIconMask, myColorIcon: Handle;
 myFlag: BOOLEAN;
 myPoint: Point;
 begin
 if theIconStyle in [bw16, Eightbit16, Fourbit16]
 then myIconMask := GetResource(‘ics#’, theIconID)
 else myIconMask := GetResource(‘ICN#’, theIconID);
 if myIconMask <> nil then begin
 HNoPurge(myIconMask);

 myFlag := FALSE;
 myColorIcon := nil;
 case theIconStyle of
 Eightbit16: 
 myColorIcon := GetResource(‘ics8’, theIconID);

 Fourbit16: 
 myColorIcon := GetResource(‘ics4’, theIconID);

 Eightbit32: 
 myColorIcon := GetResource(‘icl8’, theIconID);

 Fourbit32: 
 myColorIcon := GetResource(‘icl4’, theIconID);
 otherwise
 myFlag := TRUE;
 end;

 if myColorIcon <> nil then begin
 HNoPurge(myColorIcon);
 myFlag := TRUE;
 end;

 if myFlag then begin
 myPoint.h := theH;
 myPoint.v := theV;
 DrawIconFamily(theIconStyle, myIconMask, myColorIcon,
 myPoint, gFlags[theFlagNum]);
 end;

 if myColorIcon <> nil
 then HPurge(myColorIcon);
 HPurge(myIconMask);
 end;
 end;

begin
 MoveTo(10, 20);
 DrawString(‘Select an Icon to hilite it.’);

 MoveTo(10, 40);
 DrawString(‘Black and White Icons:’);
 DrawMyIcon(bw32, kColumn1, kRow1, kApplicationIcon, 1);
 DrawMyIcon(bw16, kColumn2, kRow1, kApplicationIcon, 2);
 DrawMyIcon(bw32, kColumn3, kRow1, kCheckerDocIcon, 3);
 DrawMyIcon(bw16, kColumn4, kRow1, kCheckerDocIcon, 4);
 DrawMyIcon(bw32, kColumn5, kRow1, kMoofIcon, 5);
 DrawMyIcon(bw16, kColumn6, kRow1, kMoofIcon, 6);

 if gHaveColor then begin
 MoveTo(10, 110);
 DrawString(‘Four Bit Icons:’);
 DrawMyIcon(Fourbit32, kColumn1, kRow2,
 kApplicationIcon, 7);
 DrawMyIcon(Fourbit16, kColumn2, kRow2,
 kApplicationIcon, 8);
 DrawMyIcon(Fourbit32, kColumn3, kRow2,
 kCheckerDocIcon, 9);
 DrawMyIcon(Fourbit16, kColumn4, kRow2,
 kCheckerDocIcon, 10);
 DrawMyIcon(Fourbit32, kColumn5, kRow2, kMoofIcon, 11);
 DrawMyIcon(Fourbit16, kColumn6, kRow2, kMoofIcon, 12);

 MoveTo(10, 180);
 DrawString(‘Eight Bit Icons:’);
 DrawMyIcon(Eightbit32, kColumn1, kRow3,
 kApplicationIcon, 13);
 DrawMyIcon(Eightbit16, kColumn2, kRow3,
 kApplicationIcon, 14);
 DrawMyIcon(Eightbit32, kColumn3, kRow3,
 kCheckerDocIcon, 15);
 DrawMyIcon(Eightbit16, kColumn4, kRow3,
 kCheckerDocIcon, 16);
 DrawMyIcon(Eightbit32, kColumn5, kRow3, kMoofIcon, 17);
 DrawMyIcon(Eightbit16, kColumn6, kRow3, kMoofIcon, 18);
 end;
end;

{Handles a mousedown in the window}
{(local cooridinates).  Checks for each}
{of the icons if click was on top of it.}
{If so, it changes the hiliting of that icon}
{(and the flag associated with it).}

procedure ClickIconWindow (theSpot: Point);
{Given an iconstyle, position, resource ID and}
{flag number, check if theSpot is touching it}
{(using PtInIconFamily).  If so, flips the flag and changes}
{the hiliting (using SelectIconFamily).}

 procedure CheckMyIcon (theIconStyle: IconStyle;
 theH, theV: INTEGER;
 theIconID: INTEGER;
 theFlagNum: INTEGER);
 var  myIconMask: Handle;
 myPoint: Point;
 begin
 if theIconStyle in [bw16, Eightbit16, Fourbit16]
 then myIconMask := GetResource(‘ics#’, theIconID)
 else myIconMask := GetResource(‘ICN#’, theIconID);
 if myIconMask <> nil then begin
 HNoPurge(myIconMask);

 myPoint.h := theH;
 myPoint.v := theV;
 if PtInIconFamily(theIconStyle, myIconMask, myPoint,
 theSpot) then begin
 gFlags[theFlagNum] := not gFlags[theFlagNum];
 SelectIconFamily(theIconStyle, myIconMask, myPoint);
 end;

 HPurge(myIconMask);
 end;
 end;

begin
 CheckMyIcon(bw32, kColumn1, kRow1, kApplicationIcon, 1);
 CheckMyIcon(bw16, kColumn2, kRow1, kApplicationIcon, 2);
 CheckMyIcon(bw32, kColumn3, kRow1, kCheckerDocIcon, 3);
 CheckMyIcon(bw16, kColumn4, kRow1, kCheckerDocIcon, 4);
 CheckMyIcon(bw32, kColumn5, kRow1, kMoofIcon, 5);
 CheckMyIcon(bw16, kColumn6, kRow1, kMoofIcon, 6);

 if gHaveColor then begin
 CheckMyIcon(Fourbit32, kColumn1, kRow2,
 kApplicationIcon, 7);
 CheckMyIcon(Fourbit16, kColumn2, kRow2,
 kApplicationIcon, 8);
 CheckMyIcon(Fourbit32, kColumn3, kRow2,
 kCheckerDocIcon, 9);
 CheckMyIcon(Fourbit16, kColumn4, kRow2,
 kCheckerDocIcon, 10);
 CheckMyIcon(Fourbit32, kColumn5, kRow2, kMoofIcon, 11);
 CheckMyIcon(Fourbit16, kColumn6, kRow2, kMoofIcon, 12);

 CheckMyIcon(Eightbit32, kColumn1, kRow3,
 kApplicationIcon, 13);
 CheckMyIcon(Eightbit16, kColumn2, kRow3,
 kApplicationIcon, 14);
 CheckMyIcon(Eightbit32, kColumn3, kRow3,
 kCheckerDocIcon, 15);
 CheckMyIcon(Eightbit16, kColumn4, kRow3,
 kCheckerDocIcon, 16);
 CheckMyIcon(Eightbit32, kColumn5, kRow3,
 kMoofIcon, 17);
 CheckMyIcon(Eightbit16, kColumn6, kRow3,
 kMoofIcon, 18);
 end;
end;

{Main event loop of application.  Handles}
{all normal events, and passes updates &}
{mouse downs to UpdateIconWindow and}
{ClickIconWindow.  This routine loops until}
{user selects close box.}

procedure MainLoop;
varmyWindow: WindowPtr;
 myPort: GrafPtr;
 myEvent: EventRecord;
begin
 repeat
 SystemTask;

 if GetNextEvent(everyEvent, myEvent) then
 case myEvent.what of

 mouseDown: 
 case FindWindow(myEvent.where, myWindow) of
 inSysWindow: 
 SystemClick(myEvent, myWindow);
 inGoAway: 
 if myWindow = gIconWindow  then
 if TrackGoAway(myWindow, myEvent.where) 
 then gDone := TRUE;
 inDrag: 
 if myWindow <> frontWindow 
 then SelectWindow(myWindow)
 else DragWindow(myWindow, myEvent.where,
 screenbits.bounds);
 inContent: 
 if myWindow <> frontWindow  then
 SelectWindow(myWindow)
 else if myWindow = gIconWindow then begin
 GetPort(myPort);
 SetPort(myWindow);
 GlobalToLocal(myEvent.where);
 ClickIconWindow(myEvent.where);
 SetPort(myPort);
 end;
 end;

 updateEvt:  begin
 GetPort(myPort);
 myWindow := WindowPtr(myEvent.message);
 BeginUpdate(myWindow);
 SetPort(myWindow);
 if myWindow = gIconWindow then
 UpdateIconWindow;
 SetPort(myPort);
 end;

 otherwise
 end;
 until gDone;
end;

{Main program}

begin
 SetUp;
 MainLoop;
end.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

SketchUp 19.1.174 - Create 3D design con...
SketchUp is an easy-to-learn 3D modeling program that enables you to explore the world in 3D. With just a few simple tools, you can create 3D models of houses, sheds, decks, home additions,... Read more
ClamXav 3.0.12 - Virus checker based on...
ClamXav is a popular virus checker for OS X. Time to take control ClamXAV keeps threats at bay and puts you firmly in charge of your Mac’s security. Scan a specific file or your entire hard drive.... Read more
BetterTouchTool 3.151 - Customize multi-...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom)... Read more
FontExplorer X Pro 6.0.9 - Font manageme...
FontExplorer X Pro is optimized for professional use; it's the solution that gives you the power you need to manage all your fonts. Now you can more easily manage, activate and organize your... Read more
Dropbox 77.4.131 - Cloud backup and sync...
Dropbox is an application that creates a special Finder folder that automatically syncs online and between your computers. It allows you to both backup files and keeps them up-to-date between systems... Read more
DiskCatalogMaker 7.5.3 - Catalog your di...
DiskCatalogMaker is a simple disk management tool which catalogs disks. Simple, light-weight, and fast Finder-like intuitive look and feel Super-fast search algorithm Can compress catalog data for... Read more
Notion 1.0.7 - A unified workspace for m...
Notion is the unified workspace for modern teams. Notion Features: Integration with Slack Documents Wikis Tasks Note: This application contains in-app and/or external module purchases. Version 1.0... Read more
Microsoft Office 2016 16.16.12 - Popular...
Microsoft Office 2016 - Unmistakably Office, designed for Mac. The new versions of Word, Excel, PowerPoint, Outlook, and OneNote provide the best of both worlds for Mac users - the familiar Office... Read more
Little Snitch 4.4.2 - Alerts you about o...
Little Snitch gives you control over your private outgoing data. Track background activity As soon as your computer connects to the Internet, applications often have permission to send any... Read more
MainStage 3 3.4.3 - Live performance too...
Apple MainStage makes it easy to bring to the stage all the same instruments and effects that you love in your recording. Everything from the Sound Library and Smart Controls you're familiar with... Read more

Latest Forum Discussions

See All

Upcoming visual novel Arranged shines a...
If you’re in the market for a new type of visual novel designed to inform and make you think deeply about its subject matter, then Arranged by Kabuk Games could be exactly what you’re looking for. It’s a wholly unique take on marital traditions in... | Read more »
TEPPEN guide - The three best decks in T...
TEPPEN’s unique take on the collectible card game genre is exciting. It’s just over a week old, but that isn’t stopping lots of folks from speculating about the long-term viability of the game, as well as changes and additions that will happen over... | Read more »
Intergalactic puzzler Silly Memory serve...
Recently released matching puzzler Silly Memory is helping its fans with their intergalactic journeys this month with some very special offers on in-app purchases. In case you missed it, Silly Memory is the debut title of French based indie... | Read more »
TEPPEN guide - Tips and tricks for new p...
TEPPEN is a wild game that nobody asked for, but I’m sure glad it exists. Who would’ve thought that a CCG featuring Capcom characters could be so cool and weird? In case you’re not completely sure what TEPPEN is, make sure to check out our review... | Read more »
Dr. Mario World guide - Other games that...
We now live in a post-Dr. Mario World world, and I gotta say, things don’t feel too different. Nintendo continues to squirt out bad games on phones, causing all but the most stalwart fans of mobile games to question why they even bother... | Read more »
Strategy RPG Brown Dust introduces its b...
Epic turn-based RPG Brown Dust is set to turn 500 days old next week, and to celebrate, Neowiz has just unveiled its biggest and most exciting update yet, offering a host of new rewards, increased gacha rates, and a brand new feature that will... | Read more »
Dr. Mario World is yet another disappoin...
As soon as I booted up Dr. Mario World, I knew I wasn’t going to have fun with it. Nintendo’s record on phones thus far has been pretty spotty, with things trending downward as of late. [Read more] | Read more »
Retro Space Shooter P.3 is now available...
Shoot-em-ups tend to be a dime a dozen on the App Store, but every so often you come across one gem that aims to shake up the genre in a unique way. Developer Devjgame’s P.3 is the latest game seeking to do so this, working as a love letter to the... | Read more »
Void Tyrant guide - Guildins guide
I’ve still been putting a lot of time into Void Tyrant since it officially released last week, and it’s surprising how much stuff there is to uncover in such a simple-looking game. Just toray, I finished spending my Guildins on all available... | Read more »
Tactical RPG Brown Dust celebrates the s...
Neowiz is set to celebrate the summer by launching a 2-month long festival in its smash-hit RPG Brown Dust. The event kicks off today, and it’s divided into 4 parts, each of which will last two weeks. Brown Dust is all about collecting, upgrading,... | Read more »

Price Scanner via MacPrices.net

Clearance 12″ 1.2GHz MacBook on sale for $899...
Focus Camera has clearance 12″ 1.2GHz Space Gray MacBooks available for $899.99 shipped. That’s $400 off Apple’s original MSRP. Focus charges sales tax for NY & NJ residents only. Read more
Get a new 2019 13″ 2.4GHz 4-Core MacBook Pro...
B&H Photo has new 2019 13″ 2.4GHz MacBook Pros on sale for up to $150 off Apple’s MSRP. Overnight shipping is free to many addresses in the US: – 2019 13″ 2.4GHz/256GB 6-Core MacBook Pro Silver... Read more
AirPods with Wireless Charging Case now on sa...
Amazon has extended their Prime Day savings on Apple AirPods by offering AirPods with the Wireless Charging case for $169.99. That’s $30 off Apple’s MSRP, and it’s the cheapest price available for... Read more
New 2019 15″ MacBook Pros on sale for $200 of...
B&H Photo has the new 2019 15″ 6-Core and 8-Core MacBook Pros on sale for $200 off Apple’s MSRP. Overnight shipping is free to many addresses in the US: – 2019 15″ 2.6GHz 6-Core MacBook Pro Space... Read more
Amazon drops prices, now offers clearance 13″...
Amazon has new dropped prices on clearance 13″ 2.3GHz Dual-Core non-Touch Bar MacBook Pros by $200 off Apple’s original MSRP, with prices now available starting at $1099. Shipping is free. Be sure to... Read more
2018 15″ MacBook Pros now on sale for $500 of...
Amazon has dropped prices on select clearance 2018 15″ 6-Core MacBook Pros to $500 off Apple’s original MSRP. Prices now start at $1899 shipped: – 2018 15″ 2.2GHz Touch Bar MacBook Pro Silver: $1899.... Read more
Price drop! Clearance 12″ 1.2GHz Silver MacBo...
Amazon has dropped their price on the recently-discontinued 12″ 1.2GHz Silver MacBook to $849.99 shipped. That’s $450 off Apple’s original MSRP for this model, and it’s the cheapest price available... Read more
Apple’s 21″ 3.0GHz 4K iMac drops to only $936...
Abt Electronics has dropped their price on clearance, previous-generation 21″ 3.0GHz 4K iMacs to only $936 shipped. That’s $363 off Apple’s original MSRP, and it’s the cheapest price we’ve seen so... Read more
Amazon’s Prime Day savings on Apple 11″ iPad...
Amazon has new 2018 Apple 11″ iPad Pros in stock today and on sale for up to $250 off Apple’s MSRP as part of their Prime Day sale (but Prime membership is NOT required for these savings). These are... Read more
Prime Day Apple iPhone deal: $100 off all iPh...
Boost Mobile is offering Apple’s new 2018 iPhone Xr, iPhone Xs, and Xs Max for $100 off MSRP. Their discount reduces the cost of an Xs to $899 for the 64GB models and $999 for the 64GB Xs Max. Price... Read more

Jobs Board

*Apple* IOS Systems Engineer - Randstad (Uni...
Apple IOS Systems Engineer **job details:** + location:Irvine, CA + salary:$45 - $55 per hour + date posted:Tuesday, July 16, 2019 + job type:Temp to Perm + Read more
Business Development Manager, *Apple* Globa...
Business Development Manager, Apple Global Tampa, FL, US Requisition Number:73805 As a Global Apple Business Development Manager at Insight, you proactively Read more
*Apple* Systems Architect/Engineer, Vice Pre...
…its vision to be the world's most trusted financial group. **Summary:** Apple Systems Architect/Engineer with strong knowledge of products and services related to Read more
*Apple* Graders/Inspectors (Seasonal/Hourly/...
…requirements. #COVAentryleveljobs ## Minimum Qualifications Some knowledge of agricultural and/or the apple industry is helpful as well as the ability to comprehend, Read more
Best Buy *Apple* Computing Master - Best Bu...
**710003BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Location Number:** 000171-Winchester Road-Store **Job Description:** Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.