Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Imaging With QuickDraw /
Chapter 4 - Color QuickDraw


Using Color QuickDraw

To use Color QuickDraw, you generally

This section gives an overview of routines that your application typically calls while using Color QuickDraw. Before calling these routines, however, your application should test for the existence of Color QuickDraw by using the Gestalt function with the gestaltQuickDrawVersion selector. The Gestalt function returns a 4-byte value in its response parameter; the low-order word contains QuickDraw version data. In that low-order word, the high-order byte gives the major revision number and the low-order byte gives the minor revision number. If the value returned in the response parameter is equal to the value of the constant gestalt32BitQD13, then the system supports the System 7 version of Color QuickDraw. Listed here are the various constants, and the values they represent, that indicate earlier versions of Color QuickDraw.

CONST
   gestalt8BitQD     = $100;  {8-bit Color QD}
   gestalt32BitQD    = $200;  {32-bit Color QD}
   gestalt32BitQD11  = $210;  {32-bit Color QDv1.1}
   gestalt32BitQD12  = $220;  {32-bit Color QDv1.2}
   gestalt32BitQD13  = $230;  {System 7: 32-bit Color QDv1.3}
Your application can also use the Gestalt function with the selector gestaltQuickDrawFeatures to determine whether the user's system supports various Color QuickDraw features. If the bits indicated by the following constants are set in the response parameter, then the features are available:

CONST
   gestaltHasColor         = 0;  {Color QuickDraw is present}
   gestaltHasDeepGWorlds   = 1;  {GWorlds deeper than 1 bit}
   gestaltHasDirectPixMaps = 2;  {PixMaps can be direct--16 or }
                                 { 32 bit}
   gestaltHasGrayishTextOr = 3;  {supports text mode }
                                 { grayishTextOr}
When testing for the existence of Color QuickDraw, your application should test the response to the gestaltQuickDrawVersion selector (rather than test for the result gestaltHasColor, which is unreliable, from the gestaltQuickDrawFeatures selector). The support for offscreen graphics worlds indicated by the gestaltHasDeepGWorlds response to the gestaltQuickDrawVersion selector is described in the chapter "Offscreen Graphics Worlds." The support for the text mode indicated by the gestaltHasGrayishTextOr response is described in the chapter "QuickDraw Text" in Inside Macintosh: Text. For more information about the Gestalt function, see the chapter "Gestalt Manager" in Inside Macintosh: Operating System Utilities.

Initializing Color QuickDraw

To initialize Color QuickDraw, use the InitGraf procedure, described in the chapter "Basic QuickDraw." Besides initializing basic QuickDraw, this procedure initializes Color QuickDraw on computers that support it.

In addition to InitGraf, all other basic QuickDraw routines work with Color QuickDraw. For example, you can use the GetPort procedure to save the current color graphics port, and you can use the CopyBits procedure to copy an image between two different color graphics ports. See the chapters "Basic QuickDraw" and "QuickDraw Drawing" for descriptions of additional routines that you can use with Color QuickDraw.

Creating Color Graphics Ports

All graphics operations are performed in graphics ports. Before a color graphics port can be used, it must be allocated with the OpenCPort procedure and initialized with the InitCPort procedure. Normally, your application does not call these procedures directly. Instead, your application creates a color graphics port by using the GetNewCWindow or NewCWindow function (described in the chapter "Window Manager" in Inside Macintosh: Macintosh Toolbox Essentials) or the NewGWorld function (described in the chapter "Offscreen Graphics Worlds" in this book). These functions automatically call OpenCPort, which in turn calls InitCPort.

Listing 4-1 shows a simplified application-defined procedure called DoNew that uses the Window Manager function GetNewCWindow to create a color graphics port.

Listing 4-1 Using the Window Manager to create a color graphics port

PROCEDURE DoNew (VAR window: WindowPtr);
VAR
   windStorage:   Ptr;  {memory for window record}
BEGIN
   window := NIL;
   {allocate memory for window record from previously allocated block}
   windStorage := MyPtrAllocationProc;
   IF windStorage <> NIL THEN {memory allocation succeeded}
   BEGIN
      IF gColorQDAvailable THEN {use Gestalt to determine color availability}
         window := GetNewCWindow(rDocWindow, windStorage, WindowPtr(-1))
      ELSE        {create a basic graphics port for a black-and-white screen}
         window := GetNewWindow(rDocWindow, windStorage, WindowPtr(-1));
   END;
   IF (window <> NIL) THEN
       SetPort(window);
END;
You can use GetNewCWindow to create color graphics ports whether or not a color monitor is currently installed. So that most of your window-handling code can handle color windows and black-and-white windows identically, GetNewCWindow returns a pointer of type WindowPtr (not of type CWindowPtr).

A window pointer points to a window record (WindowRecord), which contains a GrafPort record. If you need to check the fields of the color graphics port associated with a window, you can coerce the pointer to the GrafPort record into a pointer to a CGrafPort record.

You can allow GetNewCWindow to allocate the memory for your window record and its associated basic graphics port. You can maintain more control over memory use, however, by allocating the memory yourself from a block allocated for such purposes during your own initialization routine, and then passing the pointer to GetNewWindow, as shown in Listing 4-1.

To dispose of a color graphics port when you are finished using a color window, you normally use the DisposeWindow procedure (if you let the Window Manager allocate memory for the window) or the CloseWindow procedure (if you allocated memory for the window). If you use the CloseWindow procedure, you also dispose of the window record containing the graphics port by calling the Memory Manager procedure DisposePtr. You use the DisposeGWorld procedure when you are finished with a color graphics port for an offscreen graphics world.

Drawing With Different Foreground Colors

You can set the foreground and background colors using either Color QuickDraw or Palette Manager routines. If your application uses the Palette Manager, it should set the foreground and background colors with the PmForeColor and PmBackColor routines, as described in the chapter "Palette Manager" in Inside Macintosh: Advanced Color Imaging. Otherwise, your application can use the RGBForeColor procedure to set the foreground color, and it can use the RGBBackColor procedure to set the background color. Both of these Color QuickDraw procedures also operate for basic graphics ports created in System 7. (To set the foreground and background colors for basic graphics ports on older versions of system software, use the ForeColor and BackColor procedures described in the chapter "QuickDraw Drawing.")

The RGBForeColor procedure lets you set the foreground color to the best color available on the current graphics device. This changes the color of the "ink" used for drawing. All of the line-drawing, framing, and painting routines described in the chapter "QuickDraw Drawing" (such as LineTo, FrameRect, and PaintPoly) draw with the foreground color that you specify with RGBForeColor.

Note
Because a pixel pattern already contains color, Color QuickDraw ignores the foreground and background colors when your application draws with a pixel pattern. As described in "Drawing With Pixel Patterns" beginning on page 4-19, you can draw with a pixel pattern by using the PenPixPat procedure to assign a pixel pattern to the graphics pen, by using the BackPixPat procedure to assign a pixel pattern as the background pattern for the current color graphics port, and by using the FillCRect, FillCOval, FillCRoundRect, FillCArc, FillCRgn, and FillCPoly procedures to fill shapes with a pixel pattern.
To specify a foreground color, create an RGBColor record. Listing 4-2 defines two RGBColor records. The first is declared as myDarkBlue, and it's defined with a medium-intensive blue component and with zero-intensity red and green components. The second is declared as myMediumGreen, and it's defined with an intensive green component, a mildly intensive red component, and a very slight blue component.

Listing 4-2 Changing the foreground color

PROCEDURE MyPaintAndFillColorRects;
VAR
   firstRect, secondRect:  Rect;
   myDarkBlue:             RGBColor;
   myMediumGreen:          RGBColor;
BEGIN
      {create dark blue color}
   myDarkBlue.red := $0000;
   myDarkBlue.green := $0000;
   myDarkBlue.blue := $9999;
      {create medium green color}
   myMediumGreen.red := $3206;
   myMediumGreen.green := $9038;
   myMediumGreen.blue := $013D;
   RGBForeColor(myDarkBlue);  {draw with dark blue pen}
   PenMode(patCopy);
   SetRect(firstRect, 20, 20, 70, 70);
   PaintRect(firstRect);         {paint a dark blue rectangle}
   RGBForeColor(myMediumGreen);  {draw with a medium green pen}
   SetRect(secondRect, 90, 20, 140, 70);
   FillRect(secondRect, ltGray); {paint a medium green rectangle}
END;
In Listing 4-2, the RGBColor record myDarkBlue is supplied to the RGBForeColor procedure. The RGBForeColor procedure supplies the rgbFgColor field of the CGrafPort record with this RGBColor record, and it places the closest-matching available color in the fgColor field; the color in the fgColor field is the color actually used as the foreground color.

After using SetRect to create a rectangle, Listing 4-2 calls PaintRect to paint the rectangle. By default, the foreground color is black; by changing the foreground color to dark blue, every pixel that would normally be painted in black is instead painted in dark blue.

Listing 4-2 then changes the foreground color again to the medium green specified in the RGBColor record myMediumGreen. After creating another rectangle, this listing calls FillRect to fill the rectangle with the bit pattern specified by the global variable ltGray. As explained in the chapter "QuickDraw Drawing," this bit pattern consists of widely spaced black pixels that create the effect of gray on black-and-white screens. However, by changing the foreground color, every pixel in the pattern that would normally be painted black is instead drawn in medium green.

The effects of Listing 4-2 are illustrated in the grayscale screen capture shown in
Figure 4-9.

Figure 4-9 Drawing with two different foreground colors (on a grayscale screen)


If you wish to draw with a color other than the foreground color, you can use the PenPixPat procedure to give the graphics pen a colored pixel pattern that you define, and you can use the FillCRect, FillCRoundRect, FillCOval, FillCArc, FillCPoly, and FillCRgn procedures to fill shapes with colored patterns. The use of these procedures is illustrated in the next section.

Drawing With Pixel Patterns

Using pixel pattern resources, you can create multicolored patterns for the pen pattern, for the background pattern, and for fill patterns.

To set the pixel pattern to be used by the graphics pen in the current color graphics port, you use the PenPixPat procedure. To assign a pixel pattern as the background pattern, you use the BackPixPat procedure; this causes the ScrollRect procedure and the shape-erasing procedures (for example, EraseRect) to fill the background with your pixel pattern. To fill shapes with a pixel pattern, you use the FillCRect, FillCRoundRect, FillCOval, FillCArc, FillCPoly, and FillCRgn procedures.

Note
Because a pixel pattern already contains color, Color QuickDraw ignores the foreground and background colors when your application uses these routines to draw with a pixel pattern. Color QuickDraw also ignores the pen mode by drawing the pixel pattern directly onto the pixel image.
When you use the PenPat or BackPat procedure in a color graphics port, Color QuickDraw constructs a pixel pattern equivalent to the bit pattern you specify to PenPat or BackPat. The pen pattern or background pattern you thereby specify always uses the graphics port's current foreground and background colors. The PenPat and BackPat procedures are described in the chapter "QuickDraw Drawing."

A pixel pattern resource is a resource of type 'ppat'. You typically use a high-level tool such as the ResEdit application, available through APDA, to create 'ppat' resources. Figure 4-10 illustrates a ResEdit window displaying an application's 'ppat' resource with resource ID 128.

Figure 4-10 Using ResEdit to create a pixel pattern resource


As shown in this figure, you should also define an analogous, black-and-white bit pattern (described in the chapter "QuickDraw Drawing") to be used when this pattern is drawn into a basic graphics port. This bit pattern is stored within the pixel pattern resource.

After using ResEdit to define a pixel pattern, you can then use the DeRez decompiler to convert your 'ppat' resources into Rez input when necessary. (The DeRez resource decompiler and the Rez resource compiler are part of Macintosh Programmer's Workshop [MPW], which is available through APDA.) Listing 4-3 shows the Rez input created from the 'ppat' resource created in Figure 4-10.

Listing 4-3 Rez input for a pixel pattern resource

resource 'ppat' (128) {
   $"0001 0000 001C 0000 004E 0000 0000 FFFF"
   $"0000 0000 8292 1082 9210 8292 0000 0000"
   $"8002 0000 0000 0008 0008 0000 0000 0000"
   $"0000 0048 0000 0048 0000 0000 0002 0001"
   $"0002 0000 0000 0000 005E 0000 0000 1212"
   $"4848 1212 4848 1212 4848 1212 4848 0000"
   $"0000 0000 0002 0000 AAAA AAAA AAAA 0001"
   $"2222 2222 2222 0002 7777 7777 7777"
};
To retrieve the pixel pattern stored in a 'ppat' resource, you can use the GetPixPat function. Listing 4-4 uses GetPixPat to retrieve the 'ppat' resource created in
Listing 4-3. To assign this pixel pattern to the graphics pen, Listing 4-4 uses the PenPixPat procedure.

Listing 4-4 Using pixel patterns to paint and fill

PROCEDURE MyPaintPixelPatternRects;
VAR
   firstRect, secondRect:        Rect;
   myPenPattern, myFillPattern:  PixPatHandle;
BEGIN
   myPenPattern := GetPixPat(128);  {get a pixel pattern}
   PenPixPat(myPenPattern);      {assign the pattern to the pen}
   SetRect(firstRect, 20, 20, 70, 70);
   PaintRect(firstRect);      {paint with the pen's pixel pattern}
   DisposePixPat(myPenPattern);  {dispose of the pixel pattern}
   myFillPattern := GetPixPat(129);    {get another pixel pattern}
   SetRect(secondRect, 90, 20, 140, 70);
   FillCRect(secondRect, myFillPattern);  {fill with this pattern}
   DisposePixPat(myFillPattern); {dispose of the pixel pattern}
END;
Listing 4-4 uses the PaintRect procedure to draw a rectangle. The rectangle on the left side of Figure 4-11 illustrates the effect of painting a rectangle with the previously defined pen pattern.

Figure 4-11 Painting and filling rectangles with pixel patterns


The rectangle on the right side of Figure 4-11 illustrates the effect of using the FillCRect procedure to fill a rectangle with another previously defined pen pattern. The GetPixPat function is used to retrieve the pixel pattern defined in the 'ppat' resource with resource ID 129. This pixel pattern is then specified to the FillCRect procedure.

Copying Pixels Between Color Graphics Ports

As explained in the chapter "QuickDraw Drawing," QuickDraw has three primary image-processing routines.

In basic QuickDraw, CopyBits, CopyMask, and CopyDeepMask copy bit images between two basic graphics ports. In Color QuickDraw, you can also use these procedures to copy pixel images between two color graphics ports. Detailed routine descriptions for these procedures appear in the chapter "QuickDraw Drawing." This section provides an overview of how to use the extra capabilities that Color QuickDraw provides for these procedures.

When using CopyBits, CopyMask, and CopyDeepMask to copy images between color graphics ports, you must coerce each port's CGrafPtr data type to a GrafPtr data type, dereference the portBits fields of each, and then pass these "bitmaps" in the srcBits and dstBits parameters. If your application copies a pixel image from a color graphics port called MyColorPort, in the srcBits parameter you could specify GrafPtr(MyColorPort)^.portBits. In a CGrafPort record, the high 2 bits of the portVersion field are set. This field, which shares the same position in a CGrafPort record as the portBits.rowBytes field in a GrafPort record, indicates to these routines that you have passed it a handle to a pixel map rather than a bitmap.

Color QuickDraw's processing sequence of the CopyBits procedure is illustrated in Figure 4-12. Listing 6-1 in the chapter "Offscreen Graphics Worlds" illustrates how to use CopyBits to transfer an image prepared in an offscreen graphics world to an onscreen color graphics port.

Figure 4-12 Copying pixel images with the CopyBits procedure


With the CopyMask procedure, you can supply a pixel map to act as a copying mask. The values of pixels in the mask act as weights that proportionally select between source and destination pixel values. The process is shown in Figure 4-13, and an example of the effect can be seen in Plate 3 Plate 3 at the front of this book. Listing 6-2 in the chapter "Offscreen Graphics Worlds" illustrates how to use CopyMask to mask and copy an image prepared in an offscreen graphics world to an onscreen color graphics port.

Figure 4-13 Copying pixel images with the CopyMask procedure


The CopyDeepMask procedure combines the capabilities of the CopyBits and CopyMask procedures. With CopyDeepMask you can specify a pixel map mask, a transfer mode, and a mask region, as shown in Figure 4-14.

Figure 4-14 Copying pixel images with the CopyDeepMask procedure


On indexed devices, pixel images are always copied using the color table of the source PixMap record for source color information, and using the color table of the current GDevice record for destination color information. The color table attached to the destination PixMap record is ignored. As explained in the chapter "Offscreen Graphics Worlds," if you need to copy to an offscreen PixMap record with characteristics differing from those of the current graphics device, you should create an appropriate offscreen GDevice record and set it as the current graphics device before the copy operation.

When the PixMap record for the mask is 1 bit deep, it has the same effect as a bitmap mask: a black bit in the mask means that the destination pixel is to take the color of the source pixel; a white bit in the mask means that the destination pixel is to retain its current color. When masks have PixMap records with greater pixel depths than 1, Color QuickDraw takes a weighted average between the colors of the source and destination PixMap records. Within each pixel, the calculation is done in RGB color, on a color component basis. A gray PixMap record mask, for example, works like blend mode in a CopyBits procedure. A red mask (that is, one with high values for the red components of all pixels) filters out red values coming from the source pixel image.

Boolean Transfer Modes With Color Pixels

As described in the chapter "QuickDraw Drawing," QuickDraw offers two types of Boolean transfer modes: pattern modes for drawing lines and shapes, and source modes for copying images or drawing text. In basic graphics ports and in color graphics ports with 1-bit pixel maps, these modes describe the interaction between the bits your application draws and the bits that are already in the destination bitmap or 1-bit pixel map. These interactions involve turning the bits on or off--that is, making the pixels black or white.

The Boolean operations on bitmaps and 1-bit pixel maps are described in the chapter "QuickDraw Drawing." When you draw or copy images to and from bitmaps or 1-bit pixel maps, Color QuickDraw behaves in the manner described in that chapter.

When you use pattern modes in pixel maps with depths greater than 1 bit, Color QuickDraw uses the foreground color and background color when transferring bit patterns; for example, the patCopy mode applies the foreground color to every destination pixel that corresponds to a black pixel in a bit pattern, and it applies the background color to every destination pixel that corresponds to a white pixel in a bit pattern. See the description of the PenMode procedure in the chapter "QuickDraw Drawing" for a list that summarizes how the foreground and background colors are applied with pattern modes.

When you use the CopyBits, CopyMask, and CopyDeepMask procedures to transfer images between pixel maps with depths greater than 1 bit, Color QuickDraw performs the Boolean transfer operations in the manner summarized in Table 4-1. In general, with pixel images you will probably want to use the srcCopy mode or one of the arithmetic transfer modes described in "Arithmetic Transfer Modes" beginning on page 4-29.
Table 4-1 Boolean source modes with colored pixels (Continued)
Source modeAction on destination pixel
 If source pixel is blackIf source pixel is whiteIf source pixel is any other color
srcCopyApply foreground colorApply background colorApply weighted portions of foreground and background colors
notSrcCopyApply background colorApply foreground colorApply weighted portions of background and foreground colors
srcOrApply foreground colorLeave aloneApply weighted portions of foreground color
notSrcOrLeave aloneApply foreground colorApply weighted portions of foreground color
srcXorInvert (undefined for colored destination pixel)Leave aloneLeave alone
notSrcXorLeave aloneInvert (undefined for colored destination pixel)Leave alone
srcBicApply background colorLeave aloneApply weighted portions of background color
notSrcBicLeave aloneApply background colorApply weighted portions of background color

Note
When your application draws with a pixel pattern, Color QuickDraw ignores the pattern mode and simply transfers the pattern directly to the pixel map without regard to the foreground and background colors.
When you use the srcCopy mode to transfer a pixel into a pixel map, Color QuickDraw determines how close the color of that pixel is to black, and then assigns this relative amount of foreground color to the destination pixel. Color QuickDraw also determines how close the color of that pixel is to white, and assigns this relative amount of background color to the destination pixel.

To accomplish this, Color QuickDraw first multiplies the relative intensity of each red, green, and blue component of the source pixel by the corresponding value of the red, green, or blue component of the foreground color. It then multiplies the relative intensity of each red, green, and blue component of the source pixel by the corresponding value of the red, green, or blue component of the background color. For each component, Color QuickDraw adds the results and then assigns the new result as the value for the destination pixel's corresponding component.

For example, the pixel in an image might be all red: that is, its red component has a pixel value of $FFFF, and its green and blue components each have pixel values of $0000. The current foreground color might be black (that is, with pixel values of $0000, $0000, $0000 for its components) and its background color might be all white (that is, with pixel values of $FFFF, $FFFF, $FFFF). When that image is copied using the CopyBits procedure and the srcCopy source mode, CopyBits determines that the red component of the source pixel has 100 percent intensity; multiplying this by the intensity of the red component ($0000) of the foreground color produces a value of $0000, and multiplying this by the intensity of the red component ($FFFF) of the background color produces a value of $FFFF. Adding the results of these two operations produces a pixel value of $FFFF for the red component of the destination pixel. Performing similar operations on the green and blue components of the source pixel produces green and blue pixel values of $0000 for the destination pixel. In this way, CopyBits renders the source's all-red pixel as an all-red pixel in the destination pixel map. A source pixel with only 50 percent intensity for its red component and no intensity for its blue and green components would similarly be drawn as a medium red pixel in the destination pixel map.

Color QuickDraw produces similarly weighted destination colors when you use the other Boolean source modes. When you use the srcBic mode to transfer a colored source pixel into a pixel map, for example, CopyBits determines how close the color of that pixel is to black, and then assigns a relative amount of background color to the destination pixel. For this mode, CopyBits does not determine how close the color of the source pixel is to white.

Because Color QuickDraw uses the foreground and background colors instead of black and white when performing its Boolean source operations, the following effects are produced:

As you can see, applying a foreground color other than black or a background color other than white to the pixel can produce an unexpected result. For consistent results, set the foreground color to black and the background color to white before using CopyBits, CopyMask, or CopyDeepMask.

However, by using the RGBForeColor and RGBBackColor procedures to set the foreground and background colors to something other than black and white before using CopyBits, CopyMask, or CopyDeepMask, you can achieve some interesting coloration effects. Plate 2 at the front of this book shows how setting the foreground color to red and the background color to blue and then using the CopyBits procedure turns a grayscale image into shades of red and blue. Listing 4-5 shows the code that produced these two pixel maps.

Listing 4-5 Using CopyBits to produce coloration effects

PROCEDURE MyColorRamp;
VAR
   origPort:            CGrafPtr;
   origDevice:          GDHandle;
   myErr:               QDErr;
   myOffScreenWorld:    GWorldPtr;
   TheColor:            RGBColor;
   i:                   Integer;
   offPixMapHandle:     PixMapHandle;
   good:                Boolean;
   myRect:              Rect;
BEGIN
   GetGWorld(origPort, origDevice);    {save onscreen graphics port}
                                 {create offscreen graphics world}
   myErr := NewGWorld(myOffScreenWorld, 
                      0, origPort^.portRect, NIL, NIL, []);
   IF (myOffScreenWorld = NIL) OR (myErr <> noErr) THEN
      ; {handle errors here}
   SetGWorld(myOffScreenWorld, NIL); {set current graphics port to offscreen}
   offPixMapHandle := GetGWorldPixMap(myOffScreenWorld);
   good := LockPixels(offPixMapHandle);   {lock offscreen pixel map}
   IF NOT good THEN
      ; {handle errors here}
   EraseRect(myOffScreenWorld^.portRect); {initialize offscreen pixel map}
   FOR i := 0 TO 9 DO
   BEGIN                                  {create gray ramp}
      theColor.red := i * 7168;
      theColor.green := i * 7168;
      theColor.blue := i * 7168;
      RGBForeColor(theColor);
      SetRect(myRect, myOffScreenWorld^.portRect.left, i * 10,
              myOffScreenWorld^.portRect.right, i * 10 + 10);
      PaintRect(myRect);      {fill offscreen pixel map with gray ramp}
   END;
   SetGWorld(origPort, origDevice);    {restore onscreen graphics port}
   theColor.red := $0000;
   theColor.green := $0000;
   theColor.blue := $FFFF;
   RGBForeColor(theColor);    {make foreground color all blue}
   theColor.red := $FFFF;
   theColor.green := $0000;
   theColor.blue := $0000;
   RGBBackColor(theColor);    {make background color all red}
      {using blue foreground and red background colors, transfer "gray" }
      { ramp to onscreen graphics port}
   CopyBits(GrafPtr(myOffScreenWorld)^.portBits,   {gray ramp is source}
            GrafPtr(origPort)^.portBits,           {window is destination}
            myOffScreenWorld^.portRect, origPort^.portRect, srcCopy, NIL);
   UnlockPixels(offPixMapHandle);
   DisposeGWorld(myOffScreenWorld);
END;
Listing 4-5 uses the NewGWorld function, described in the chapter "Offscreen Graphics Worlds," to create an offscreen pixel map. The sample code draws a gray ramp into the offscreen pixel map, which is illustrated on the left side of Plate 2 at the front of this book. Then Listing 4-5 creates an all-blue foreground color and an all-red background color. This sample code then uses the CopyBits procedure to transfer the pixels in the offscreen pixel map to the onscreen window, which is shown on the right side of Plate 2.

Here are some hints for using foreground and background colors and the srcCopy source mode to color a pixel image:

To help make color work well on different screen depths, Color QuickDraw does some validity checking of the foreground and background colors. If your application is drawing to a color graphics port with a pixel depth equal to 1 or 2, and if the foreground and background colors aren't the same but both of them map to the same pixel value, then the foreground color is inverted. This ensures that, for instance, a red image drawn on a green background doesn't map to black on black.

On indexed devices, these source modes produce unexpected colors, because Color QuickDraw performs Boolean operations on the indexes rather than on actual color values, and the resulting index may point to an entirely unrelated color. On direct devices these transfer modes generally do not exhibit rigorous Boolean behavior except when white is set as the background color.

Dithering

With the CopyBits and CopyDeepMask procedures you can use dithering, a technique used by these procedures for mixing existing colors together to create the illusion of a third color that may be unavailable on an indexed device. For example, if you specify dithering when copying a purple image from a 32-bit direct device to an 8-bit indexed device that does not have purple available, these procedures mix blue and red pixels to give the illusion of purple on the 8-bit device.

Dithering is also useful for improving images that you shrink when copying them from a direct device to an indexed device.

On computers running System 7, you can add dithering to any source mode by adding the following constant or the value it represents to the source mode:

CONST ditherCopy = 64;  {add to source mode for dithering}
For example, specifying srcCopy + ditherCopy in the mode parameter to CopyBits causes CopyBits to dither the image when it copies the image into the destination pixel map.

Dithering has drawbacks. First, dithering slows the drawing operation. Second, a clipped dithering operation does not provide pixel-for-pixel equivalence to the same unclipped dithering operation. When you don't specify a clipping region, for example, CopyDeepMask begins copying the upper-left pixel in your source image and, if necessary, begins calculating how to dither the upper-left pixel and its adjoining pixels in your destination in order to approximate the color of the source pixel. As CopyDeepMask continues copying pixels in this manner, there is a cumulative dithering effect based on the preceding pixels in the source image. If you specify a clipping region to CopyDeepMask, dithering begins with the upper-left pixel in the clipped region; this ignores the cumulative dithering effect that would otherwise occur by starting at the upper-left corner of the source image. In particular, if you clip and dither a region using the srcXor mode, you can't use CopyDeepMask a second time to copy that region back into the destination pixel map in order to erase that region.

If you replace the Color Manager's color search function with your own search function (as described in the chapter "Color Manager" in Advanced Color Imaging on the Mac OS), CopyBits and CopyDeepMask cannot perform dithering. Without dithering, your application does color mapping on a pixel-by-pixel basis. If your source pixel map is composed of indexed pixels, and if you have installed a custom color search function, Color QuickDraw calls your function once for each color in the color table for the source PixMap record. If your source pixel map is composed of direct pixels, Color QuickDraw calls your custom search function for each color in the pixel image for the source PixMap record; with an image of many colors, this can take a long time.

If you specify a destination rectangle that is smaller than the source rectangle when using CopyBits, CopyMask, or CopyDeepMask on a direct device, Color QuickDraw automatically uses an averaging technique to produce the destination pixels, maintaining high-quality images when shrinking them. On indexed devices, Color QuickDraw averages these pixels only if you specify dithering. Using dithering even when shrinking 1-bit images can produce much better representations of the original images. (The chapter "QuickDraw Drawing" includes a code sample called MyShrinkImages, shown in Listing 3-11 on page 3-29, that illustrates how to use CopyBits to scale a bit image when copying it from one window into another.)

Arithmetic Transfer Modes

In addition to the Boolean source modes described previously, Color QuickDraw offers a set of transfer modes that perform arithmetic operations on the values of the red, green, and blue components of the source and destination pixels. Although rarely used by applications, these arithmetic transfer modes produce predictable results on indexed devices because they work with RGB colors rather than with color table indexes. These arithmetic transfer modes are represented by the following constants:

CONST
   blend       = 32; {replace destination pixel with a blend }
                     { of the source and destination pixel }
                     { colors; if the destination is a bitmap or }
                     { 1-bit pixel map, revert to srcCopy mode}
   addPin      = 33; {replace destination pixel with the sum of }
                     { the source and destination pixel colors-- }
                     { up to a maximum allowable value; if }
                     { the destination is a bitmap or }
                     { 1-bit pixel map, revert to srcBic mode}
   addOver     = 34; {replace destination pixel with the sum of }
                     { the source and destination pixel colors-- }
                     { but if the value of the red, green, or }
                     { blue component exceeds 65,536, then } 
                     { subtract 65,536 from that value; if the }
                     { destination is a bitmap or 1-bit }
                     { pixel map, revert to srcXor mode}
   subPin      = 35; {replace destination pixel with the }
                     { difference of the source and destination }
                     { pixel colors--but not less than a minimum }
                     { allowable value; if the destination }
                     { is a bitmap or 1-bit pixel map, revert to }
                     { srcOr mode}
   transparent = 36; {replace the destination pixel with the }
                     { source pixel if the source pixel isn't }
                     { equal to the background color}
   addMax      = 37; {compare the source and destination pixels, }
                     { and replace the destination pixel with }
                     { the color containing the greater }
                     { saturation of each of the RGB components; }
                     { if the destination is a bitmap or }
                     { 1-bit pixel map, revert to srcBic mode}
   subOver     = 38; {replace destination pixel with the }
                     { difference of the source and destination }
                     { pixel colors--but if the value of a red, }
                     { green, or blue component is }
                     { less than 0, add the negative result to }
                     { 65,536; if the destination is a bitmap or }
                     { 1-bit pixel map, revert to srcXor mode}
   adMin       = 39; {compare the source and destination pixels, }
                     { and replace the destination pixel with }
                     { the color containing the lesser }
                     { saturation of each of the RGB components; }
                     { if the destination is a bitmap or }
                     { 1-bit pixel map, revert to srcOr mode}
Note
You can use the arithmetic modes for all drawing operations; that is, your application can pass them in parameters to the PenMode, CopyBits, CopyDeepMask, and TextMode routines. (The TextMode procedure is described in Inside Macintosh: Text.
When you use the arithmetic transfer modes, each drawing routine converts indexed source and destination pixels to their RGB components; performs the arithmetic operation on each pair of red, green, and blue components to provide a new RGB color for the destination pixel; and then assigns the destination a pixel value close to the calculated RGB color.

For indexed pixels, the arithmetic transfer modes obtain the full 48-bit RGB color from the CLUT. For direct pixels, the arithmetic transfer modes use the 15 or 24 bits of the truncated RGB color. Note, however, that because the colors for indexed pixels depend on the set of colors currently loaded into a graphics device's CLUT, arithmetic transfer modes may produce effects that differ between indexed and direct devices.

Note
The arithmetic transfer modes have no coloration effects.
When you use the addPin mode in a basic graphics port, the maximum allowable value for the destination pixel is always white. In a color graphics port, you can assign the maximum allowable value with the OpColor procedure, described on page 4-69. Note that the addOver mode is slightly faster than the addPin mode.

When you use the subPin mode in a basic graphics port, the minimum allowable value for the destination pixel is always black. In a color graphics port, you can assign the minimum allowable value with the OpColor procedure. Note that the subOver mode
is slightly faster than the subPin mode.

When you use the addMax and adMin modes, Color QuickDraw compares each RGB component of the source and destination pixels independently, so the resulting color isn't necessarily either the source or the destination color.

When you use the blend mode, Color QuickDraw uses this formula to calculate the weighted average of the source and destination pixels, which Color QuickDraw assigns to the destination pixel:

dest = source weight/65,535 + destination (1 - weight/65,535)

In this formula, weight is an unsigned value between 0 and 65,535, inclusive. In a basic graphics port, the weight is set to 50 percent gray, so that equal weights of the source and destination RGB components are combined to produce the destination color. In a color graphics port, the weight is an RGBColor record that individually specifies the weights of the red, green, and blue components. You can assign the weight value with the OpColor procedure.

The transparent mode is most useful on indexed devices, which have 8-bit and 4-bit pixel depths, and on black-and-white devices. You can specify the transparent mode in the mode parameter to the TextMode, PenMode, and CopyBits routines. To specify a transparent pattern, add the transparent constant to the patCopy constant:

transparent + patCopy 
The transparent mode is optimized to handle source bitmaps with large transparent holes, as an alternative to specifying an unusual clipping region or mask to the CopyMask procedure. Patterns aren't optimized, and may not draw as quickly.

The arithmetic transfer modes are most useful in direct and 8-bit indexed pixels, but work on 4-bit and 2-bit pixels as well. If the destination pixel map is 1 bit deep, the arithmetic transfer mode reverts to a comparable Boolean transfer mode, as shown in Table 4-2. (The hilite mode is explained in the next section.)

Table 4-2 Arithmetic modes in a 1-bit environment
Initial arithmetic modeResulting source mode
blendsrcCopy
addOver, subOver, hilitesrcXor
addPin, addMaxsrcBic
subPin, adMin, transparentsrcOr

Because drawing with the arithmetic modes uses the closest matching colors, and not necessarily exact matches, these modes might not produce the results you expect. For instance, suppose your application uses the srcCopy mode to paint a green pixel on a screen with 4-bit pixel values. Of the 16 colors available, the closest green may contain a small amount of red, as in RGB components of 300 red, 65,535 green, and 0 blue. Then, your application uses addOver mode to paint a red pixel on top of the green pixel, ideally resulting in a yellow pixel. But the red pixel's RGB components are 65,535 red, 0 green, and 0 blue. Adding the red components of the red and green pixels wraps to 300, since the largest representable value is 65,535. In this case, addOver causes no visible change at all. You can prevent the maximum value from wrapping around by using the OpColor procedure to set the maximum allowable color to white, in which the maximum red value is 65,535. Then you can use the addPin mode to produce the desired yellow result.

Note that the arithmetic transfer modes don't call the Color Manager when mapping a requested RGB color to an indexed pixel value. If your application replaces the Color Manager's color-matching routines (which are described in the chapter "Color Manager" in Advanced Color Imaging on the Mac OS), you must not use these modes, or you must maintain the inverse table yourself.

Highlighting

When highlighting, Color QuickDraw replaces the background color with the highlight color when your application draws or copies images between graphics ports. This has the visual effect of using a highlighting pen to select the object. For instance, TextEdit (described in Inside Macintosh: Text) uses highlighting to indicated selected text; if the highlight color is yellow, TextEdit draws the selected text, then uses InvertRgn to produce a yellow background for the text.

With basic QuickDraw, you can use InvertRect, InvertRgn, InvertArc, InvertRoundRect, or InvertPoly and any image-copying routine that uses the srcXor source mode to invert objects on the screen.

In general, however, you should use highlighting with Color QuickDraw when selecting and deselecting objects such as text or graphics. (Highlighting has no effect in basic QuickDraw.) The line reading "hilited" in Figure 4-15 uses highlighting; the user selected red as the highlight color, which the application uses as the background for the text. (This figure shows the effect in grayscale.) The application simply inverts the background for the line reading "inverted." Inversion reverses the colors of all pixels within the rectangle's boundary. On a black-and-white monitor, this changes all black pixels in the shape to white, and changes all white pixels to black. Although this procedure operates on color pixels in color graphics ports, the results are predictable only with direct pixels or 1-bit pixel maps.

Figure 4-15 Difference between highlighting and inverting


The global variable HiliteRGB is read from parameter RAM when the machine starts. Basic graphics ports use the color stored in the HiliteRGB global variable as the highlight color. Color graphics ports default to the HiliteRGB global variable, but you can override this by using the HiliteColor procedure, described on page 4-69.

To turn highlighting on when using Color QuickDraw, you can clear the highlight bit just before calling InvertRect, InvertRgn, InvertArc, InvertRoundRect, InvertPoly, or any drawing or image-copying routine that uses the patXor or srcXor transfer mode. On a bitmap or a 1-bit pixel map, this works exactly like inversion and is compatible with all versions of QuickDraw.

The following constant represents the highlight bit:

CONST pHiliteBit = 0; {flag bit in HiliteMode used with BitClr}
You can use the BitClr procedure as shown in Listing 4-6 to clear system software's highlight bit (BitClr is described in Inside Macintosh: Operating System Utilities).

Listing 4-6 Setting the highlight bit

PROCEDURE MySetHiliteMode;
BEGIN
   BitClr(Ptr(HiliteMode), pHiliteBit);
END;
Listing 4-7 shows the code that produced the effects in Figure 4-15.

Listing 4-7 Using highlighting for text

PROCEDURE HiliteDemonstration (window: WindowPtr);
CONST
   s1 = ' hilited ';
   s2 = ' inverted ';
VAR
   familyID:   Integer;
   r1, r2:     Rect;
   info:       FontInfo;
   bg:         RGBColor;
BEGIN
   TextSize(48);
   GetFontInfo(info);
   SetRect(r1, 0, 0, StringWidth(s1), info.ascent + info.descent);
   SetRect(r2, 0, 0, StringWidth(s2), info.ascent + info.descent);
   OffsetRect(r1, 30, 20);
   OffsetRect(r2, 30, 100);
{fill the background with a light-blue color}
   bg.red := $A000;
   bg.green := $FFFF;
   bg.blue := $E000;
   RGBBackColor(bg);
   EraseRect(window^.portRect);
{draw the string to highlight}
   MoveTo(r1.left + 2, r1.bottom - info.descent);
   DrawString(s1);
   MySetHiliteMode;  {clear the highlight bit}
{InvertRect replaces pixels in background color with the }
{ user-specified highlight color}
   InvertRect(r1);
{the highlight bit is reset automatically}
{show inverted text, for comparison}
   MoveTo(r2.left + 2, r2.bottom - info.descent);
   DrawString(s2);
   InvertRect(r2);
END;
Color QuickDraw resets the highlight bit after performing each drawing operation, so your application should always clear the highlight bit immediately before calling a routine with which you want to use highlighting.

Another way to use highlighting is to add this constant or its value to the mode you specify to the PenMode, CopyBits, CopyDeepMask, and TextMode routines:

CONST hilite = 50;{add to source or pattern mode for highlighting}
Highlighting uses the pattern or source image to decide which bits to exchange; only bits that are on in the pattern or source image can be highlighted in the destination.

A very small selection should probably not use highlighting, because it might be too hard to see the selection in the highlight color. TextEdit, for instance, uses highlighting to select and deselect text, but not to highlight the insertion point.

Highlighting is optimized to look for consecutive pixels in either the highlight or background colors. For example, if the source is an all-black pattern, the highlighting is especially fast, operating internally on one long word at a time instead of one pixel at a time. Highlighting a large area without such consecutive pixels (a gray pattern, for instance) can be slow.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996