Chapter 5. The Graphics Context

The graphics primitives supplied with X are quite simple. Most of the details about how graphics are to be drawn are stored in a resource called a graphics context (GC). GCs are stored in the server, thus reducing the amount of information that needs to be transmitted for each graphics request. This chapter describes how to use GCs and provides details on each member of the XGCValues structure. Everyone should read this chapter.

The X routines that draw graphics are called graphics primitives. They draw dots, lines, text, images, and tile or fill areas, and will be described fully in Chapter 6, Drawing Graphics and Text. But a given graphics primitive does not contain all the information needed to draw a particular graphic. A server resource called a graphics context (GC) contains values for variables that apply to each graphics primitive. The appearance of everything that is drawn by a program is controlled by the GC that is specified with each graphics primitive. (The border and background of a window are not affected or controlled by the GC--they are controlled by window attributes, and are drawn by the server.) What is drawn into a pixmap is also controlled by the GC used in the drawing to the pixmap and, again, possibly with a different GC, if the pixmap is copied into a window. To draw, you must first create a GC and set its values, then specify that GC as an argument in the graphics primitive.

There are two performance-related reasons X was designed to use GCs. First, they reduce the traffic between Xlib and the server because the GC information is held in the server and needs to be sent only once before the first graphics request. Each subsequent primitive that specifies the same GC will use the same values. When a few settings of the GC need to be changed, only the selected few need to be sent, not the entire GC. Second, you can create several GCs and then simply specify which GC you want applied to each graphics request. This has important performance benefits on servers that are capable of caching multiple GCs in their display hardware.

The GC also allows for more convenient programming, since to provide the same flexibility without the GC, you would need to specify an absurd number of arguments every time you called a graphics primitive.

A few more words are needed regarding the distinction between the roles of the graphics primitive and the GC. You can think of a graphics primitive as specifying the general shape to be drawn, while the GC specifies how to draw it. For example, a primitive that draws a filled rectangle specifies the top-left corner of the rectangle in the drawable and its dimensions, while the GC specifies its color or the pattern applied to it (among other things). Note that both the graphics primitive and the GC play a role in selecting exactly which pixels are drawn. For example, the graphics primitive specifies the start and end points for lines (including unfilled arcs, rectangles, and polygons), while the GC specifies the width of the line and the shape of the joints and ends of the lines.Other components of the GC affect pixel selection with othergraphics primitives. For all primitives, the GC includes a clip mask that you can use to restrict which pixels are drawn.

To predict the effect of particular GC settings on a particular graphics primitive, it is useful to visualize the drawing process in a number of stages, even though in reality the drawing of each bit of each pixel is performed by the server in a single equation.[11]

  1. The first stage is pixel selection. As we just described, pixel selection is specified by the graphics primitive, in some cases along with the line_width, clip_mask, and other elements in the GC. The result of the first stage is a bitmap--a single rectangle of bits, with the pixels to be drawn set to one and the pixels not to be drawn set to zero.

  2. The second stage applies one or two colors or a pattern to the results of the first stage, resulting in a pixmap which has the same depth (number of bits per pixel) as the drawable. [12] The output of the second stage is referred to later in this chapteras the source.

  3. In the third stage, a plane mask may be applied to select which planes of the drawable can be affected by the graphics request. This is done to play tricks with color, such as to draw temporary graphics that can be erased without erasing other things already drawn in a window, as demonstrated in Chapter 7, Color. By default, the plane mask is all ones and therefore has no effect on what is drawn.

  4. In the fourth stage, the pixel values resulting from stage three can be combined with what is already on the screenusing so-called logical functions. Most graphics are drawn by simply overwriting the existing graphics using a logicalfunction of GXcopy, but there are useful tricks that can be played by using certain other logical functions.One such effect is called rubber-banding; the window manager uses thistechnique to show you the outline of a window you are moving or resizing.

Figure 5-1 illustrates these four stages used in drawing a wide line, and the GC elements that can be used to control each stage. The sections below that describe the various members of the GC are organized according to the stage that they affect. This information is summarized in one of the GC-at-a-glance tables inside the back cover of Volume Two, Xlib Reference Manual.

Since we are not yet using any of the tricks that require use of the plane_mask or logical function, the third and fourth stages in Figure 5-1 use the default values of these GC elements. They therefore do not modify the result of the graphics primitive.

To make this overview complete, it's important to mention that the GC also has the following two features (described more completely in “Graphics Exposure” and “Subwindow Mode”):

Now we will discuss how to create and set the GC, before moving on to details of individual members of the GC.

Figure 5-1. Four stages in drawing a line


Creating and Setting a Graphics Context

Before a GC can be used, you must create it by calling XCreateGC(). XCreateGC() requires only four arguments: display, drawable, values, and valuemask.

  • The display argument (pointer to a Display structure) should be familiar by now; it specifies the connection to the X server. The display argument is used in virtually every Xlib routine.

  • The drawable argument is a window or pixmap ID. You might think that the drawable argument specifies which window or pixmap the GC is to be used in, but this is not necessarily the case. It really indicates which screen the GC resource is associated with and the depth of windows it can be used with. A GC can be used on any window or pixmap of the same depth and on the same screen as the drawable specified. (Drawables were introduced in “Pixmaps and Drawables” in Chapter 2) This implies that if you want to draw into a pixmap of depth one, you need to create that pixmap first, and then use it as the drawable argument in creating the GC. A BadMatch error when drawing usually indicates you did not use the right drawable when creating the GC.

  • The values argument is an XGCValues structure (shown in Example 5-1) filled with the desired settings for the GC.

  • The valuemask argument specifies which members of the XGCValues structure are actually read. The members not represented by a bit set to one in the valuemask are given the default values listed in “Querying the Graphics Context” The symbols used to make this bitmask correspond to the members of XGCValues shown in Table 5-1.

The GC is set very much like the window attributes are set, described in “The Window Attribute Structures” in Chapter 4 Of course, there is a different structure and there are different masks for specifying which members are to be set. One other difference in practice is that every member of the GC can be set with an individual “convenience routine.” You may prefer, therefore, to create a default GC and then modify it with the individual routines rather than to set all the members in both the structure and the mask before you call XCreateGC() or XChangeGC(). Both approaches are demonstrated below.

Example 5-1 and Table 5-1 present the XGCValues structure and the masks used when calling XCreateGC() or XChangeGC().

Example 5-1. The GCValues structure

/* Data structure for setting graphics context  */
typedef struct {
   int function;               /* Logical function */
   unsigned long plane_mask;   /* Plane mask */
   unsigned long foreground;   /* Foreground pixel */
   unsigned long background;   /* Background pixel */
   int line_width;             /* Line width */
   int line_style;             /* LineSolid, LineOnOffDash,
                                * LineDoubleDash */
   int cap_style;              /* CapNotLast, CapButt, CapRound,
                                * CapProjecting */
   int join_style;             /* JoinMiter, JoinRound, JoinBevel */
   int fill_style;             /* FillSolid, FillTiled, FillStippled,
                                * FillOpaqueStippled */
   int fill_rule;              /* EvenOddRule, WindingRule */
   int arc_mode;               /* ArcChord, ArcPieSlice */
   Pixmap tile;                /* Tile pixmap for tiling operations */
   Pixmap stipple;             /* Pixmap of depth 1 */
   int ts_x_origin;            /* Offset for tile or stipple operations */
   int ts_y_origin;
   Font font;                  /* Font for text operations (except
                                * XDrawText) */
   int subwindow_mode;         /* ClipByChildren, IncludeInferiors */
   Bool graphics_exposures;    /* Should events be generated on
                                * XCopyArea, XCopyPlane  */
   int clip_x_origin;          /* Origin for clipping */
   int clip_y_origin;
   Pixmap clip_mask;           /* Bitmap for clipping */
   int dash_offset;            /* Patterned/dashed line information */
   char dashes;
} XGCValues;

The meaning and possible values for each member are described in Sections 5.3 through 5.7.

Table 5-1 shows the symbols used to specify which members of the XGCValues structure actually contain meaningful values. The valuemask is made up of these symbols combined by means of a bitwise OR (|).

Table 5-1. Symbols for Setting the XGCValues Structure

Member

Mask

Set Bit

Default

function

GCFunction

0

GXcopy

plane_mask

GCPlaneMask

1

all 1 's

foreground

GCForeground

2

0

background

GCBackground

3

1

line_width

GCLineWidth

4

0

line_style

GCLineStyle

5

LineSolid

cap_style

GCCapStyle

6

CapButt

join_style

GCJoinStyle

7

JoinMiter

fill_style

GCFillStyle

8

FillSolid

fill_rule

GCFillRule

9

EvenOddRule

arc_mode

GCArcMode

22

ArcPieSlice

tile

GCTile

10

pixmap filled with foreground pixel

stipple

GCStipple

11

pixmap filled with 1 's

ts_x_origin

GCTileStipXOrigin

12

0

ts_y_origin

GCTileStipYOrigin

13

0

font

GCFont

14

(implementation dependent)

subwindow_mode

GCSubwindowMode

15

ClipByChildren

graphics_exposures

GCGraphicsExposures

16

True

clip_x_origin

GCClipXOrigin

17

0

clip_y_origin

GCClipYOrigin

18

0

clip_mask

GCClipMask

19

None

dash_offset

GCDashOffset

20

0

dashes

GCDashList

21

4 (i.e., the list [4, 4])

Table 5-1 lists the default values for each element of the GC. A useful quick reference to the graphics context is provided inside the back cover of Volume Two.

A valuemask composed of the symbols shown in Table 5-1 is used in XChangeGC(), XCopyGC(), and XCreateGC(). In XCopyGC(), though, the valuemask indicates which members are copied from the source GC to the destination GC, and the rest of the members in the destination are left unchanged. In XChangeGC(), the specified members are changed and the rest are left unchanged.

Example 5-2 shows a simple way to set some of the values for a GC before creating it. This example uses the default values except for the foreground and background pixel values. You must always set at least the foreground component of the GC, and also the background component if it is used in what you intend to draw. This is because the default values for the foreground and background components, zero and one respectively, are not guaranteed to be black and white or even contrasting. (The relationship between pixel values and colors is explained in Chapter 7, Color.)

Example 5-2. Example of setting a GC while creating it

GC gc;
XGCValues values;
unsigned long valuemask;
   .
   .
/* Open display, create window, etc. */
   .
   .
values.foreground = BlackPixel(display,screen_num);
values.background = WhitePixel(display,screen_num);
gc = XCreateGC(display, RootWindow(display, screen_num),
      (GCForeground | GCBackground), &values);
/* Now you can use gc in drawing routines */

In Example 5-2, the foreground pixel value is set to the value returned by the BlackPixel() macro. This will result in a color of black if the default colormap is installed (more on this in Chapter 7, “Color”). To obtain a pixel value that represents any color other than black or white, you will need to allocate the color as described in Chapter 7, “Color”

Convenience functions are also available to change most elements of a GC after it is created. These functions are listed in Sections 5.3 through 5.7, which describe each GC element in detail. Example 5-3 performs the same functions as Example 5-2 but by creating a default GC and then modifying the contents with convenience functions.

Example 5-3. Example of setting default GC then changing it

GC gc;
   .
   .
   .
/* Open display, create window, etc. */
   .
   .
   .
gc = XCreateGC(display, RootWindow(display, screen_num), 0, NULL);
XSetForeground(display, gc, BlackPixel(display,screen_num));
XSetBackground(display, gc, WhitePixel(display,screen_num));
/* Now you can use gc in drawing routines */



Note: You may wonder which of these two ways is more efficient, setting the XGCValues and valuemask, or calling the convenience functions. Actually, there is not much difference, since in both cases, the individual requests to change the same GC are packaged into a single protocol request before being sent to the server. This optimization is implemented by Xlib. The method you should choose is mainly a matter of personal preference.

Also note that Xlib provides the function XFlushGC() to defeat Xlib's caching of GC changes by sending them to the server immediately instead of waiting until the GC is needed. XFlushGC() is used mainly in extensions that have drawing requests which otherwise would not trigger Xlib's cache.

Switching Between Graphics Contexts

One purpose of the GC is to store information about how to interpret graphics requests so that the same information does not have to be sent with every request. Another useful feature of the GC concept is that you can create several GCs with the different characteristics you need and then switch between them. Example 5-4 demonstrates how this is done. It creates two slightly different GCs with swapped foreground and background pixel values.

Example 5-4. Example of switching graphics contexts

GC gc1, gc2;
XGCValues values;
unsigned long valuemask;
   .
   .
   .
/* Open display, create window, etc. */
values.foreground = BlackPixel(display,screen_num);
values.background = WhitePixel(display,screen_num);
gc1 = XCreateGC(display, RootWindow(display, screen_num),
   (GCForeground | GCBackground), &values);
values.foreground = WhitePixel(display,screen_num);
values.background = BlackPixel(display,screen_num);
gc2 = XCreateGC(display, RootWindow(display, screen_num),
   (GCForeground | GCBackground), &values);
/* Now you can use either gc in drawing routines, thereby
 * quickly swapping the foreground and background colors */


Whether it is faster to switch between GCs or to modify a few values of a single GC depends on the particular server implementation. On some types of display hardware, several or many GCs can be cached. On these servers, it is faster to switch between GCs than to change members of them. On servers that do not cache or that cache only one GC, it is faster to change one or two elements of the GC than to switch between two slightly different GCs. There is no way for the application to tell which of these two server types is in use. Therefore, accepted practice is to compromise by creating a small number of GCs (more on this in “GCs and Server Efficiency”).

Now that you know how to create, set, and modify the GC, and how to set up multiple GCs, we can go into more detail about each element of the GC. The following sections describe each member of the graphics context, grouped according to the how they affect the drawing process: Pixel Selection, Coloring and Patterning, and Graphics Tricks.

Controlling Pixel Selection

As previously described, pixel selection can be thought of as the first of four stages in the drawing process. The pixels drawn are selected by a combination of the graphics primitive and various members of the graphics context. This section describes those GC elements.

Line Characteristics

Six of the graphics context components are line characteristics. These components obviously affect the graphics primitives that draw lines, but they also affect those that draw unfilled rectangles, arcs, and polygons. Here are the six line characteristics:

line_width 

Specifies the width of the line in pixels. Zero means to draw using the server's fastest algorithm with a line width of one pixel, with some loss of accuracy.

line_style 

Specifies whether the line is solid in foreground, dashed in foreground, or alternating foreground and background. Possible values are LineSolid, LineOnOffDash, or LineDoubleDash.

cap_style 

Controls the appearance of the ends of a line and in some cases the ends of dashes in a line. Possible values are CapButt, CapNotLast, CapProjecting, and CapRound.

join_style 

Controls the appearance of joints between consecutive lines drawn within a single graphics primitive. Possible values are JoinBevel, JoinMiter, and JoinRound.

dashes 

Specifies a pattern of dash lengths for custom-designed dashed lines. (Used only if the line_style is LineOnOffDash or LineDoubleDash.)

dash_offset 

Specifies the starting point of the dash pattern for custom-designed dashed lines. (Used only if the line_style is LineOnOffDash or LineDoubleDash.)

The line_width, line_style, cap_style, and join_style components can be set using XSetLineAttributes(), while dashes and dash_offset can be set with XSetDashes(). Now we'll describe each of these line characteristics in more detail, followed by an example that sets them.

Line Width

The line_width member of XGCValues is measured in pixels. The line width can be set with XSetLineAttributes().

A line_width greater than or equal to 1 is considered a wide line, and the value 0 is a special case, considered a thin line. Wide and thin lines often use different drawing algorithms. The thin line is intended to be a fast algorithm for drawing a line of width 1 but may not be as uniform as a wide line between different servers.

Wide lines are drawn centered on the path described by the graphics request. A wide line drawn from [x1,y1] to [x2,y2] always draws the same pixels as a wide line drawn from [x2,y2] to [x1,y1], not counting cap and join styles. This is not necessarily the case for thin lines.

Unless otherwise specified by the join or cap style, the bounding box of a wide line with endpoints [x1,y1], [x2,y2] and width w is a rectangle with vertices at the following real coordinates:

Lower Left:    [x1-(w*sin(Θ)/2), y1+(w*cos(Θ)/2)]
Upper Right:   [x1+(w*sin(Θ)/2), y1-(w*cos(Θ)/2)]
Lower Left:    [x2-(w*sin(Θ)/2), y2+(w*cos(Θ)/2)]
Lower Right:   [x2+(w*sin(Θ)/2), y2-(w*cos(Θ)/2)]

where is the angle of the line measured from horizontal.

A pixel is drawn if the center of the pixel is fully inside the bounding box (which is viewed as having infinitely thin edges). If the center of the pixel is exactly on the bounding box, it is part of the line only if the interior of the box is immediately to the pixel's right. Pixels with centers on a horizontal edge are part of the line only if the interior of the box is immediately below the pixel.

Thin lines (line_width == 0) are one-pixel-wide lines drawn using an unspecified, device-dependent fast algorithm. The set of points comprising thin lines will not be affected by clipping.

A wide line of width 1 and a thin line with line_width 0 drawn between the same two points may not be exactly alike. Because of their different drawing algorithms, thin lines may not mix well with wide lines, aesthetically speaking. For precise and uniform results across all displays, use a line_width of 1 rather than 0. If speed is the goal, use a line_width of 0.

Line Style

The line_style member of XGCValues defines which sections of a line are drawn and in which pixel value, as shown in Figure 5-2. The line style can be set with XSetLineAttributes(). The actual length of each dash and gap is set by the dashes member of XGCValues, described in “Dash List and Offset” The constants used to set line_style are as follows:

LineSolid 

Specifies that the full path of the line is drawn using the foreground pixel value.

LineOnOffDash 

Specifies that only the dashes are drawn, with the foreground pixel value, and cap_style applied to the ends of each dash (except that CapNotLast is treated as CapButt for dash ends).

LineDoubleDash 

Specifies that the full path of the line is drawn, dashes with the foreground pixel value, gaps with the background pixel values, and CapButt style always used where dashes and gaps meet.

Figure 5-2. The line styles


Cap Style

The cap_style member of XGCValues defines how the endpoints of lines are drawn, as shown in Figure 5-3. The cap style can be set with XSetLineAttributes(). The constants used to set cap_style are as follows:

CapNotLast 

Is equivalent to CapButt, except that for a line_width of 0 or 1, the final endpoint is not drawn. If specified with line_style LineOnOffDash or LineDoubleDash, the ends of the dashes or where even and odd dashes meet are treated as CapButt.

CapButt 

Specifies that lines will be square at the endpoint with no projection beyond. The end is perpendicular to the slope of the line.

CapRound 

Specifies that lines will be terminated by a circular arc with the diameter equal to the line_width, centered on the endpoint (equivalent to CapButt for line_width of 0 or 1).

CapProjecting 

Specifies that lines will be square at the end but with the path continuing beyond the endpoint for a distance equal to half the line_width (equivalent to CapButt for line_width of 0 or 1).

Join Style

The join_style member of XGCValues defines how corners are drawn for wide lines drawn within a single graphics primitive, as shown in Figures 5-4 and 5-5. The join style can be set with XSetLineAttributes(). The constants used to set join_style are as follows:

JoinMiter 

Specifies that the outer edges of the two lines should extend to meet at an angle. If the angle between the two lines is less than 11 degrees, JoinBevel is used.

JoinRound 

Specifies that lines should be joined by a circular arc with diameter equal to the line_width, centered on the join point.

JoinBevel 

Specifies CapButt endpoint styles, with the triangular notch filled.

Figure 5-3. The line cap (end) styles


Figure 5-4. The line join styles


Figure 5-5. Detail of JoinRound for 8-pixel-wide lines


Dash List and Offset

The dashes member of XGCValues can only be directly set to a single, nonzero value specifying the length in pixels of both the dashes and the gaps. More complicated patterns can be set only with XSetDashes().

In XSetDashes(), the dash_list argument is a real list, with each value representing the length of a single dash or gap in the line. The initial and alternating members of dash_list are the length of the even dashes; the others are the odd dashes (gaps). All members must be nonzero. The length of the dash_list is also an argument to XSetDashes(). The dashes element of XGCValues is equivalent to specifying a two-member dash_list [N, N] in XSetDashes(), where N is the single value specified in XGCValues.dashes.

The dash_offset for XSetDashes() defines the phase of the pattern, specifying how many pixels into the pattern the line should actually begin. Figure 5-6 shows the same line drawn with and without offset to demonstrate its effect.

Example 5-5 shows a code segment that creates and sets the line dashes of five GCs. Figure 5-6 shows the lines that result from drawing with these GCs.

Example 5-5. Code segment specifying five styles of dashed line in five GCs

#define NUMLINES 5
#define DOTTED_LIST_LENGTH 2
#define DOT_DASHED_LIST_LENGTH 4
#define SHORT_DASHED_LIST_LENGTH 2
#define LONG_DASHED_LIST_LENGTH 2
#define ODD_DASHED_LIST_LENGTH 3
void main(argc, argv)
int argc;
char **argv;
{
   GC gca[NUMLINES];
     .
     .
     .
   /* Open display, create windows, etc. */
   set_dashes(gca);
        while (1)  {
                XNextEvent(display, &report);
                switch  (report.type) {
                case Expose:
                        if (report.xexpose.count == 0)
                                draw_lines(win, gca, width, height);
                        break;
        .
        .
        .
}
set_dashes(gca)
GC gca[];
{
   XGCValues gcv;
   int i;
   static int dash_list_length[] = {
      DOTTED_LIST_LENGTH,
      DOT_DASHED_LIST_LENGTH,
      SHORT_DASHED_LIST_LENGTH,
      LONG_DASHED_LIST_LENGTH,
      ODD_DASHED_LIST_LENGTH
   };
   /* Must be at least one element in each list */
   static unsigned char dotted[DOTTED_LIST_LENGTH] =
         {3, 1};
   static unsigned char dot_dashed[DOT_DASHED_LIST_LENGTH] =
         {3, 4, 3, 1};
   static unsigned char short_dashed[SHORT_DASHED_LIST_LENGTH] =
         {4, 4};
   static unsigned char long_dashed[LONG_DASHED_LIST_LENGTH] =
         {4, 7};
   static unsigned char odd_dashed[ODD_DASHED_LIST_LENGTH] =
         {1, 2, 3};
   static unsigned char *dash_list[] = {
      dotted,
      dot_dashed,
      short_dashed,
      long_dashed,
      odd_dashed,
   };
   int dash_offset = 0;
   /* Open display, create window, etc. */
   gcv.line_style = LineOnOffDash;
   for (i = 0 ; i < NUMLINES; i++) {
      gca[i] = XCreateGC(display, RootWindow(display, screen_num),
            GCLineStyle, &gcv);
      XSetDashes(display, gca[i], dash_offset, dash_list[i],
            dash_list_length[i]);
   }
}
draw_lines(win, gca, window_width, window_height)
Window win;
GC gca[];
unsigned int window_width, window_height;
{
        int i;
        for (i=0;i < NUMLINES; i++) {
                XDrawLine(display, win, gca[i],
                        window_width/4, 40 + (10 * i),
                        3 * (window_width/4), 40 + (10 * i));
        }
}


Figure 5-6. Lines drawn with GCs set in Example 5-5


Example of Setting Line Characteristics

Example 5-6 demonstrates how to set the line characteristics with XSetLineAttributes(). This routine and XSetDashes() (which sets dashes, demonstrated in Example 5-5) are the only ways to set line characteristics, other than with XCreateGC() or XChangeGC().

Example 5-6. Setting line characteristics in a GC

set_line_attributes(gc)
GC gc;
{
   unsigned int line_width = 3;    /* 0 would be fast line of width 1 */
   int line_style = LineSolid;     /* If LineOnOffDash or LineDoubleDash,
                                    * must set dashes */
   int cap_style = CapRound;       /* else CapNotLast, CapButt, or
                                    * CapProjecting */
   int join_style = JoinRound;     /* else JoinMiter or JoinBevel */
   XSetLineAttributes(display, gc, line_width, line_style,
           cap_style, join_style);
}


The Font

The font member of a GC specifies which font will be used in text-drawing graphics primitives that use this GC, and can be set with XSetFont(). If the specified font has not been loaded by this client, a graphics primitive that tries to draw text will not fail; it just will not draw. Therefore, you should make sure you load the font.

The X server actually loads a requested font into memory only when XLoadFont() or XLoadQueryFont() is called and if the specified font has not already been loaded by another client. A font is unloaded when the last program using the font exits or unloads it. Duplicate copies of a font are never stored in the server.

There are several ways to deal with fonts. Most programs will use XLoadQueryFont() to load a font and get information about the dimensions of each character. XLoadQueryFont() returns a pointer to an XFontStruct. The font in the GC can then be set to XFontStruct.fid. (See Chapter 6, “Drawing Graphics and Text” for details.)

The default font is always loaded, but it is not the same on all servers. “Loading Fonts” in Chapter 6 describes how a program can find out about the default font on the particular server it is connected to.

Fill Rule

The fill_rule member of XGCValues defines which pixels are drawn for paths given in XFillPolygon() requests. The fill_rule is also an argument to XPolygonRegion(), which is described in “Regions” in Chapter 6 The fill_rule in the GC is set with XSetFillRule(). The fill_rule may be EvenOddRule (the default in the GC) or WindingRule.

As shown in Figure 5-7, EvenOddRule means that if areas overlap an odd number of times, they are not drawn. Technically, it specifies that a point is drawn if an infinite ray with the point as origin crosses the path an odd number of times.

WindingRule, also shown in Figure 5-7, means that overlapping areas are always filled, regardless of how many times they overlap. Technically, this rule specifies that a point is inside the filled area if an infinite ray with the point as origin crosses an unequal number of clockwise- and counterclockwise-directed path segments.

Since polygons are drawn as a series of points connected by lines, the order of the points determines the direction of each line. A clockwise-directed path segment is one which crosses the ray from left to right as observed from the point. A counterclockwise-directed segment is one which crosses the ray from right to left as observed from the point. The case where a directed line segment is coincident with the ray is uninteresting, because you can simply choose a different ray that is not coincident with a segment.

All calculations are performed on infinitely small points, so that if any point within a pixel is considered inside, the entire pixel is drawn. Pixels with centers exactly on vertical boundaries are considered inside only if the filled area is to the right. On horizontal boundaries, the pixel is considered inside only if the filled area is below the pixel.

Figure 5-7. fill_rule constants for filling closed polygons


Arc Mode (for Filling)

The arc_mode member of XGCValues controls filling of arcs drawn with XFillArc and XFillArcs(). The arc_mode is set with XSetArcMode().

An arc is specified for XFillArc or XFillArcs() as follows:

  • The arc is bounded by a rectangle whose center is the center of the arc.

  • The position of the upper-left corner of the rectangle is relative to the origin of the destination drawable.

  • Two angles indicate the starting and stopping position of the arc. These are measured in sixty-fourths of a degree starting from the three-o'clock position, with positive angles indicating counterclockwise measurement.

The meanings of the arc specifications are demonstrated in Figure 6-1.

The arc_mode can be either ArcPieSlice or ArcChord. Figure 5-8 demonstrates the two modes. For ArcChord, the arc and the single line segment joining the endpoints of the arc create a closed figure to fill. For ArcPieSlice, the arc and the two line segments joining the endpoints of the arc with the center point create a closed figure to fill.

Figure 5-8. arc_mode constants for filling arcs


Clip Mask

Clipping allows you to limit the effect of graphics requests to a particular area or to particular pixels of the window or pixmap. The clip_mask member of XGCValues is a bitmap that indicates which pixels of the destination drawable are to be affected by graphics requests. By default, all pixels in the destination drawable are affected.

Pixels not represented by a set bit in the clip mask will not be drawn. The clip_mask can be set with XSetClipMask(), XSetClipRectangles(), or XSetRegion(). XSetClipMask() sets a clip mask composed of an arbitrary set of bits. XSetClipRectangles() specifies an array of rectangles that will collectively be used as a clip mask. XSetRegion() is another way to set the clip mask to a set of rectangles, sometimes more convenient than XSetClipRectangles(). XUnionRectWithRegion() can be used to add the rectangle from an Expose event into a region. Then XSetRegion() sets the GC to clip output to those areas. This is useful for redrawing only the areas that have been exposed. See Example 3-15, which uses this technique. Figure 5-9 shows a rectangular clip_mask, which could be set with XSetClipMask(), XSetClipRectangles(), or XSetRegion().

If the clip_mask is set manually with XSetClipMask() or while creating the GC, a pixmap of depth 1 must be used. Then the only pixels drawn are those for which the clip_mask has a set bit. This pixmap must have the same root as the GC, or a BadMatch error will be generated.

Figure 5-9. Use of clip origin to locate the clip_mask relative to drawable

The clip origin, which places the clip_mask relative to the destination drawable, is specified by two other members of the GC structure: clip_x_origin and clip_y_origin. Figure 5-9 shows how these coordinates specify the upper-left corner of the clip mask relative to the upper-left corner of the destination drawable specified in the graphics request. The origin of the clip_mask can be set with XSetClipOrigin(). The gray area in the figure represents the data to be drawn. The rectangle filled with unshaded squares represents the clip mask, which has all bits set to one. The lighter gray at the bottom shows the area outside the clip mask; this data will not be drawn.

Controlling Coloring and Patterning

The first stage of the drawing process (pixel selection) results in a bitmap with bits set to one indicating the pixels to be drawn. However, a window on a color display (or a pixmap to be copied to a color display) must have multiple bits per pixel to represent colors. The second stage of the drawing process colors the pixels.

There are four ways of coloring the pixels, controlled by the fill_style member of the GC. One of them uses a single color, and the other three apply patterns in different ways. You can pattern anything you can draw, including text, although lines of width 0 are not patterned.

We will begin by discussing the simple case, drawing with only the foreground color using fill_style of FillSolid. Then, to understand the effect of the patterning values for the fill_style, we must digress into a short description of tiles and stipples, followed by a discussion of the three styles of patterning.

Drawing in Foreground Only

Basic drawing is done using the foreground member of the GC. The foreground specifies the pixel value to be applied to the pixels selected by the graphics primitive, when the fill_style is FillSolid. [13] The uses of the background color are restricted and are described in “Drawing in Foreground and Background” You can set the foreground with XSetForeground().

Figure 5-10 shows the use of the foreground pixel value when drawing a character with XDrawString(). We will contrast this later with a string drawn using XDrawImageString(), which will also draw the bounding box with the background pixel value.

Figure 5-10. Use of foreground in XDrawString() character

Now we move on to describe patterning. If you are familiar with tiles and stipples, you can skip Sections 5.4.2 and 5.4.3 and jump to “Fill Style”

Tiles

A tile is a pixmap used to pattern the pixels selected by the first stage of the drawing process. The tile member of the GC can be set with XSetTile().

Tiles are so named because they are laid out next to each other in an array like bathroom tile. The origin of the first tile is specified with ts_x_origin and ts_y_origin, which are relative to the origin of the destination drawable. These members of the GC are set with XSetTSOrigin(). Only pixels specified by set bits in the first stage bitmap are tiled. Figure 5-11 shows how tiles are used to pattern an area. Instead of being filled with a solid color (or shade of gray), the area is filled with the tile pattern.

Figure 5-11. Tiling an area

Creating a tile is described in “Creating Bitmaps, Pixmaps, Tiles, and Stipples” in Chapter 6 The tile pixmap must be created on the same root window and have the same depth as the destination drawable. If these conditions are not satisfied, a BadMatch error is generated. If a pixmap is used simultaneously in a graphics request both as a destination and as a tile, the results are not defined.

Note that on monochrome displays, tiles are often used to simulate different levels of gray. For example, a checkerboard tile of black and white dots will appear gray on the screen. With 4 × 4 tile pixmaps with different arrangements of black and white dots, it is possible to develop several distinguishable levels of gray.

Stipples

Stippling is similar to tiling, except that a stipple is a pixmap of depth 1, not of the depth of the drawable. The pixel values used to draw the pattern are the foreground and background in the GC.

Just like tiles, stipples are laid out starting from the position specified with ts_x_origin and ts_y_origin, which are relative to the origin of the destination drawable.

Creating a pixmap of depth one to be used as a stipple is described in “Creating Bitmaps, Pixmaps, Tiles, and Stipples” in Chapter 6 The stipple pixmap must be created on the same root window and have the same depth as the destination drawable. If these conditions are not satisfied, a BadMatch error is generated. If a pixmap is used simultaneously in a graphics request both as a destination and as a stipple, the results are not defined.

The stipple member of the GC may be changed with XSetStipple(). If both the stipple and tile members of the GC are set, the fill_style determines which is used. Both cannot be used in a single graphics request.

Fill Style

We have demonstrated the simplest case, drawing using the foreground only with fill_style of FillSolid. Now that you know about tiles and stipples, we can describe the values for the fill_style that cause patterning. The fill_style member of XGCValues controls whether the source graphics are drawn with a solid color, a tile, or one of the two techniques using a stipple. The fill_style member of the GC may be changed with XSetFillStyle().

Remember that only the bits that are set to one in the first stage bitmap are affected by coloring or patterning. The fill_style affects all line, text, and fill requests except lines drawn with line_width zero. Possible values are:

FillSolid 

Specifies that graphics should be drawn using the foreground pixel value and in some cases also the background pixel value.

FillTiled 

Specifies that graphics should be drawn using the tile pixmap.

FillStippled 

Specifies that graphics should be drawn using the foreground pixel value masked by stipple. In other words, bits set in the source and stipple are drawn in the foreground pixel value.

FillOpaqueStippled 

Specifies that graphics should be drawn using stipple, using the foreground pixel value for set bits in stipple and the background pixel value for unset bits in stipple.

When the depth of the drawable is one, there is no difference between tiling with fill_style of FillTiled and stippling with fill_style of FillOpaqueStippled.

Figure 5-12 demonstrates the four fill styles demonstrated on small pixmaps.

Figure 5-12. fill_style demonstrated on small pixmaps


Odd dashes (numbering starting from zero) in dotted lines are a special case. For the gaps (odd dashes) in lines with line_style of LineDoubleDash, FillSolid means to draw the gaps in the background pixel value, and FillStippled means to draw in the background pixel value masked by stipple. With a line_style of LineDoubleDash, FillTiled and FillStippled have the effect of wiping out the odd dashes, so that the line looks like LineOnOffDash with the specified fill style.

Drawing in Foreground and Background

The background is used for unset bits in the first stage output in just four situations: when using XDrawImageString() (see “Positioning of Text” in Chapter 6), using XCopyPlane() (see “Copying and Clearing Areas” in Chapter 6), drawing with line_style of LineDoubleDash (see “Line Style”), and with any primitive when the fill_style is FillOpaqueStippled (see “Fill Style”).

Figure 5-13 shows the use of the foreground and background values when drawing a character with XDrawImageString(). This primitive draws both the character and its bounding box. The character itself is drawn in the foreground pixel value; the remainder of the pixels in the bounding box are drawn with the background pixel value.

Figure 5-13. Use of foreground and background in XDrawImageString() character

The background member of the GC is set with XSetBackground().

Tile and Stipple Sizes

A pixmap of any size can be used for tiling or stippling, but on some types of hardware, particular tile or stipple sizes run much faster than arbitrary sizes. XQueryBestSize() returns the closest tile or stipple size to the one you specify and also the largest allowable cursor. XQueryBestTile() and XQueryBestStipple() perform the same functions, but only for tiles and stipples, respectively.

“Creating Bitmaps, Pixmaps, Tiles, and Stipples” in Chapter 6 explains how to create a tile or stipple.

Controlling Graphics Tricks

The GC provides a flexible way to control exactly which planes are affected by graphics requests and how the source and old destination pixel values are used to compute the new destination pixel values. These features are needed only for playing certain tricks like rubber-banding, and nondestructively overlaying graphics. We will demonstrate these techniques later in the book (in Chapters 14 and 7 respectively), but describe the corresponding GC components here.

Example 5-7 shows the types of the logical operation and plane mask components of the GC.

Example 5-7. Members of XGCValues that control combining of source and destination pixels

int function;                   /* Logical function */
unsigned long plane_mask;       /* Plane mask */


The source (result of stage 2 of the drawing process) and existing destination pixels are combined by performing a logical function on the corresponding bits for each pixel. The plane_mask restricts the operation to a subset of planes, so that some bits in the source may be excluded from the computation. The clip_mask restricts the operation to a subset of the pixels, likewise eliminating some pixels from the result.

The source, destination, and plane_mask are combined using the algorithm shown below to yield the new destination pixel values. For each bit in each pixel that has been selected and colored in the first two drawing stages, the following expression defines whether that bit is set in the destination drawable:

((src FUNC dst) AND plane_mask) OR (dst AND (NOT plane_mask))

That is, if the plane_mask bit is set, the source and existing destination pixels are combined using the logical function represented by FUNC. If the plane_mask bit is not set, the existing bit in the destination is not changed.

In the next two sections, we'll look at the actual values that can be specified for these members.

Logical Function

The function member of the GC selects a logical function. Logical functions control how the source pixel values generated by a graphics request are combined with the old destination pixel values already present on the screen or drawable to result in the final destination pixel values. Logical functions are also sometimes called raster operations, raster ops, or display functions. The logical function can be changed by a call to XSetFunction().

The source is the output of a graphics primitive or an area of the screen or drawable (for an XCopyArea()); the destination is the area of the drawable or window that is to receive the output. The 16 logical functions defined in <X11/X.h > are shown in Table 5-2.

Table 5-2. Logical Functions in the GC

Logical Function

Hex Code

Definition

GXclear

0x0

0

GXand

0x1

src AND dst

GXandReverse

0x2

src AND (NOT dst)

GXcopy

0x3

src

GXandInverted

0x4

(NOT src) AND dst

GXnoop

0x5

dst

GXxor

0x6

src XOR dst

GXor

0x7

src OR dst

GXnor

0x8

(NOT src) AND (NOT dst)

GXequiv

0x9

(NOT src) XOR dst

GXinvert

0xa

(NOT dst)

GXorReverse

0xb

src OR (NOT dst)

GXcopyInverted

0xc

(NOT src)

GXorInverted

0xd

(NOT src) OR dst

GXnand

0xe

(NOT src) OR (NOT dst)

GXset

0xf

1

Figures 5-14a, 5-14b, and 5-14c illustrate the effect of three logical functions on a single pixel of an eight-plane screen given a particular set of source and destination pixel values.

Figure 5-14. The effect of logical function GXcopy

GXcopy, the default logical function, is the most frequently used because it copies without reference to the existing destination pixels, with predictable effects on both monochrome and color displays. GXxor and GXinvert are also used quite frequently. Rarely, programs may use other functions in concert with particular planes of a color display. Here is some more detail on the most frequently used logical functions:

GXcopy 

Ignores the bits already in the destination drawable. It is used for both monochrome and color.

GXinvert 

Ignores the source and inverts the old destination. This logical function is used to change black to white and vice versa when modifying only one plane. This can be used for highlighting on monochrome or color screens, but is not as good as GXxor on color screens.

GXxor 

Combines the source and existing bits in such a way that, if the operation is repeated, the drawable is returned to its condition just before the two operations. It is important that these two operations occur without intervening manipulation of the selected bits (for windows, the server should be grabbed but for a very short time). [14] Otherwise, the second XOR operation will not leave the drawable unchanged. GXxor has these properties on both monochrome and color screens.

Figure 5-15. The effect of logical function GXxor


Figure 5-16. The effect of logical function GXand


Plane Mask

The plane_mask member of XGCValues determines which planes of the destination drawable are modified. By default, all planes are modified. The plane_mask can be changed by a call to XSetPlaneMask().

Destination planes represented by a bit set to 1 in the plane_mask can be changed by the graphics primitive, and the other planes cannot. The defined constant AllPlanes() provides a plane_mask with all bits set, which can be used when every plane is to be affected (this is also the default). A plane_mask of 0 cancels the effect of the graphics primitive. A plane_mask with only 1 bit set is useful for highlighting on both color and monochrome displays. Other tricks using the plane_mask are described in Chapter 7, Color. The macro DisplayPlanes() returns the number of planes available on the screen. However, the depth of the window is the upper limit on the number of meaningful bits in the plane_mask. Figure 5-15 illustrates the effect of the plane_mask.

Figure 5-17. The effect of the plane_mask on a 12-plane display


Graphics Exposure

When using XCopyArea() and XCopyPlane() to copy data from one drawable to another, it is possible that certain portions of the source region will be obscured, unmapped, or otherwise unavailable. If this is the case, it may be desirable to generate an event to signal the client that one or more areas in the destination window could not be copied to and should be redrawn some other way.

The graphics_exposures flag in the GC specifies whether or not events should be generated in such a case. There are actually two event types that can be generated if graphics_exposures is set to True:

  • One or more GraphicsExpose events are sent when a destination region cannot be completely drawn because the source region was obscured, unmapped, or otherwise unavailable.

  • A single NoExpose event occurs when the specified source region is completely available.

These event types are not selected by XSelectInput() or in the event_mask attribute; setting graphics_exposures to True is the only way to select them. The graphics_exposures member of the GC can be set with XSetGraphicsExposures().

Figure 5-16 shows a typical XCopyArea() request where the source region is obscured. It shows the areas that would be specified in the GraphicsExpose events generated.

As shown in Figure 5-16, a single XCopyPlane() or XCopyArea() can result in more than one GraphicsExpose event, since the resulting area to be redrawn may be composed of several rectangles. A copy such as the one shown in Figure 5-16 would generate two GraphicsExpose events. One rectangle is specified by each event. If windows A and B are removed and the copy repeated, a single NoExpose event is generated.

When graphics_exposures is False, neither of these events is sent under any circumstances. By default, graphics_exposures is True.

Figure 5-18. Copying a partially unavailable area


Subwindow Mode

The subwindow_mode member of XGCValues controls whether subwindows obscure their parent for purposes of drawing on the parent. This member is set with XSetSubwindowMode().

The value ClipByChildren sets the default condition, in which drawing into the area of a window obscured by its visible children produces no effect.

If the subwindow_mode is set to IncludeInferiors, drawing appears through visible children even when they have opaque backgrounds. The use of IncludeInferiors on a window of depth 1 with mapped inferiors of differing depth is not illegal, but the results are not defined in standard Xlib.

One familiar use of IncludeInferiors is the window manager's “rubber banding” of window outlines while they are being moved or resized. The outline is drawn on the root window with the GC set to IncludeInferiors.

Sharing GCs Between Clients

Despite the fact that a GC is a server resource and theoretically shareable, separate clients should not attempt to share GCs, because of the way GCs are implemented.

GCs and Server Efficiency

Some servers can cache a limited number of GCs in their display hardware. These systems achieve highest performance when the number of GCs created by an application is less than the number that can be cached at one time. Furthermore, each GC takes up some amount of server memory. Therefore, it is a general principle that an application should create as few GCs as reasonably possible.

However, this should not be taken to extremes. For example, all applications could be written to use only one GC, changing it frequently every time different characteristics are needed. But this defeats two of the purposes of the GC, which are to reduce network traffic and simplify programming. There are also performance costs when GCs are changed too often.

Deciding how many GCs to create and when to change them is a trade-off between the benefits of a more efficient server against the benefits of reduced network traffic and simpler programming. The designers of X still think that using a small number of GCs is, overall, the best approach.

Querying the Graphics Context

When you call a number of the GC convenience routines, such as XSetForeground() and XSetLineAttributes(), you might expect each to generate a separate protocol request to change the GC. But this is not what happens. Xlib saves up the changes in an internal structure and makes a single request to the server just before the GC is actually used by a drawing request.

The type GC is a pointer to this internal structure. All Xlib routines use a pointer to this internal structure, not a integer ID, as we have previously implied. However, this fact does not impact how you write Xlib code at all. In practice, a pointer to an opaque structure and an integer ID such as a window ID are treated exactly the same.

In R4, the XGetGCValues() function has been added to allow clients to read Xlib's cache of the fields in each GC. This can save an application from having to maintain its own cache of GC values, when it needs to change the GC in several different places in ways that depend on the current contents.

Note that XGetGCValues() is not a true round-trip query to the server--there is no protocol request that actually asks the server for these values. This has good and bad consequences. The good part is that XGetGCValues() is fast because it is not subject to network delays. The bad side is that the values in Xlib's cache do not include the default values for certain of the GC members. The tile, stipple, and font fields contain invalid IDs when XGetGCValues() is called on a default GC. Therefore, even though there is actually a default font that is always loaded on a server, you cannot use XGetGCValues() to find out its ID. To get information about the default font, pass the default GC to XQueryFont() and it will get information about the default font. Neither is there any obvious reason for needing the IDs of the tile and stipple in the default GC.

Also note that the clip_mask and dashes members of the GC cannot be queried.

The Default GC Versus Default Values of a GC

The server creates one GC, called the default GC, when it starts up. This GC is returned by the XDefaultGC() and the DefaultGC() macro. It contains foreground and background colors that are guaranteed contrasting (but not necessarily black and white), and it contains a default font that is guaranteed to be loaded, but is not necessarily the same font on all servers. The values in the default GC must not be changed.

The default GC can be used in simple applications. But it is not very useful since all applications should provide user customization of fonts and colors, and few can avoid the need to modify other GC components as well.

When you create a GC of your own, its default values are slightly different from the values of the default GC. Its foreground and background values are 0 and 1, respectively, so they are not necessarily black and white or contrasting. That's why you must always set foreground and background when you create a GC. Also, the default font is implementation dependent, and it may not be loaded. Therefore, you must always load the font before attempting to draw with it.

Table 5-3 shows the default values for all members of a graphics context you create.

Table 5-3. The Default Values of a Graphics Context

Component

Value

function

GXcopy

plane_mask

all 1 's

foreground

0

background

1

line_width

0

line_style

LineSolid

cap_style

CapButt

join_style

JoinMiter

fill_style

FillSolid

fill_rule

EvenOddRule

arc_mode

ArcPieSlice

tile

Pixmap filled with foreground pixel

stipple

Pixmap filled with 1 's

ts_x_origin

0

ts_y_origin

0

font

(Implementation dependent)

subwindow_mode

ClipByChildren

graphics_exposures

True

clip_x_origin

0

clip_y_origin

0

clip_mask

None

dash_offset

0

dashes

4 (i.e., the list [4, 4])

A useful quick reference to the graphics context is provided inside the back cover of Volume Two.



[11] I am indebted to Ollie Jones of PictureTel Corp. for the idea of thinking of the graphics context as affecting several independent stages in drawing, which he calls the “graphics pipeline.”

[12] For XCopyArea() this second stage is skipped, since the pixels taken from the source drawable must already have the same depth as the destination drawable.

[13] For practical purposes, you can loosely think of a pixel value as the “color” in which an object will be drawn, though it applies to both color and monochrome systems. Even on a color system, the actual color resulting from the specified foreground or background pixel value will depend on the plane mask and logical function, as well as the red, green, and blue values stored in the colormap entry to which the resulting value points! Later references in this chapter to drawing in the “foreground color” should be interpreted in this light.

[14] When the server is grabbed, the client that grabbed it has sole control over the server and the screen. All other clients are put on hold; the server saves up events queued for them and does not change the screen on their behalf until the server grab is released. The server is grabbed and released with XGrabServer() and XUngrabServer(), but this should be done only when really necessary.