Jump to content

GPIGuide - Color and Mix Attributes

From EDM2

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

GPI Guide and Reference
  1. How to Use the GPI Guide and Reference
  2. Graphics Functions
  3. Data Types
  4. Graphics Orders
  5. Graphics Orders Data Types
  6. Errors
  7. Area and Polygon Primitives
  8. Bit Maps
  9. Creating and Drawing Retained Graphics
  10. Character String Primitives
  11. Clipping and Boundary Determination
  12. Color and Mix Attributes
  13. Correlation
  14. Coordinate Spaces and Transformations
  15. Editing Retained Graphics and Graphics Segments
  16. Fonts
  17. Graphics Attributes
  18. Line and Arc Primitives
  19. Marker Primitives
  20. Matrix Multiplication
  21. Metafiles
  22. Print Job Submission and Manipulation
  23. Presentation Spaces and Device Contexts
  24. Paths
  25. Regions
  26. Notices
  27. Glossary

This chapter covers color and mix attributes in OS/2 applications, related to presentation spaces, device contexts, and various graphics primitives (lines, arcs, markers, areas, character strings, bitmaps, and images).

About Color and Mix Attributes

Color and mix attributes define graphics primitives= primitives. They are specified in BUNDLE data structures for the five graphics primitives and bitmaps. Some primitives and bitmaps have both foreground and background color attributes. For instance, a character string primitive has a foreground color for the character and a background color for the surrounding cell.

The mix attribute determines how a primitive combines with existing drawings, affecting the resulting color when primitives of different colors overlap. Primitives with foreground and background colors also have corresponding mix attributes.

Color Implementation

Understanding color implementation aids in grasping color and mix attributes. A pel (pixel element) is the smallest addressable display screen element. Monochrome displays use one bit per pel (on/off). Color displays have red, green, and blue phosphors per pel, illuminated by color guns to produce eight standard colors:

Pel appears... Red Green Blue
Red ON OFF OFF
Green OFF ON OFF
Blue OFF OFF ON
White ON ON ON
Black OFF OFF OFF
Cyan (Turquoise) OFF ON ON
Pink ON OFF ON
Yellow ON ON OFF

Monochrome displays use one bit per pel, while eight-color systems require three bits per pel to record color gun settings. More bits (e.g., six bits for 32 combinations) allow intensity control for additional colors. To manage storage, applications use a logical color table to define a subset of colors, mapped to the nearest available colors in the hardware’s physical palette.

RGB Color Encoding

RGB components are stored in RGB or RGB2 structures or as a 32-bit long integer. In a long integer, the first 8 bits are reserved for flags (set to 0), and the remaining 24 bits encode color intensity (8 bits each for red, green, blue, ranging from 0 to 255). A value of 0 indicates no presence of the color, 128 indicates a pale color, and 255 indicates maximum intensity.

The RGB value is calculated as:

RGB Value = (R * 65536) + (G * 256) + B
Where:
    R = red intensity
    G = green intensity
    B = blue intensity

Standard RGB values for the eight colors are:

Color RGB value
Black 0x00000000
Red 0x00FF0000
Green 0x0000FF00
Blue 0x000000FF
Pink 0x00FF00FF
Cyan 0x0000FFFF
Yellow 0x00FFFF00
White 0x00FFFFFF

Color Tables

A color table is an array of RGB values, divided into logical and physical types. The logical color table is specific to a presentation space, defining application-specific colors. The physical color table lists colors a device can currently generate, shared across applications. Due to hardware limitations, the physical table may be a subset of possible device colors, with the operating system mapping logical table RGB values to the closest physical table colors.

Logical Color Table

A logical color table contains variable entries, each specifying an RGB combination (see img43.bmp: Logical Color Table). For example, yellow is created by mixing equal intensities of red and green with no blue, addressed by index 21 in a modified table. Stored in a presentation space, it allows applications to use color indexes instead of explicit RGB values. Colors vary by device, so table definitions can be adjusted for optimal results.

Default Logical Color Table

The PM provides a default logical color table:

Default Logical Color Table

Color Index Index Number Effect
CLR_FALSE -5 All bits are set to 0.
CLR_TRUE -4 All bits are set to 1.
CLR_DEFAULT -3 Default value
CLR_WHITE -2 White
CLR_BLACK -1 Black
CLR_BACKGROUND 0 Natural background color for the device
CLR_BLUE 1 Blue
CLR_RED 2 Red
CLR_PINK 3 Pink
CLR_GREEN 4 Green
CLR_CYAN 5 Cyan
CLR_YELLOW 6 Yellow
CLR_NEUTRAL 7 Neutral - The contrasting color to CLR_BACKGROUND
CLR_DARKGRAY 8 Dark gray
CLR_DARKBLUE 9 Dark blue
CLR_DARKRED 10 Dark red
CLR_DARKPINK 11 Dark pink
CLR_DARKGREEN 12 Dark green
CLR_DARKCYAN 13 Dark cyan
CLR_BROWN 14 Brown
CLR_PALEGRAY 15 Pale gray

Note: Entries after CLR_PALEGRAY are device-dependent. The PM maps color index names to index numbers, which address the corresponding color.

Device-Independent Color Indexing

Three indexes provide device independence: CLR_DEFAULT, CLR_BACKGROUND, and CLR_NEUTRAL, selecting colors by purpose:

Index Purpose
CLR_BACKGROUND The natural background color for the device. This is the color of the paper on a printer, and the window background color (white, by default) on a display.
CLR_NEUTRAL The contrast color to CLR_BACKGROUND. This is usually black on a printer, and the default window text color (black, by default) on a display.
CLR_DEFAULT Unless redefined, this has the same effect as CLR_NEUTRAL.

These colors vary by device (e.g., CLR_NEUTRAL may be black on a white background or white on a black background).

Defining a Logical Color Table

Use GpiCreateLogColorTable to modify the default logical color table, allowing applications to:

  • Replace part or all of the table.
  • Add color definitions.
  • Reset to default values.

Methods include:

  • Supplying an array of color indexes and RGB values to add or change entries.
  • Supplying an array of RGB values for consecutive index values.

Color Tables in Index Mode

The default logical color table is in index mode, defined as an array of color indexes and RGB values. Use LCOLF_INDRGB or LCOLF_CONSECRGB formats in GpiCreateLogColorTable to alter it. Color index names retrieve colors by their index number (e.g., CLR_NEUTRAL retrieves index 7’s color). To make CLR_NEUTRAL blue, set index 7 to the desired blue RGB value.

  • LCOLF_INDRGB: Supply index-RGB pairs for non-consecutive updates or additions.
  • LCOLF_CONSECRGB: Supply RGB values and a starting index for consecutive updates or additions (e.g., redefining CLR_BLUE and CLR_PINK requires specifying CLR_RED).

When setting a color field in a primitive’s attribute structure via GpiSetAttrs, the value is an index into the logical color table. The operating system uses this index to retrieve the RGB value, then finds the closest color in the physical color table for drawing.

Color Tables in RGB Mode

Colors can be specified directly as RGB values using GpiSetAttrs or GpiSetColor by switching the logical color table to RGB mode with GpiCreateLogColorTable and LCOLF_RGB format (no color array). Switch back to index mode using LCOLF_INDRGB or LCOLF_CONSECRGB.

Querying the Available Colors

Query functions provide color table information:

  • GpiQueryColorData: Checks if the default logical color table is in effect, returning its format and index range (smallest is 0, largest ≥ 15).
  • GpiQueryRealColors: Returns RGB values of physical color table colors and their corresponding logical color table indexes.
  • GpiQueryNearestColor: Returns the closest physical color table RGB value for a specified RGB input.
  • GpiQueryRGBColor: Returns the physical color table RGB value for a logical color table index. In RGB mode, it matches GpiQueryNearestColor results.
  • GpiQueryColorIndex: Returns the logical color table index for the closest match to a given RGB value.
  • GpiQueryLogColorTable: Returns colors in the current logical color table.
  • GpiQueryRealColors: Returns colors in the physical color table.

Physical Color Table

Each device has a physical color table, similar to a logical color table, containing RGB definitions of producible colors. The PM maps logical color table indexes to the closest physical color table match during drawing, allowing a presentation space to work with multiple device contexts without invalidating color definitions. For example, a colorful screen drawing can be sent to an eight-pen plotter, with colors substituted to the closest matches.

Palette Manager

Use GpiCreatePalette to modify the physical color palette for specific color needs or to prevent dithering of the 16 default colors (use LCOL_PURECOLOR flag). Use LCOL_OVERRIDE_DEFAULT_COLORS for all 256 colors. Palette manager functions should be used sparingly, as consistent colors across windows are not guaranteed, and palette changes are device-dependent.

Do not mix color table and palette functions. Use DevQueryCaps to check palette support.

Realizing a Color Palette

WinRealizePalette maps application-requested colors to the system’s physical palette when a window is activated, realizing the changes. If palette entries are limited (max 256), a WM_REALIZEPALETTE message is sent to all desktop applications.

Applications using palette manager functions must repaint their screens upon receiving WM_REALIZEPALETTE, mapping colors to the closest match in the new palette. Non-palette applications perform default processing for repainting. No message is sent for palette additions without changes.

Note: Color mapping may not closely match the original color. Physical palette changes occur as window focus shifts, with notifications sent as needed. WinRealizePalette returns the number of altered entries. Palette realization requires ≥256 colors.

Color Attribute

The PM uses system colors defined in a system color table, separate from logical color tables. Query RGB values with WinQuerySysColor. Graphic primitives’ colors are specified separately, with every primitive having a foreground color and some having a background color.

Primitive Foreground

The foreground is the primitive itself (e.g., a full arc’s foreground is the arc; see img44.bmp). The default foreground color is CLR_DEFAULT, producing black on displays in the default color table. If the table is replaced, CLR_DEFAULT uses index 7 (CLR_NEUTRAL).

Primitive Background

Primitives with backgrounds include areas, character strings, images, and markers. The background is the entire character/marker box, area to be filled, or unset pels in an image (see img32.bmp). The background is drawn first, followed by the foreground. The default background color is CLR_BACKGROUND, matching the device’s natural background (e.g., paper color on printers, window background on displays).

Changing the Foreground and Background Colors of Primitives

  • Set foreground color with GpiSetColor, using a color index or RGB value based on the table mode. Indexes beyond the default table must be loaded first. System colors (e.g., SYSCLR_ACTIVEBORDER) can be used, bypassing the logical color table.
  • Set specific primitive type foreground colors with GpiSetAttrs. For example, setting CLR_RED via GpiSetColor and CLR_CYAN for markers via GpiSetAttrs makes markers cyan and other primitives red.
  • Set background color with GpiSetBackColor, ensuring it differs from the output area’s background and selecting an appropriate mix attribute.
  • Query colors with GpiQueryBackColor (character primitive background), GpiQueryColor (foreground), or GpiQueryAttrs (specific primitive type foreground/background).

Color Output and Mix Attributes

A mix attribute is a bitwise operation on logical color table indexes, specifying colors relative to others. When drawing, the operating system uses the mix attribute to determine the output color. For example, using FM_XOR ensures a line is the inverse of the background color, visible even on a matching background.

Example: If LINEBUNDLE’s lColor is CLR_RED and usMixMode is FM_OR, with a CLR_GREEN drawing surface, the result is:

0010 (red index)
0100 (green index)
------
0110 (yellow index)

The line appears yellow despite specifying CLR_RED.

Mix Attribute

Mix attributes control how primitives combine with existing drawings, determining overlap colors. They include foreground mix (all primitives) and background mix (primitives with backgrounds: areas, character strings, images, markers). Attribute structures contain fields for both.

Seventeen foreground mix attributes combine foreground and drawing-surface color indexes using bitwise operators:

Foreground Mix Attributes

Mix Attribute Effect Description
FM_DEFAULT Default Default foreground mix attribute (overpaint).
FM_OR OR Bitwise OR on foreground and drawing-surface color indexes.
FM_OVERPAINT Overpaint Uses foreground color index. Default foreground mix attribute.
FM_XOR Exclusive-OR (XOR) Bitwise XOR on foreground and drawing-surface color indexes.
FM_LEAVEALONE Leave-alone (Invisible) Uses drawing-surface color index.
FM_AND AND Bitwise AND on foreground and drawing-surface color indexes.
FM_SUBTRACT (Inverse Source) AND Destination Inverts foreground color index, then AND with drawing-surface index.
FM_MASKSRCNOT Source AND (Inverse Destination) Inverts drawing-surface color index, then AND with foreground index.
FM_ZERO All zeros RGB value is 0x00000000.
FM_NOTMERGESRC Inverse (Source OR Destination) Inverse of FM_OR result.
FM_NOTXORSRC Inverse (Source XOR Destination) Inverse of FM_XOR result.
FM_INVERT Inverse (Destination) Inverse of drawing-surface color index.
FM_MERGESRCNOT Source OR (Inverse Destination) OR on foreground and inverse drawing-surface color indexes.
FM_NOTCOPYSRC Inverse (Source) Inverse of foreground color index.
FM_MERGENOTSRC (Inverse Source) OR Destination AND on drawing-surface and inverse foreground color indexes.
FM_NOTMASKSRC Inverse (Source AND Destination) Inverse of FM_AND result.
FM_ONE All 1’s RGB value is 0x00FFFFFF.

Five background mix attributes combine background and drawing-surface color indexes:

Background Mix Attributes

Mix Attribute Effect Description
BM_DEFAULT Default Default background mix attribute (Leave-alone).
BM_OR OR Bitwise OR on background and drawing-surface color indexes.
BM_OVERPAINT Overpaint Uses background color index.
BM_XOR Exclusive-OR (XOR) Bitwise XOR on background and drawing-surface color indexes.
BM_LEAVEALONE Leave-alone (Invisible) Uses drawing-surface color index.

The resulting RGB values come from the physical color table, making outcomes unpredictable without palette realization. Common attributes are FM_OVERPAINT, FM_OR, FM_XOR (foreground) and BM_LEAVEALONE, BM_OVERPAINT (background).

Overpaint Mix Attribute

FM_OVERPAINT replaces existing drawing with the primitive’s foreground color at overlap points (e.g., a red circle over a yellow square appears red; see img45.bmp). BM_OVERPAINT replaces the background with the primitive’s background color, visible only if different from the output area’s background (see img46.bmp, with FM_OVERPAINT foreground).

OR Mix Attribute

FM_OR merges the foreground with the existing drawing via a bitwise OR on color indexes, producing a third color at overlap points (e.g., a circle over a square; see img47.bmp). The result is unpredictable without palette realization. BM_OR follows the same rules.

Exclusive-OR (XOR) Mix Attribute

FM_XOR, available on displays, allows objects to be drawn and removed by redrawing with FM_XOR, restoring overlapped graphics. Useful for animation, the process is:

1. Draw the object with FM_XOR. 2. Calculate the next position. 3. Redraw in the current position with FM_XOR to erase it. 4. Draw in the new position with FM_XOR.

For retained graphics, dynamic segments use FM_XOR automatically. At overlap points, identical colors cancel out (see img48.bmp). BM_XOR follows the same rules.

Leave-Alone Mix Attribute

FM_LEAVEALONE prevents drawing the primitive’s foreground. BM_LEAVEALONE, commonly used for character strings and markers with FM_OVERPAINT, prevents drawing the background (see img49.bmp).

Specifying Foreground and Background Mix Attributes

Note: Not all devices support all mix attributes. Unsupported attributes default to the device’s default mix, with no error. Use DevQueryCaps to check support.

Color on Advanced Display Devices

Some devices display a fixed number of colors (e.g., 156) from a larger pool (e.g., >256,000). Palette manager functions allow changing the physical color table and displayed colors without redrawing. Use DevQueryCaps with CAPS_ADDITIONAL_GRAPHICS to confirm support.

Dithering

If a requested color is unavailable, the operating system may use dithering to approximate it by mixing available colors (e.g., yellow and green pels for light green). Dithering leverages human eye perception, creating a third color at a distance (e.g., alternating colors in a checkerboard). It works for solid fills but not lines. On monochrome devices, black and white pels create gray shades. Prevent dithering with LCOL_PURECOLOR in GpiCreateLogColorTable, using the nearest pure color.

Considerations When Using Monochrome Displays

Drawing Color Graphics on Monochrome Devices

Monochrome devices (screens, printers, bitmaps) map colors to a reset color (used by GpiErase) and a contrast color (opposite of reset: black if reset is white, and vice versa). The reset color is determined by CLR_BACKGROUND:

  • If no logical color table is loaded, CLR_BACKGROUND is the paper color (printer) or SYSCLR_WINDOW (display).
  • If loaded, CLR_BACKGROUND is index 0’s color.
  • If CLR_BACKGROUND is white or black, it becomes the reset color. Otherwise, it translates to the nearest (dark colors → black, pale → white).

Rules for mapping:

  • Graphics in CLR_BACKGROUND or the actual reset color are drawn in the reset color; others use the contrast color.
  • CLR_WHITE produces white, CLR_BLACK produces black, regardless of reset color.
  • Without a color table, CLR_DEFAULT or CLR_NEUTRAL as foreground produce the contrast color; as background, they produce the reset color.
  • GpiQueryNearestColor returns the reset color if input matches it, otherwise the contrast color.

Drawing Color Area Fill Patterns on Monochrome Devices

Area primitives use current foreground/background colors and mix attributes. On color devices, monochrome pattern bits set to 1 use the foreground color, 0 use the background. On monochrome devices, for non-default/solid patterns, the color closest to white becomes 1 bits (e.g., cyan foreground, red background → cyan to white (1), red to black (0)):

Pattern    As 1s and 0s  Color Surface  Monochrome Surface
\   \      10001000      RcccRccc       01110111
 \   \     01000100      cRcccRcc       10111011
  \   \    00100010      ccRcccRc       11011101
   \   \   00010001      cccRcccR       11101110
\   \      10001000      RcccRccc       01110111
 \   \     01000100      cRcccRcc       10111011
  \   \    00100010      ccRcccRc       11011101
   \   \   00010001      cccRcccR       11101110

With BM_LEAVEALONE, background bits (1s) are not drawn:

Pattern    As 1s and 0s  Color Surface  Monochrome Surface
\   \      10001000      R...R...       0...0...
 \   \     01000100      .R...R..       .0...0..
  \   \    00100010      ..R...R.       ..0...0.
   \   \   00010001      ...R...R       ...0...0
\   \      10001000      R...R...       0...0...
 \   \     01000100      .R...R..       .0...0..
  \   \    00100010      ..R...R.       ..0...0.
   \   \   00010001      ...R...R       ...0...0

For bitmap fill patterns, bits in the background color are set to 0, others to 1. For solid patterns (PATSYM_DEFAULT or PATSYM_SOLID):

  • Dithering off, color surface: Uses nearest color.
  • Dithering on, color surface: Combines colors to approximate the requested color (e.g., red and white for pink).
  • Dithering on, monochrome surface: Sets pels to suggest color intensity.

Enable/disable dithering with LCOL_PURECOLOR in GpiCreateLogColorTable.

Using Color and Mix Attributes

Color and mix attribute functions support:

  • Creating a logical color table
  • Determining the current logical color table’s format and index values
  • Finding the index for an RGB value
  • Determining an index’s RGB value
  • Setting/querying foreground and background colors
  • Setting/querying foreground and background mix attributes

Creating a Logical Color Table

Create an array of RGB values and call GpiCreateLogColorTable with LCOL_RESET and LCOLF_CONSECRGB:

#define INCL_GPILOGCOLORTABLE
#include <os2.h>

void fncCOLR01(void) {
    HPS hps;
    LONG alTable[] = {
        0xFFFFFF, /* White */
        0xFF88FF,
        0xFF8800,
        0xFF8888,
        0xFF0088,
        0x880088,
        0x008888,
        0x00FF88,
        0x00F800,
        0x008800,
        0x000088,
        0x0000F8,
        0x0800F8,
        0x888888,
        0x080808,
        0x000000 /* Black */
    };

    GpiCreateLogColorTable(hps,
        LCOL_RESET, /* Start with the default */
        LCOLF_CONSECRGB, /* Consecutive RGB values */
        0, /* Starting index in table */
        sizeof(alTable) / sizeof(LONG), /* Number of elements in table */
        alTable);
}

Determining the Color-Table Format and Index Values

Use GpiQueryColorData to check the logical color table’s format and index range, loading a new table if the default is in use:

#define INCL_GPILOGCOLORTABLE
#include <os2.h>

void fncCOLR02(void) {
    HPS hps;
    LONG aClrData[3];
    LONG alTable[16];

    GpiQueryColorData(hps, 3, aClrData);

    if (aClrData[QCD_LCT_FORMAT] == LCOLF_DEFAULT)
        GpiCreateLogColorTable(hps,
            LCOL_RESET, /* Start with the default */
            LCOLF_CONSECRGB, /* Consecutive RGB values */
            0, /* Starting index in table */
            sizeof(alTable) / sizeof(LONG), /* Number of elements in table */
            alTable);
}

Determining the Index Value of an RGB Value

Use GpiQueryColorIndex to find the logical color table index closest to an RGB value, then set the foreground color:

#define INCL_GPILOGCOLORTABLE
#include <os2.h>

void fncCOLR03(void) {
    LONG lIndex; /* Logical-color-table index */
    HPS hps;

    lIndex = GpiQueryColorIndex(hps, LCOLOPT_REALIZED, 0x00FF00FF);

    if ((lIndex >= 0) && (lIndex <= 15)) /* Check for valid index */
        GpiSetColor(hps, lIndex);
}

Setting the Primitive Color Attributes

Set colors for a specific primitive type with GpiSetAttrs or all primitives with GpiSetColor and GpiSetBackColor. Example for line primitives:

#define INCL_GPIPRIMITIVES
#include <os2.h>

void fncCOLR04(void) {
    LINEBUNDLE lbnd; /* Line-primitive attribute structure */
    HPS hps;

    lbnd.lColor = CLR_DARKGRAY;

    GpiSetAttrs(hps, PRIM_LINE, LBB_COLOR, 0, &lbnd);
}

Example for all primitives:

#include <os2.h>

void fncCOLR05(void) {
    HPS hps;

    GpiSetColor(hps, CLR_DARKGRAY);
}

Creating a Palette

Check palette support with DevQueryCaps, create an array of RGB2 structures, and use GpiCreatePalette, GpiSelectPalette, WinRealizePalette, then clean up:

#define INCL_GPILOGCOLORTABLE
#define INCL_GPIBITMAPS
#include <os2.h>

void fncCOLR06(void) {
    COLOR clrCurrent;
    HPAL hpal;
    HDC hdc;
    HPS hps;
    HAB hab;
    HWND hwnd;
    POINTL aptl[2], ptl;
    LONG cSimulColors, lPalSupport;
    SHORT j;
    RGB2 *prgb2ColorData;

    /* Determine how many colors the device can display at once. */
    DevQueryCaps(hdc, CAPS_COLORS, 1, &cSimulColors);

    /* Determine if the device supports palette manager functions. */
    DevQueryCaps(hdc, CAPS_ADDITIONAL_GRAPHICS, 1, &lPalSupport);

    /* Allocate space for the array of RGB2 structures. */
    DosAllocMem((PPVOID)&prgb2ColorData, cSimulColors * sizeof(RGB2), fALLOC);

    /* Fill the array of RGB2 structures with shades of blue. */
    clrCurrent = 0x000000FF;
    for (j = 0; j < cSimulColors; j++) {
        prgb2ColorData[j].bRed = 0;
        prgb2ColorData[j].bGreen = 0;
        prgb2ColorData[j].bBlue = clrCurrent;
        prgb2ColorData[j].fcOptions = 0;
        clrCurrent = clrCurrent > 0 ? --clrCurrent : 0x000000FF;
    }

    if (lPalSupport & CAPS_PALETTE_MANAGER) {
        hpal = GpiCreatePalette(hab, /* Create palette */
            0L,
            LCOLF_CONSECRGB, /* Format of color table entries */
            cSimulColors, /* Number of entries in table */
            (PULONG)prgb2ColorData); /* Pointer to color table */
    }

    GpiSelectPalette(hps, hpal);
    WinRealizePalette(hwnd, hps);
    GpiSelectPalette(hps, NULLHANDLE); /* Restore default physical colors */
    GpiDeletePalette(hpal); /* Delete palette */
}