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: Advanced Color Imaging on the Mac OS /
Chapter 1 - Palette Manager / Using the Palette Manager


Creating Palettes

This section shows you several different ways to create a palette. The first example uses the NewPalette function in a loop to create a 16-color palette with 14 shades of red plus black and white. The second example shows how to create a resource file containing the same 16 colors and shows you how to select the right color set by inhibiting certain colors on different screens. It also shows how to copy a palette from a color table and assign the palette to a window.

Creating a Palette in Code

The Palette Manager is particularly useful when your application needs to display an image that uses a narrowed or skewed color set. For example, suppose that your application displays PICT images on a 4-bit depth screen and that a particular image contains only shades of red. By default, the system provides a range of colors to use. If your application uses these 16 default colors to display the red PICT file, the display will lose all subtlety. With the Palette Manager, you can create a special palette containing red hues only to display this PICT image.

Note
This example assumes that the PICT image to display uses shades of red. One way to obtain information about a picture to display is by using the Picture Utilities, described in the chapter "Pictures" of Inside Macintosh: Imaging With QuickDraw. The Picture Utilities allow you to obtain various information about a picture to display, including the most used colors in the picture. The Picture Utilities are available in system software version 7.0 and later.
Listing 1-1 shows how to create a palette containing shades of red only.

Listing 1-1 Creating a red palette

PaletteHandle DoMake14RedPalette (void)
{
   long              iterator;
   PaletteHandle     myPalette;
   RGBColor          color;
            
   myPalette = NewPalette(14, nil, pmTolerant, 4000);
   /* check here for nil result */

   color.green = 0;
   color.blue  = 0;
   for (iterator = 0; iterator < 14; iterator++)
   {
      /* range red component from 1/14 to 14/14 */
      /* iterator is a long, so it can safely be multiplied by 65535 */
      color.red = (iterator+1) * 65535 / 14;
      SetEntryColor(myPalette, iterator, &color);
   };
   return ( myPalette );
}
In Listing 1-1, the DoMake14RedPalette function creates a new 14-color palette containing 14 shades of red. The Palette Manager always guarantees that black and white are available to an application so that menus, windows, and other such things display properly. Therefore, you can load at most 14 new colors from a palette into a 16-color lookup table because the Palette Manager will not overwrite white (the first entry) or black (the last entry).

After defining three variables, the DoMake14RedPalette function calls the NewPalette function to create a palette large enough to hold 14 colors. The nil value passed as the second parameter specifies that the palette not be created from a color lookup table. In this case, the Palette Manager sets all three fields of each color to 0 (black). In essence, NewPalette has created a palette template. The SetEntryColor function later fills in, or changes, the color value for each color in the palette. The NewPalette function defines each color as tolerant with a tolerance of $4000.

Because this palette contains shades of red only, the green and blue fields of the color variable are set to 0. The code then defines 14 shades of red in a loop by dividing the maximum red value (65535) by 14 and adding 1/14 of 65,535 each time through the loop. Each time through the loop, the SetEntryColor function places a shade of red at the next palette entry (identified by iterator, the iteration variable). When the loop is finished, the first 14 entries in the palette created by the NewPalette function contain 14 evenly distributed shades of red.

Note
When the Palette Manager uses this palette--after you have attached it to a window with the SetPalette or NSetPalette function--it looks at the colors of the color lookup table for the 4-bit display device and determines which of the 14 colors in the palette it must load. It then loads as many of the 14 palette entries as necessary to create the 14 shades of red defined in the palette. The Palette Manager keeps the first entry in the table as white and the last as black and uses the other 14 indexes as necessary to load the shades of red. See "How the Palette Manager Allocates Colors for Display" (page 1-17) for more information on how the Palette Manager loads colors from a palette into a color lookup table.

Creating a Palette in a Resource File

The format of a palette resource (type 'pltt') is an image of the palette structure minus the private fields. The private fields in both the header and in each ColorInfo record are created by the GetNewPalette function and are reserved for future use.

Listing 1-2 shows a palette resource with 16 entries as it would appear within a resource file. The black and white entries each have a tolerance value of 0, meaning that their color should be matched exactly. The shades of red have a tolerance of $4000.

Listing 1-2 A palette ('pltt') resource

resource 'pltt' (128, "Simple Palette") {
{  /* array ColorInfo: 16 elements */
/* [1]  white */
65535, 65535, 65535, pmTolerant, 0,
/* [2] 2 through 15 are shades of red */
4681, 0, 0,       pmTolerant, $4000,
/* [3] */
9362, 0, 0,       pmTolerant, $4000,
/* [4] */
14043, 0, 0,      pmTolerant, $4000,
/* [5] */
18724, 0, 0,      pmTolerant, $4000,
/* [6] */
23405, 0, 0,      pmTolerant, $4000
/* [7] */
28086, 0, 0,      pmTolerant, $4000,
/* [8] */
32768, 0, 0,      pmTolerant, $4000,
/* [9] */
37449, 0, 0,      pmTolerant, $4000,
/* [10] */
42130, 0, 0,      pmTolerant, $4000,
/* [11] */
46811, 0, 0,      pmTolerant, $4000,
/* [12] */
51492, 0, 0,      pmTolerant, $4000,
/* [13] */
56173, 0, 0,      pmTolerant, $4000,
/* [14] */
60854, 0, 0,      pmTolerant, $4000,
/* [15] */
65535, 0, 0,      pmTolerant, $4000,
/* [16]  black */
0, 0, 0,          pmTolerant, 0
   }
};
Use the GetNewPalette function to obtain a 'pltt' resource; it initializes private fields in the palette structure. (Don't use the Resource Manager function GetResource.)

The palette defined by the palette resource in Listing 1-2 is identical to the palette created in code in Listing 1-1 in the previous section except that white and black are explicitly placed in the palette in Listing 1-2. This palette contains black and white and 14 shades of red. The shades of red are defined by setting the green and blue values of each color entry to 0 and distributing the red color in 14 increments from the lowest to the highest value (65535). One possible use of this palette is to display a PICT file that contains only shades of red.

Selecting the Right Color Set

Different types of screens (for example, screens with different bit depths) often require different color sets to best display the same image. This section, like the previous section, uses an example of creating a palette to display an image in various shades of red. In addition, the code has been modified to display different colors on different screens.

Listing 1-3 shows the code to display 14 shades of red on a 4-bit color screen, 254 shades of red on an 8-bit color screen, and no color requests at all on 2-bit screens or grayscale screens.

Listing 1-3 Displaying different colors on different types of screens

PaletteHandle MyMakeRedPalette (void)
{
   long              iterator;
   PaletteHandle     myPalette;
   RGBColor          color;
            
   myPalette = NewPalette(254+14, nil, 0, 0);
   /* check here for nil result */

   color.green = 0;
   color.blue = 0;
   /* make 14 reds that are inhibited on all 
      screens except 4-bit color */
   for (iterator = 0; iterator < 14; iterator++)
   {
      /* range red component from 1/14 to 14/14 */
      /* iterator is a long, so it can safely be multiplied by 65535 */
      color.red = (iterator+1) * 65535 / 14;
      SetEntryColor(myPalette, iterator, &color);
      SetEntryUsage(myPalette, iterator, pmTolerant+pmInhibitC2+
                           pmInhibitG2+pmInhibitG4+
                           pmInhibitC8+pmInhibitG8, 0);
   };
   /* make 254 reds that are inhibited on all 
      screens except 8-bit color */
   for (iterator = 0; iterator < 255; iterator++)
   {
      /* range red component from 1/254 to 254/254 */
      /* iterator is a longint, so can safely be multiplied by 65535 */
         color.red = (iterator+1) * 65535 / 254;
         SetEntryColor(myPalette, 14+iterator, &color);
         SetEntryUsage(myPalette, 14+iterator, pmTolerant+pmInhibitC2+
                                 pmInhibitG2+pmInhibitG4+
                                 pmInhibitC4+pmInhibitG8, 0);
   };
   return ( myPalette );
}
In Listing 1-3, the MyMakeRedPalette function creates a palette that contains shades of red to optimally display an image that contains shades of red only. The palette contains two less colors than the maximum (14 for a 4-bit screen and 254 for an 8-bit screen) so that black and white are available in the color table.

After defining three variables, the MyMakeRedPalette function calls the NewPalette function to create a palette large enough to hold 268 colors (254 plus 14). The nil value passed as the second parameter specifies that no color table be used to create the template, in which case all the colors in the palette are set to black. The two for loops that follow use the SetEntryColor and SetEntryUsage functions to change the colors and their use in the palette.

The first loop creates 14 shades of red. The green and blue values for all the colors are already set to 0. The SetEntryColor function is called in a loop to set the 14 red values. It specifies 1/14 increments up to the maximum value of 65535. The SetEntryUsage function specifies that each color is tolerant (it uses the pmTolerant constant to do this) and that the tolerance is 0 so that each color must match exactly.

The SetEntryUsage function also specifies that these 14 colors are to be used only on a 4-bit screen. It does this by inhibiting these colors on all other indexed screens using the constants pmInhibitC2 (inhibit on a 2-bit color screen), pmInhibitG2 (inhibit on a 2-bit grayscale screen), and so on for the other types of indexed screens.

The second loop creates 254 shades of red. It uses the SetEntryColor function as the first loop does with the only difference being that the red color value is incremented 1/254 at each iteration rather than 1/14. As in the first loop, the SetEntryUsage function specifies that each color is tolerant with a tolerance of 0 so that the colors must match exactly. The SetEntryUsage function also specifies that the colors are to be used only on an 8-bit color screen. It does this by inhibiting the colors on all other indexed screens.

Creating a Palette by Copying and Assigning It to a Window

You can create a palette from the colors in a color table by using the NewPalette function. It copies the colors from the table and assigns the usage and tolerance values you specify as function parameters. You assign a usage value and a tolerance value that apply to all of the palette's entries, but you can change individual entries with subsequent calls to the SetEntryUsage function, which can change both the usage and tolerance values of an entry. (If you call NewPalette without supplying a color table as a source, it creates a palette with all entries equal to black. You can then modify the entries as needed, using the SetEntryColor and SetEntryUsage functions.)

Note
Color tables define the colors that are available for pixel images on indexed devices. You can create color tables from either ColorTable data structures or color table ('clut') resources. See the chapter "Color QuickDraw" of Inside Macintosh: Imaging With QuickDraw for more information on color tables.
For example, suppose you are drawing to an indexed device with a color table containing 14 colors plus black and white and you want a palette in which the fifth color must be matched exactly but the others need only be close. Listing 1-4 shows how to create the palette from the color table and then modify the tolerance value for the fifth entry (remember that color table and palette entries are numbered starting at 0).

Listing 1-4 Copying a color table to a palette

#define  clutID   68

void DoCreatePalette()
{
   CTabHandle     myColorTable;
   PaletteHandle  myPalette;
   WindowPtr      myWindow;

   myColorTable = GetCTable (clutID);

   /* create a new window */
   myWindow = NewCWindow(nil, &BaseRect, "\pUsing Palette 
                        Manager", TRUE, documentProc, 
                        (WindowPtr) -1, TRUE, nil);

   /* create a 16-color palette */
   myPalette = NewPalette(16, myColorTable, pmTolerant, $2000);
   /* modify the 5th entry */
   SetEntryUsage(myPalette, 4, 0);

   /* assign the palette to the window */
   SetPalette ((WindowPtr) myWindow, myPalette, TRUE);
}
After defining three variables (to hold the color table ID, the palette ID, and the window ID), the DoCreatePalette function uses the GetCTable function (described in the chapter "Color QuickDraw" of Inside Macintosh: Imaging With QuickDraw) to retrieve the default color-table ('clut') resource for a screen with a 4-bit pixel depth. (You can obtain the default color-table resource for a screen by adding 64 to the pixel depth of the screen. Therefore, 68 identifies the color-table resource for a 4-bit pixel depth screen.) The NewCWindow function (described in the chapter "Window Manager" of Inside Macintosh: Macintosh Toolbox Essentials) then creates a new color window.

The NewPalette function creates a palette from the color-table resource. All colors in the new palette have a tolerance of $2000. The SetEntryUsage function changes the tolerance of the fifth entry such that an exact match is required.

Finally, the SetPalette function assigns the newly created palette to the newly created window.

Note
You can use either the SetPalette or NSetPalette function to assign a palette to a window. These functions are identical except that the NSetPalette function is more versatile in specifying whether the window is to receive updates when the color environment changes. NSetPalette allows you to specify that the window is to receive updates only when the window is active, updates only when it is not active, all updates, or none. The SetPalette function only allows the last two settings.
If you want to change a window's palette momentarily and then restore the first palette, use the GetPalette function to obtain a handle to a window's current palette and then restore it with either SetPalette or NSetPalette.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
13 NOV 1996