Chapter 6. Drawing Graphics and Text

This chapter describes the routines used to draw lines, geometrical figures, and text. It also discusses the use of the pixmaps, images, and regions. You should be familiar with the use of the graphics context before attempting to use these routines.

Drawing with computers is a little like drawing by hand. Holding the pencil is not hard, but getting anything recognizable to appear on the page is a different matter. Similarly, drawing with X is quite easy, but designing what to draw and where can be a challenge. We can do little more in this chapter than tell you how to hold the pencil; the rest is up to you.

This chapter describes various techniques that have to do with drawing: drawing lines, rectangles, and arcs; using bitmaps; placing and drawing text; using regions; creating and using cursors; and using images.

The draw_text and draw_graphics routines called in the basicwin program in Chapter 3, “Basic Window Program” are used as examples in this chapter. Also described here are various versions of the draw_box routine, which is called in the simple window manager winman described in Chapter 16, “Window Management”

Note that, before you draw anything, you must set up a graphics context to specify, at minimum, the foreground and background pixel values for drawing and the font if you are drawing text. For monochrome applications, you should set these values using the BlackPixel() and WhitePixel() macros described in Chapter 3, “Basic Window Program” For color applications, you should use one of the color allocation routines described in Chapter 7, “Color” While the default foreground and background values in a GC may work on some servers, they are hardcoded (0 and 1) and should not be relied upon by any client, since they will give inconsistent results on color displays.

Drawing

The X drawing primitives are easy-to-use routines capable of drawing points, connected lines (polylines), disconnected lines (disjoint polylines), rectangles, and circles, ellipses, or arcs. Separate primitives are provided that fill rectangles, polygons, circles, ellipses, and arcs.

These primitives select the source pixels that will be operated on according to the graphics context. The GC is described in Chapter 5, The Graphics Context. The most common error generated while drawing is BadMatch. If you get this error, it means the drawable and the GC specified in the drawing call are not the same depth. The safest way to prevent this is to always create the drawable first, and then use the drawable as an argument when creating the GC that will be used to draw into it.

XDrawPoint() requires only the coordinates of the point to be drawn. XDrawPoints() requires a pointer to an array of coordinates for the points, the number of points, and a mode flag which controls whether the coordinates are interpreted relative to the origin of the drawable or relative to the previous point drawn.

XDrawLine() is similar to XDrawPoint() but requires two points, a beginning and an end. XDrawLines() works just like XDrawPoints() but draws lines between consecutive points in the list. If the first and last points coincide, the lines will be joined properly according to the join_style in the GC.

XDrawSegments() draws lines that are not necessarily connected end to end. It requires an array of pairs of endpoints. There is no mode flag for XDrawSegments(), so the coordinates are always relative to the origin of the drawable. If the end point of one segment and the beginning of the next coincide, the lines will be joined according to the join_style in the GC. The remaining end points will be drawn according to the cap_style in the GC.

XDrawRectangle() draws the outline of a rectangle when given the upper-left corner and the height and width. XDrawRectangles() draws multiple rectangles from an array of corner coordinates and dimensions. The actual width and height of a rectangle is one pixel larger than the dimensions specified, according to the X protocol, as shown in Figure 6-2. These actual dimensions maintain consistency with the definition of a filled rectangle or a clipping region, which are exactly the size specified. The corners of rectangles are drawn according to the join_style in the GC.

XDrawArc() is similar to XDrawRectangle(), except that it draws an arc that fits inside the rectangle. This function can draw circles and ellipses (or parts thereof) whose axes are parallel to the window coordinates. An elliptical arc occurs if the rectangle is not a square. The extent of the arc is specified by two angles: the first is the starting angle relative to the three-o'clock position, and the second is the angle relative to the starting position. The angles are signed integers in sixty-fourths of a degree (0 to 360 * 64 is a complete circle), with positive values drawing the arc counterclockwise. This scale factor is required so that angles can be specified more accurately than allowed by integral values between 0 and 360 degrees. Figure 6-1 demonstrates the arguments needed for XDrawArc().

Figure 6-1. Angle measurement for XDrawArc or XDrawArcs()

X Version 11 also supports the XDraw() and XDrawFilled() routines that were available in X Version 10, though the performance of these is low. These routines are described in Appendix B, X10 Compatibility.

Xlib does not provide routines for drawing Bezier or cubic spline curves.

The Request Size Limit

One caveat of all the Xlib routines that draw multiple objects is that there is a maximum number of objects that can be drawn with a single call, and this number varies according to the server your application is connected to. In Release 4, this affects the calls XDrawArcs(), XDrawLines(), XDrawPoints(), XDrawRectangles(), XDrawSegments(), XDrawText(), XDrawText16(), XFillArcs(), and XFillRectangles(). In Release 5 and later, it affects only XDrawArcs(), XDrawLines(), XDrawText(), and XDrawText16(), because the other calls are divided into multiple requests. (This cannot be done for XDrawArcs() or XDrawLines() because this would disturb the server's joining of the lines.)

To determine how many objects you can draw in a single call, you find out your server's maximum request size using XMaxRequestSize(). Subtract 3, and this is the maximum number of points you can draw in a single XDrawPoints() request. You can draw one-half this many lines, segments, or rectangles, and one-third this many arcs.

For XDrawText(), XDrawText16(), XwcDrawText(), and XmbDrawText(), which draw a series of strings as will be described later, the maximum number is based on the number and length of these strings.

Scaling Graphics

All drawing measurements in X are made in pixels. The positions you specify are relative to the origin (upper-left corner inside border) of the window specified in the drawing request. The width and height of a rectangle or bounding box for an arc are also specified in pixels.

Scaling based on pixels has a weakness caused by the fact that pixels are not always the same size on the screen. Imagine a desktop publishing application. Its goal is to make everything drawn on the screen as close as possible to what will appear on the printed page. People may run the application from a PC which has a 9.5" by 7.25" screen with an 640 by 480 array of pixels or from a workstation which has a 13.5" by 10.5" screen with an array of, perhaps, 1152 by 900 pixels. The ruler lines drawn by the application would look much different on the two screens if their sizes were not adjusted accordingly. The application should calculate the ratio of the size in millimeters of the screen to its size in pixels, in both directions. The required information is returned by the DisplayHeight(), DisplayHeightMM(), DisplayWidth(), and DisplayWidthMM() macros.

This correction of size distortion also solves a second, smaller problem. The relative density of pixels in the x and y directions on the screen may vary. For example, a square drawn with equal width and height may appear rectangular on the screen, since some (but, fortunately, not many) screens have more space between rows of pixels than between columns. By correcting for size variation, this problem goes away. It is also possible to allow size variations but correct for the aspect ratio distortion by multiplying the height measurements in pixels by the ratio:


     DisplayHeight/DisplayHeightMM()
     _______________________________
      DisplayWidth/DisplayWidthMM()

or by multiplying the width measurements in pixels by the inverse of this ratio. Do not multiply both the width and height measurements.

Example of Drawing Graphics

All drawing routines are used in essentially the same way:

  • First, create and set the graphics context.

  • Then calculate the dimensions and placement of what you want to draw.

  • Finally, do the actual drawing.

Example 6-1 shows a routine named draw_graphics that places and draws a rectangle. As you can tell from the brevity of the routine, most of the trouble goes into setting the GC properly and positioning the item to be drawn. The actual drawing is very simple.

This routine is called from the basicwin program described in Chapter 3, “Basic Window Program” By the time it is called, we have already done many things. The display is opened, windows and resources created (including the GC), and window manager hints set. Most importantly, draw_graphics is called only in response to Expose events. It is used to draw the window for the first time and to redraw the contents of areas exposed later.

Example 6-1. The draw_graphics routine

draw_graphics(win, gc, window_width, window_height)
Window win;
GC gc;
unsigned int window_width, window_height;
{
        int x, y;
        unsigned int width, height;
        height = window_height/2;
        width = 3 * window_width/4;
        x = window_width/2 - width/2;  /* Center */
        y = window_height/2 - height/2;
        XDrawRectangle(display, win, gc, x, y, width, height);
}


The calling routine gets the window_width and window_height arguments from ConfigureNotify events because the window being drawn into is a top-level window which might get resized by the window manager. Routines to draw into descendents of the top-level window may also require size arguments if the sizes of the windows will be adjusted in response to a resized top-level window.

Filling

The XFillArc(), XFillArcs(), XFillPolygon(), XFillRectangle(), and XFillRectangles() commands act much like the drawing routines described at the start of “Drawing” except that they fill an area instead of drawing the outline.

Surprisingly, the filling and drawing versions of the rectangle routines do not draw the same outline if given the same arguments. The routine that fills a rectangle draws an outline one pixel shorter in width and height than the routine that just draws the outline, as shown in Figure 6-2. It is easy to adjust the arguments for the rectangle calls so that one draws the outline and another fills a completely different set of interior pixels. Simply add 1 to x and y and subtract 1 from width and height.

The XFillPolygon() routine is somewhat different from the other filling routines, since there is no directly analogous routine that draws a polygon with lines (though XDrawLines() can be used to draw a polygon). Like the other routines, XFillPolygon() uses an array of points to specify the nodes to be connected, but it connects the first and last points to form a closed figure and then fills the resulting shape. The shape flag (which can be one of the symbols Complex, Convex, or Nonconvex) is a hint that may enable the server to improve the performance of the filling operation. The mode argument indicates whether the coordinates of the vertices are interpreted relative to the origin of the drawable or relative to the previous point.

The fill_rule member of the GC controls how complex, self-intersecting polygons are filled. The WindingRule setting of the fill_rule specifies that overlapping areas of a polygon drawn in a single call are filled. With EvenOddRule, areas overlapping an odd number of times are not filled. See “Fill Rule” in Chapter 5 for more information.

Figure 6-2. The pixels affected by XFillRectangle() vs. XDrawRectangle() with the same arguments


Creating Bitmaps, Pixmaps, Tiles, and Stipples

Bitmaps, tiles, and stipples are all forms of pixmaps, all of type Pixmap. Applications often need to create pixmaps for icon patterns, cursors, and tiles.

The data used to create a pixmap for any purpose can be included in a program at compile time or read in at run time. In both methods, you must have a bitmap file created with XWriteBitmapFile() or the bitmap application.

In the first method, you use an #include statement to read the bitmap file at run time and then call XCreateBitmapFromData() or XCreatePixmapFromBitmapData() if you want a pixmap with depth for a window background or a tile.

In the second method, you create a single-plane Pixmap with XCreatePixmap() and call XReadBitmapFile() to fill the Pixmap with the data from the file. Then if you want a pixmap with depth for the background of a window or for a tile, you can create another pixmap of the desired depth and call XCopyPlane() to copy the bitmap into the pixmap. Normally, an application would choose reading the data from a file if the user needs to be able to change the bitmap between invocations of the client.

XWriteBitmapFile() can be used to write the contents of a bitmap into a file conforming to X Version 11 bitmap file format.

Example 6-2 shows some bitmap data in standard X11 bitmap file format and two subroutines, one that creates a stipple from included data and the other that reads the bitmap data from a file.

Example 6-2. Creating a stipple from included data and from data read from a file

#define icon_bitmap_width 40
#define icon_bitmap_height 40
static char icon_bitmap_bits[] = {
   0xc3, 0xc3, 0x7f, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
   0x00, 0x00, 0x80, 0x38, 0x00, 0x40, 0x00, 0x80, 0x24, 0x00, 0x00, 0x00,
     .
     .
     .
   0x0c, 0x30, 0x18, 0x00, 0x84, 0x04, 0x60, 0x0e, 0x00, 0xdc, 0x02, 0x80,
   0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00};
void main(argc, argv)
int argc;
char **argv;
{
     .
     .
     .
   Pixmap stipple;
   unsigned int stip_width, stip_height;
   char *filename = "bitmaps/icon_bitmap";
   if (create_included_stipple(&stipple, &stip_width,
         &stip_height) == False)
       fprintf(stderr, "basic: couldn't create included bitmap\n");
   printf("stipple is %dx%d\n", stip_width, stip_height);
   if (create_read_stipple(&stipple, filename, &stip_width,
         &stip_height) ! = BitmapSuccess)
       fprintf(stderr, "basic: can't read bitmap\n");
   printf("stipple is %dx%d\n", stip_width, stip_height);
     .
     .
     .
}
create_included_stipple(stip, width, height)
Pixmap *stip;                  /* Returned created stipple */
unsigned int *width, *height;  /* Returned */
{
   if ((*stip = XCreateBitmapFromData(display,
         RootWindow(display, screen_num), icon_bitmap_bits,
         icon_bitmap_width, icon_bitmap_height))
         == False)
      return(False);
   *width = name_width;
   *height = name_height;
   return(True);
}
create_read_stipple(stip, filename, width, height)
Pixmap *stip;                  /* Returned created stipple */
char *filename;
unsigned int *width, *height;  /* Returned */
{
   int value;
   int x_hot, y_hot;           /* Don't care about these unless for
                                * cursor */
   value = XReadBitmapFile(display, RootWindow(display, screen_num),
         filename, width, height, stip, &x_hot, &y_hot);
   if (value == BitmapFileInvalid)
      fprintf(stderr, "Filename %s contains invalid bitmap data\n",
            filename);
   else if (value == BitmapOpenFailed)
      fprintf(stderr, "Filename %s could not be opened\n",
            filename);
   else if (value == BitmapNoMemory)
      fprintf(stderr, "Not enough memory to allocate pixmap\n");
   return(value);
   /* Returns BitmapSuccess if everything worked */
}

To create a pixmap with depth from included data, you can substitute XCreatePixmapFromBitmapData() for XCreateBitmapFromData() in the example above. However, to create a pixmap with depth from data read from a file, you must create a bitmap with XReadBitmapFile() as shown above, then create a pixmap with depth using XCreatePixmap(), then copy from the bitmap to the pixmap using XCopyPlane().

Copying and Clearing Areas

XClearWindow() clears an entire window. If the window has a background_ pixmap attribute, then the window is redrawn with this tile. If the window has background_pixmap or background_pixel attribute None, then the contents of the window are not changed. No exposure events are generated by XClearWindow(), since the usual intent of this command is to clear the window, not to refresh the old contents (which would be the normal response to an exposure event). Conversely, XClearWindow() is not needed to clear a window before redrawing it due to an Expose event, because the server automatically draws the exposed area with the background pixel value or pixmap.

XClearArea() is like XClearWindow() but acts on a particular area within a window defined by the call's x, y, height, and width arguments. If the height or width argument is 0, then some special rules take effect that clear an area to the right and/or the bottom of the window, as shown in Figure 6-3.

Figure 6-3. XClearArea() -- area cleared with various width and height arguments

If the width argument is 0, the left edge of the cleared area is x and the right edge is the right border of the window. If the height is 0, the top is y and the bottom is the bottom of the window. If both height and width are 0, then the area between x and y and the bottom and right sides of the window are cleared. The exposures argument indicates whether an Expose event is generated on the cleared area.

XCopyArea() is used for many purposes, including copying off-screen pixmaps to the screen and copying one screen area to another. You need to specify the source and destination drawables, the upper-left corner of the source and destination locations, and the width and height of the area. Note that the source and destination drawables must have the same depth, or an error occurs.

Areas of the source that are not visible, not preserved in the backing store, or outside the boundaries of the source drawable are not copied. If the destination has a background tile attribute other than None, the destination areas corresponding to the uncopyable areas of the source are filled or tiled according to the background attributes.

The operation of XCopyPlane() is quite different from XCopyArea(). A single plane of the source region is given “depth” by “coloring” it with the foreground and background pixel values from the GC, before being written into the destination drawable. In other words, set bits in the source plane are given the foreground pixel value in the destination drawable, while unset bits are given the background pixel value. Therefore, XCopyPlane() is useful for translating a pixmap of depth 1 (a bitmap) into a pixmap of the same depth as a window where it can be displayed. If the graphics_exposures member of the GC is True, then one or more GraphicsExpose events are generated on the destination region when part of the source region could not be copied or a single NoExpose event is generated if all the source region could be copied. This is the case for both XCopyArea() and XCopyPlane() requests.

That's about all there is to say about simple drawing, filling, copying, and clearing. Now we'll move on to drawing text.

Fonts and Text

A font in X is a set of bitmaps and may represent text, a set of cursor shapes, or perhaps some other set of shapes for some other purpose.

The following sections describe the character format, how to load fonts, character metrics, the XFontStruct and XCharStruct structures, placing text, font properties, and more.

Character Format

Every X function that draws text has two versions: one that handles single-byte (8-bit) fonts and one for two-byte (16-bit) fonts. The difference between these two is that a single-byte font is limited to 256 characters, while a two-byte font may have up to 256 rows each with 256 characters, a total of 65,536 characters. Large numbers of characters are necessary for Oriental languages.

On many servers, only single-byte fonts can be used with the routines whose names do not end in 16 and only two-byte fonts may be used with the routines that do end in 16. However, some servers may handle either type in either routine. At the moment, there is only one two-byte font on the standard X distribution, the Kanji font used by the kterm program, a terminal emulator for Japanese.

Loading Fonts

A font must be loaded before being used. If one or more clients are using the same font, they share the same copy in the server, but each must request that the font be loaded, if only to get the font ID. The available fonts are stored in a database that is accessible with the XListFonts() and XListFontsWithInfo() commands.

XListFonts() lists the fonts that match the specified pattern (with wildcards) that are available on the current server. The list of font names generated by XListFonts() can be freed when no longer needed using XFreeFontNames(). See the next section for how to specify font names.

Once the desired font name is found, it can be used as a string in XLoadFont(). Some fonts, such as “fixed” and “9x15,” are almost always available and should not require a search through the list of fonts. The XLoadFont() command loads a font and returns the font ID, which is used in all subsequent references to that font. The font ID is used in XSetFont() to associate the font with a GC to be used in drawing text. XLoadFont() returns a value that must be checked to make sure the loading succeeded.

If the font is constant width, then it is ready for use as soon as it is loaded. If the font is proportionally spaced and your program needs to calculate the extent of many strings in the same font, then you may want to get the table of the extents of the font characters and perform this calculation locally in order to save repeated round-trip requests to the server. This information is stored in an XFontStruct (described in “The XFontStruct Structure”), which is filled by calling the XQueryFont() routine. Both the XLoadFont() and XQueryFont() operations may be done together with XLoadQueryFont().

If the font ID passed to the XQueryFont() routines is of type GContext, the information about the font associated with the specified GC is returned. This is how you get information about the default font, which is always loaded. Pass the value returned by

XGContextFromGC(DefaultGC(display, screen_num))

to XQueryFont().

The load_font routine shown in Example 6-3 is called in the basicwin program described in Chapter 3, “Basic Window Program” It loads a font and gets the font information structure for later use in the routines that actually draw the text.

Example 6-3. The load_font routine

load_font(font_info)
XFontStruct **font_info;
{
   char *fontname = "9x15";
   /* Access font */
   if ((*font_info = XLoadQueryFont(display,fontname)) == NULL)
   {
      (void) fprintf( stderr, "Basic: Cannot open 9x15 font\n");
      exit( -1 );
   }
}


In a more general client, the font name should be an argument to load_font, and provision should be made to read it from the command line or resource database.

XListFontsWithInfo() returns a list of the loaded fonts matching a font name (with wildcards) and returns the information structure associated with each loaded font. The information returned is identical to that returned by XQueryFont() except that per-character metrics are not returned. Only the maximum metrics over the entire font are returned. If XFontStruct.min_byte1 and XFontStruct.max_byte1 are 0, the font is a single-byte font.

XFreeFontInfo() should be used to free the font information structure when the font is no longer needed but before the font is unloaded using XUnloadFont(). XFreeFont() combines XFreeFontInfo() and XUnloadFont().

Font Naming

Your application should allow font names to be specified by the user using resources. However, you do need to specify default fonts. Font naming is defined by the X Logical Font Description convention, known as XLFD. The complete XLFD is presented in Volume Zero. However, the basics are covered in Appendix A, Specifying Fonts.

Character Metrics

Before going on to the structures that specify characters and fonts, we should go over some terminology. The measurements shown in Figure 6-4 are some of the font metrics that are the measurements in pixels that describe both a font as a whole and each character in the font. The names shown for the metrics are members of the font information structures.

Notice that the origin is not at the upper-left corner of each character, as in most of the rest of X. The origin of each character is on the baseline, which is a row of pixels somewhere near the lower middle of a line of text. This part of X has been written to conform closely to the existing standards for fonts provided by companies like Adobe.

Notice that two structures are mentioned in Figure 6-4, XFontStruct and XCharStruct. XFontStruct holds information about the entire font, while XCharStruct (itself the type of several members of XFontStruct) holds information about a single character. These two structures have some common member names, but their meanings are different.

There is a difference between the font ascent and descent members in XFontStruct and the ascent and descent members in each individual XCharStruct. The former specifies the largest of each measurement in any character in the font, and the latter specifies the measurements of single characters.

The XCharStruct Structure

One XCharStruct structure contains the metrics of a single character in a font. XCharStruct is shown in Example 6-4. Refer to Figure 6-4 for the meaning of each of its members.

Example 6-4. The XCharStruct structure

/* Per character font metric information */
typedef struct {
   short lbearing;              /* Origin to left edge of character */
   short rbearing;              /* Origin to right edge of character */
   short width;                 /* Advance to next char's origin */
   short ascent;                /* Baseline to top edge of character */
   short descent;               /* Baseline to bottom edge of
                                 * character */
   unsigned short attributes;   /* Per char flags (not predefined) */
} XCharStruct;


Figure 6-4. The metrics of two characters

The attributes member is for font-specific information. It does not have any standard use or meaning.

The XFontStruct Structure

Example 6-5 shows the XFontStruct structure. This structure contains information about the font as a whole.

Example 6-5. The XFontStruct structure

typedef struct {
   XExtData *ext_data;           /* Hook for extension to hang data */
   Font fid;                     /* Font ID for this font */
   unsigned direction;           /* Direction the font is painted */
   unsigned min_char_or_byte2;   /* First character */
   unsigned max_char_or_byte2;   /* Last character */
   unsigned min_byte1;           /* First row that exists (for two-byte
                                  * fonts) */
   unsigned max_byte1;           /* Last row that exists (for two-byte
                                  * fonts) */
   Bool all_chars_exist;         /* Flag if all characters have nonzero
                                  * size */
   unsigned default_char;        /* Char to print for undefined character */
   int n_properties;             /* How many properties there are */
   XFontProp *properties;        /* Pointer to array of additional
                                  * properties*/
   XCharStruct min_bounds;       /* Minimum bounds over all existing char*/
   XCharStruct max_bounds;       /* Maximum bounds over all existing char*/
   XCharStruct *per_char;        /* first_char to last_char information */
   int ascent;                   /* Max extent above baseline for spacing */
   int descent;                  /* Max descent below baseline for spacing */
} XFontStruct;


XFontStruct includes three members of type XCharStruct: one describes the smallest measurement for each character metric among all the characters in the font; one describes the largest; and one points to a list of structures, one for every character in the font. Note that the minimum character metrics (min_bounds) do not describe the smallest character in the font, but the smallest of every measurement found anywhere in the font. The same goes for max_bounds.

The following list describes in detail each member of the XFontStruct structure. Only font developers need to learn all these members. In general, an application programmer will use only the ascent and descent members and occasionally the min_bounds, max_bounds, min_byte1, and max_byte1 members. These members are placed first so you can just scan the rest if you are interested. Refer back to Figure 6-4 for a visual representation of ascent and descent.

  • The min_bounds and max_bounds are structures containing the minimum and maximum extents of the characters in the font, ignoring nonexistent characters. The bounding box of the font (the smallest rectangle that could contain any character bitmap in the font), by superimposing all of the characters at the same origin (specified by x, y), has its upper-left coordinate at:

    [x + min_bounds.lbearing, y - max_bounds.ascent]
    

    The bounding box's width is:

    max_bounds.rbearing - min_bounds.lbearing
    

    Its height is:

    max_bounds.ascent + max_bounds.descent
    
  • ascent is the logical extent of the font above the baseline and is used for determining line spacing. Specific character bitmaps may extend beyond this ascent.

  • descent is the logical extent of the font below the baseline and is used for determining line spacing. Specific character bitmaps may extend beyond this descent. If the baseline is at absolute y coordinate y, then the logical extent of the font is between the y coordinates (y-XFontStruct.ascent) and (y+XFontStruct.descent-1), inclusive.

  • direction can be either FontLeftToRight or FontRightToLeft. This member is a hint about whether most XCharStruct members have a positive (FontLeftToRight) or a negative (FontRightToLeft) character-width metric, indicating the preferred direction of drawing the font.

  • min_byte1 and max_byte1 are both 0 for single-byte fonts, since the second byte is not used. These members can be tested to see if a font is single- or two-byte. If single-byte, min_char_or_byte2 specifies the index of the first member of the per_char array and max_char_or_byte2 specifies the index of the last member.

    min_byte1 and max_byte1 represent the first and last rows that exist in the font. There may be up to 256 rows in a font, but no normal font is likely to need all 256 rows (256 * 256 characters). For two-byte fonts, both min_char_or_byte2 and max_char_or_byte2 will be less than 256, and the two-byte character index values corresponding to per_char array member N (counting from 0) are:

    byte1 = N/D  min_byte1           /* Row offset */
    byte2 = N%D  min_char_or_byte2   /* Column offset */
    

    where:

    D   =   number of characters per row
                        (max_char_or_byte2 - min_char_or_byte2 + 1)
    /       =    integer division
    %     =    integer modulus


  • If the per_char pointer is NULL, then all glyphs (characters in the font) between the first and last character, inclusive, have the same extent and other information, as given by both min_bounds and max_bounds.

  • If all_chars_exist is True, then all characters in the per_char array have nonzero bounding boxes.

  • default_char specifies the index that will be used when an undefined or nonexistent index is used. default_char is a single-byte character. For a font using two-byte matrix format, default_char has byte1 in the most significant byte and byte2 in the least significant byte. If default_char itself specifies an undefined or nonexistent character, then no printing is performed for undefined or nonexistent index values.

The XFontProp member of XFontStruct is provided to allow additional properties (over and above the predefined properties) to be associated with a font. See “Font Properties” for a description of predefined and additional font properties.

Positioning of Text

All the routines that draw text require the same basic techniques for positioning text on the screen.

Let's consider a string drawn with XDrawImageString(). XDrawImageString() draws the entire rectangle described by the max_bounds of the font, with the character drawn in the foreground pixel value and the rest drawn in the background pixel value (both from the GC). Figure 6-5 demonstrates the drawing of three strings. The origin of the baseline of each text line is specified in the XDrawImageString() call. The offset of the first line of text in Figure 6-5 is (20 + ascent). Subsequent lines are placed (ascent + descent) below the origin of the first line. For routines other than XDrawImageString()*, [15] these coordinates still position the background rectangle even though that rectangle is not filled.

  • If you want the upper-left corner of the background rectangle to be at pixel coordinate (x,y), then pass (x, y+ascent) as the baseline origin coordinates to the text drawing routines, where ascent is the font ascent as given in XFontStruct.

  • If you want the lower-left corner of the background rectangle to be at pixel coordinate (x,y), then pass (x, y-descent+1) as the baseline origin coordinates to the text routines, where descent is the font descent as given in XFontStruct.

Figure 6-5. The vertical positioning of strings

It is important to find out how wide a given string is going to be in the chosen font. This width must be smaller than the width of the drawable if you want to be able to read the end of the text!

Listed below are several routines that return either a string width or its extent (both width and height). Both types of routines return the width of the specified string in pixels. The routines that return an extent also provide vertical size information in the form of ascent and descent measurements for the particular string in question and for the font as a whole.

XTextWidth() and XTextWidth16() 

Return the width in pixels of a string in a particular font.

XTextExtents() and XTextExtents16() 

Return string and font metrics, which include the width and height of the bounding box containing the string in the specified font. Use these routines if making repeated calls with the same XFontStruct.

XQueryTextExtents() and XQueryTextExtents16() 

Perform the same function as XTextExtents() and XTextExtents16(), but they query the server instead of requiring a filled XFontStruct and performing the computation locally. Use these routines if you only need to calculate metrics once (or so) for a given font.

To position text vertically using the returned extents, normally you should use the font ascent and descent (rather than the string ascent and descent) if you will be drawing other strings that you want lined up. If you are seriously pressed for space, it is possible to save a few pixel rows with certain strings by using the string ascent and descent measurements.

Whether you center, left justify, or right justify text is completely up to you. The only crucial test is to see that there is enough room for the height and width of the string at the chosen position.

Text-drawing Routines

The following routines draw text into a drawable:

XDrawString() and XDrawString16() 

Draw a string into a drawable. They require only the string, its length, and the position of the baseline origin. The font in the GC is used both as a source for the graphics operation and as a clip mask, so that pixels in the destination drawable that are not in each font character are not drawn.

The internationalized versions of these functions are XmbDrawString() and XwcDrawString() (new in R5).

XDrawImageString() and XDrawImageString16() 

Act just like XDrawString() and XDrawString16() except that the bounding box around the text string is filled with the background pixel value defined in the GC. This avoids annoying flicker on many screens in clients that do a lot of redrawing, such as editors and terminal emulators. These routines are very useful when you need to be able to highlight the text for selections or to indicate that a menu choice has been made, because the foreground and background of the GC can be swapped to redraw the text highlighted. Using the other text routines to do this requires changing the background attribute of the window or copying the entire area to itself with a logical function of GXinvert. The function and fill_style in the GC are ignored for this request, but they are effectively GXcopy and FillSolid.

The internationalized versions of these functions are XmbDrawImageString() and XwcDrawImageString() (new in R5).

XDrawText() and XDrawText16() 

Can draw one or more strings to the screen using one XTextItem structure for each string. Each structure contains the string of text to be drawn, specifies what font to use, and provides a horizontal offset (the delta member) from the end of the last item of text. A font member other than None causes the font to be stored in the specified GC; otherwise, the font in that GC is used.

Accented or overstruck characters can be drawn in this manner. These functions can also be used to draw complex arrangements of text in one call instead of having to call XDrawString() several times, changing the position, text, and font in between each call.

The internationalized versions of these functions are XmbDrawText() and XwcDrawText() (new in R5).

Example 6-6 displays the XTextItem structures used by XDrawText() and XDrawText16().

Example 6-6. The XTextItem and XChar2b structures

typedef struct {
   char *chars;           /* Pointer to string */
   int nchars;            /* Number of characters */
   int delta;             /* Delta between strings */
   Font font;             /* Font to print it in, None don't change */
} XTextItem;
typedef struct {
   XChar2b *chars;        /* Two-byte characters */
   int nchars;            /* Number of characters */
   int delta;             /* Delta between strings */
   Font font;             /* Font to print it in, None don't change */
} XTextItem16;
typedef struct {          /* Normal 16-bit characters are two bytes */
   unsigned char byte1;
   unsigned char byte2;
} XChar2b;


The font member of XTextItem specifies the font to be used to draw the string in the chars member and is stored in the GC for use in subsequent text requests.

The delta member specifies a change in horizontal position before the string is drawn. The delta is always added to the character origin and is not dependent on the draw direction of the font. For example, if x = 40, y = 20, and items[0].delta = 8, then the string specified by items[0].chars would be drawn starting at x = 48, y = 20. If items[0].chars pointed to two characters with a combined width of 16 pixels, the next delta, items[1].delta, would begin at x = 64. The next text item would begin at the end of this delta. The delta member can also be used to backspace for overstriking characters.

The draw_text Routine

Example 6-7 shows the draw_text routine, called from the basicwin program described in Chapter 3, “Basic Window Program” draw_text draws three strings in different locations in the window. It demonstrates how to calculate the vertical position of a string using the font ascent.

Example 6-7. The draw_text routine

draw_text(win, gc, font_info, win_width, win_height)
Window win;
GC gc;
XFontStruct *font_info;
unsigned int win_width, win_height;
{
   char *string1 = "Hi! I'm a window, who are you?";
   char *string2 = "To terminate program, press any key";
   char *string3 = "or button while in this window.";
   char *string4 = "Screen Dimensions:";
   int len1, len2, len3, len4;
   int width1, width2, width3;
   char cd_height[50], cd_width[50], cd_depth[50];
   int font_height;
   int initial_y_offset, x_offset;
   /* Need length for both XTextWidth and XDrawString */
   len1 = strlen(string1);
   len2 = strlen(string2);
   len3 = strlen(string3);
   /* Get string widths for centering */
   width1 = XTextWidth(font_info, string1, len1);
   width2 = XTextWidth(font_info, string2, len2);
   width3 = XTextWidth(font_info, string3, len3);
   /* Output text, centered on each line */
   font_height = font_info->ascent + font_info->descent;
   /* Output text, centered on each line */
   XDrawString(display, win, gc, (win_width - width1)/2,
         font_height, string1, len1);
   XDrawString(display, win, gc, (win_width - width2)/2,
         (int)(win_height - (2 * font_height)),
         string2, len2);
   XDrawString(display, win, gc, (win_width - width3)/2,
         (int)(win_height - font_height),
         string3, len3);
   /* Copy numbers into string variables */
   (void) sprintf(cd_height, " Height - %d pixels",
         DisplayHeight(display,screen_num));
   (void) sprintf(cd_width, " Width  - %d pixels",
         DisplayWidth(display,screen_num));
   (void) sprintf(cd_depth, " Depth  - %d plane(s)",
         DefaultDepth(display, screen_num));
   /* Reuse these for same purpose */
   len4 = strlen(string4);
   len1 = strlen(cd_height);
   len2 = strlen(cd_width);
   len3 = strlen(cd_depth);
   /* To center strings vertically, we place the first string
    * so that the top of it is two font_heights above the center
    * of the window; since the baseline of the string is what
    * we need to locate for XDrawString and the baseline is
    * one font_info->ascent below the top of the character,
    * the final offset of the origin up from the center of
    * the window is one font_height + one descent */
   initial_y_offset = win_height/2 - font_height -
         font_info->descent;
   x_offset = (int) win_width/4;
   XDrawString(display, win, gc, x_offset, (int) initial_y_offset,
         string4,len4);
   XDrawString(display, win, gc, x_offset, (int) initial_y_offset +
         font_height,cd_height,len1);
   XDrawString(display, win, gc, x_offset, (int) initial_y_offset +
         2 * font_height,cd_width,len2);
   XDrawString(display, win, gc, x_offset, (int) initial_y_offset +
         3 * font_height,cd_depth,len3);
}

Note that this routine may be called repeatedly in response to Expose events. That is why the font is loaded, a GC is created, and its font member is set to the loaded font in separate routines before the event loop. The font information structure (containing the font ID) and GC resource ID are passed to draw_text as arguments.

Vertical Text and Rotated Text

Xlib provides routines that draw horizontal strings, but not vertical ones. If you want to draw strings vertically that read normally, you need to use a separate text drawing call for each character. You use a baseline with the same x coordinate but a different y coordinate for each character.

Drawing strings vertically that read sideways is even more of a problem. The core X protocol and font server provides no way to rotate text, and the XLFD provides no way to name such font variations. [16] One possibility is to use fonts that have their characters sideways. There is only one of these in the distribution from MIT, called rot-s16.

Hewlett Packard has developed enhancements to the R5 font server that support rotated and anamorphically scaled text. They come in the form of patches to the source code for the font server in MIT's X distribution. These patches have been donated to the X Consortium so they are freely available for ftp on export.lcs.mit.edu. See The X Resource, Issue 3, Summer 1992, for a complete description.

Font Properties

Font properties give detailed information about a font, usually for use only in desktop publishing applications. A font is not guaranteed to have any properties. When possible, fonts should have at least the properties represented by the atoms listed in Table 6-1. These atoms are defined in <X11/Xatom.h>. XGetFontProperty() returns the value of a property given the atom for that property. In the descriptions in Table 6-1, the data associated with a property is referred to with the same name as the property, but in mixed case. For example, the property atom XA_SUPERSCRIPT_X contains a value that is referred to as SuperscriptX in the description.

Applications that make heavy use of proportionally spaced text may use these properties to space various characters properly.

For a further description of font properties and associated conventions, see Appendix M, Logical Font Description Conventions, of Volume Zero, X Protocol Reference Manual (as of the second printing).

Table 6-1. Font Properties

Property Name

Type

Description

XA_MIN_SPACE

unsigned int

The minimum interword spacing.

XA_NORM_SPACE

unsigned int

The normal interword spacing.

XA_MAX_SPACE

unsigned int

The maximum interword spacing.

XA_END_SPACE

unsigned int

The additional spacing at the end of sentences.

XA_SUPERSCRIPT_X

int

Offset (in pixels) from the character origin where superscripts should begin. If the origin is at [x,y] , then superscripts should begin at: [x + SuperscriptX, y - SuperscriptY]

XA_SUPERSCRIPT_Y

int

Offset (in pixels) from the character origin where superscripts should begin. If the origin is at [x,y] , then superscripts should begin at: [x + SuperscriptX, y - SuperscriptY]

XA_SUBSCRIPT_X

int

Offset (in pixels) from the character where subscripts should begin. If the origin is at [x,y] , then subscripts should begin at: [x + SubscriptX, y + SubscriptY]

XA_SUBSCRIPT_Y

int

Offset (in pixels) from the character where subscripts should begin. If the origin is at [x,y] , then subscripts should begin at: [x + SubscriptX, y + SubscriptY]

XA_UNDERLINE_POSITION

int

Y offset (in pixels) from the baseline to the top of an underline. If the baseline is y-coordinate y, then the top of the underline is at: [y + UnderlinePosition]

XA_UNDERLINE_THICKNESS

unsigned int

Thickness in pixels of an underline.

XA_STRIKEOUT_ASCENT

int

Vertical extents (in pixels) for boxing or voiding characters. If the baseline is at y-coordinate y, then the top of the strikeout box is at: [y - StrikeoutAscent] and the height of the box is: [StrikeoutAscent + StrikeoutDescent]

XA_STRIKEOUT_ DESCENT

int

Vertical extents (in pixels) for boxing or voiding characters. If the baseline is at y-coordinate y, then the top of the strikeout box is at: [y - StrikeoutAscent] and the height of the box is: [StrikeoutAscent + StrikeoutDescent]

XA_ITALIC_ANGLE

int

The angle of the dominant staffs of characters in the font, in degrees scaled by 64, relative to the three-o'clock position from the character origin, with positive indicating counterclockwise motion (as in XDrawArc ).

XA_X_HEIGHT

int

"1 ex" as in TeX, but expressed in units of pixels. Often the height of lowercase x .

XA_QUAD_WIDTH

int

"1 em" as in TeX, but expressed in units of pixels. The width of an m in the current font and point size.

XA_CAP_HEIGHT

int

Y offset from the baseline to the top of the capital letters, ignoring accents, in pixels. If the baseline is at y-coordinate y, then the top of the capitals is at: (y - CAP_HEIGHT)

XA_WEIGHT

unsigned

The weight or boldness of the font, expressed as a value between 0 and 1000.

XA_POINT_SIZE

unsigned

The point size, expressed in tenths of a point, of this font at the ideal resolution. There are 72.27 points to the inch.

XA_RESOLUTION

unsigned

The number of pixels per point, expressed in hundredths, at which this font was created.


It is also possible for fonts to have properties not in this predefined list. If there are such properties, they will be stored in a list of XFontProp structures in the XFontStruct for the font. Example 6-8 shows the XFontProp structure. The documentation for each font must describe these additional properties if they are defined.

Example 6-8. The additional font property structure

/* Additional properties to allow arbitrary information with fonts */
typedef struct {
    Atom name;
    unsigned long card32;
} XFontProp;


Setting the Font Path

XFreeFontPath(), XGetFontPath(), and XSetFontPath() are available to get or set the current search path for fonts. These functions are very rarely needed, but you should know that they exist. Their purpose is to allow for additional directories of fonts besides the default, which is /usr/lib/X11/fonts on UNIX-based systems. The font path is common to all clients of the server, so it should be modified with care. If the directory that contains the standard fonts is removed from the path, neither any client nor the server can access fonts.

In Release 5, font servers need to be added to the font path so the X server can access them.

Regions

An X region is an arbitrary set of pixels on the screen. But usually a region is either a rectangular area, several overlapping or adjacent rectangular areas, or a general polygon. Regions are chiefly used to set the clip_mask member of the GC. XSetRegion() sets the clip_mask to a region so that output will occur only within the region. Using XSetRegion() is a lot easier than defining a single-plane pixmap with the desired size and shape and then using that bitmap to set the clip_mask with XSetClipMask(), and it is more flexible than the clip_mask you can set with XSetClipRectangles().

The most common use of setting the clip_mask to a region is to combine the rectangle from each of multiple contiguous Expose events on a single window into a single region and clip the redrawing to that region. This provides a performance improvement in some situations. See “Repainting the Window” in Chapter 3 for more information and an example.

A region has an x and y offset, which is used internally when making calculations with regions (offsets for all regions have a common origin). The offset has an effect if the region is used as a clip_mask. When making a graphics request with the clip_mask of the GC set with XSetRegion(), the offset of the region is added to clip_x_origin and clip_y_origin to determine the placement of the region relative to the destination drawable.

Regions can be created with XCreateRegion() or XPolygonRegion(). XCreateRegion() creates an empty region to which rectangles can be added with XUnionRectWithRegion() and various other functions that perform mathematical operations on regions. XCreateRegion() and XPolygonRegion() return a pointer to the opaque type Region, whose definition a program does not need to know. Just the pointer is used to refer to the region. XPolygonRegion() creates a region of the same shape as XDrawLines() would draw given the same arguments (except that XPolygonRegion() does not require a drawable or a GC and therefore interprets the lines as thin lines). It specifies a list of points and has a flag that indicates whether areas overlapping an odd number of times should be included or not included in the region (just like the fill_rule in the GC).

Each region is implemented as a group of nonoverlapping rectangles. Therefore, performance will be best if the regions you use have sides parallel to the coordinate axes. Nonetheless, nonrectangular regions can be created with XPolygonRegion().

A region is destroyed with XDestroyRegion(). The best way to clear a region is to destroy it and create a new one.

XClipBox() returns the size and position of the smallest rectangle that completely encloses the given region. This function returns an XRectangle structure that contains the coordinates of the upper-left corner and the width and height of the rectangle enclosing a region.

Moving and Resizing Regions

XOffsetRegion() changes the offset of the specified region by the number of pixels specified by its arguments dx and dy. XShrinkRegion() reduces the size of the given region by the number of pixels specified by dx and dy, with positive values indicating that the region is to be increased in size. XShrinkRegion() also modifies the offset of the region to keep the center of the region near its original position.

Computations with Regions

Several functions are available to combine two regions in various ways. Each function takes three regions as arguments: two operands and a region in which to place the result.

XIntersectRegion() 

Computes the intersection (overlapping area) of two regions.

XUnionRegion() 

Computes the union (total of both areas) of two regions.

XSubtractRegion() 

Subtracts two regions. The result is the region listed first minus the intersection of the two regions.

XXorRegion() 

Computes the difference between the union and the intersection of two regions.

XUnionRectWithRegion() 

Computes the union of a rectangle and region and sets the region to the result.

Returning Region Information

This group of region functions makes logical determinations about regions. All of these routines return nonzero if their conditions are satisfied.

XEmptyRegion() 

Determines whether there is any area in the specified region.

XEqualRegion() 

Determines whether two regions have the same offset, size, and shape.

XPointInRegion() 

Determines whether a specified point resides in a region.

XRectInRegion() 

Determines whether a rectangle specified by x, y, width, and height occurs completely inside, completely outside, or overlapping a given region. It returns RectangleIn if the rectangle is completely inside the region, RectanglePart if the rectangle overlaps the edge of a region, and RectangleOut if the rectangle and the region are nonintersecting.

Images

Xlib provides an image structure that is capable of storing all the data corresponding to a screen area or pixmap. The major difference between an image and a pixmap is that an image is a structure on the client side, so its contents can be manipulated directly by the client, instead of solely through X protocol requests. Xlib provides the routines XGetImage() and XPutImage() that use the X protocol to transfer the contents of a window or pixmap into an image structure and to write the contents of an image structure back into a window or pixmap.

Xlib provides a few minimal routines for manipulating image structures, including routines to create and initialize an empty image structure, destroy an image structure, get a pixel, set a pixel, extract a subimage of an image, and add a constant value to all pixels in an image. These routines can be relatively slow, because they change the byte- and bit-order of the image before performing the operation and then change it back before placing it back in the image. However, in some implementations of Xlib, optimized versions of these routines will automatically be used when the byte- and bit-order used by the server happens to be the same as that used by the machine running the client. This should be quite fast but is not always available.

The image-processing routines provided by Xlib are minimal--they do not provide a complete image manipulation package. However, the image structure does contain all the information necessary to implement a complete package. An application can implement its own routines to manipulate the image data directly. However, this code is difficult to write in a portable and efficient fashion because of the large number of data formats that are possible. [17] XGetImage() returns data that uses the byte- and bit-order of the server. The application will need to swap this into the native byte- and bit-order before doing image processing. XPutImage() takes care of swapping it back before sending it to the server, so that the application need not convert the data back to the server-native byte- and bit-order. However, XPutImage() does not convert images of different depths.

The XImage data structure is shown in Example 6-9.

Example 6-9. The XImage structure

   struct _XImage {
   int width, height;          /* Size of image */
   int xoffset;                /* Number of pixels offset in
                                * x direction */
   int format;                 /* XYBitmap, XYPixmap, ZPixmap */
   char *data;                 /* Pointer to image data */
   int byte_order;             /* Data byte order, LSBFirst,
                                * MSBFirst */
   int bitmap_unit;            /* Quantity of scan line 8, 16, 32 */
   int bitmap_bit_order;       /* LSBFirst, MSBFirst */
   int bitmap_pad;             /* 8, 16, 32 either XY or Z format */
   int depth;                  /* Depth of image */
   int bytes_per_line;         /* Accelerator to next line */
   int bits_per_pixel;         /* Bits per pixel (ZPixmap format) */
   unsigned long red_mask;     /* Bits in z arrangement */
   unsigned long green_mask;
   unsigned long blue_mask;
   char *obdata;               /* Hook for the object routines to
                                * hang on */
   struct funcs {              /* Image manipulation routines */
      struct _XImage *(*create_image)();
      int (*destroy_image)();
      unsigned long (*get_pixel)();
      int (*put_pixel)();
      struct _XImage *(*sub_image)();
      int (*add_pixel)();
      } f;
} XImage;

The function pointers in the image object allow Xlib implementors to replace MIT's generic functions with functions optimized for the byte- and bit-order used in the machine that is running Xlib.

  • The height, width, and xoffset are set when an image is created. The offset is used to align an image to even-addressable boundaries.

  • The format member may be XYBitmap, XYPixmap, or ZPixmap.

    In XYBitmap, the bitmap is represented in scan line order, with each scan line made up of multiples of the bitmap_unit and padded with meaningless bits. Within each bitmap_unit, the bit order depends on bitmap_bit_order.

    In XYPixmap, each plane is represented as a bitmap, and the planes appear in most significant to least significant bit order, with no padding between planes.

    In ZPixmap, the pixels (instead of bits) are listed in scan line order. Each pixel has bits_per_pixel bits, and the bits in the pixel that are allocated to red, green, and blue for DirectColor and TrueColor are specified by red_mask, blue_mask, and green_mask. See Chapter 7, “Color” for more information on these masks. At the end of each scan line, a pad is used as for XYBitmap.

  • The byte_order is the data byte order, either LSBFirst or MSBFirst. The bitmap_bit_order is the bit order within each byte, again either LSBFirst or MSBFirst. The bitmap_unit specifies how many bits make up a unit of image data (usually the same as the word size), and it can be 8, 16, or 32. Together, these members determine the exact arrangement of bits in memory. Figure 6-6 shows the effect of the various byte_order and bit_order combinations assuming a bitmap_unit of 16. VAXes and 80*86 systems use byte_order of LSBFirst, while 68000-family systems use MSBFirst. Note that with these three variables alone there are 12 different data formats. The ImageByteOrder() and BitmapBitOrder() macros return which byte order and bit order is used by the server.

  • The bitmap_pad member can be 8, 16, or 32, and it specifies the quantum of the scan line. In other words, the start of one scan line and the start of the next are separated by an integer multiple of this number.

  • The depth of an image is assigned as the image is created. The depth of a window from which image data is read must match this depth.

  • The bytes_per_line member specifies how many bytes make up a scan line.

  • The bits_per_pixel member is for ZPixmap images only. This member of the XImage structure must match the member of the same name in the ScreenFormat structure (itself a member of Display).

  • The red_mask, green_mask, and blue_mask members are for ZPixmap only and specify the number of bits in the pixel that are allocated to red, green, and blue. This implies that the visual is DirectColor or TrueColor. See Chapter 7, “Color”for more information.

Figure 6-6. Bit and byte order possibilities for images when bitmap_unit = 16


Manipulating Images

These are the available functions that operate on images:

XCreateImage() 

Allocates memory for an XImage structure and sets various members. Note that it uses the server's data format, which is often not appropriate. The byte- and bit-order fields should usually be changed directly to the client-native format. However, then the call _XInitImageFuncPtrs(image) should be issued to reset the mapping to the appropriate versions of the functions for manipulating the image. This call is supposed to be private to Xlib and, therefore, should be watched for changes in later releases, but this is currently the accepted method.

XGetImage() 

Fills an XImage structure with data corresponding to a visible area of the screen or a pixmap.

XPutImage() 

Dumps an XImage structure with data into an area of a window or a pixmap.

XDestroyImage() 

Frees the data field in an image structure if the image structure was allocated in the application. If the image was created using XCreateImage(), XGetImage(), or XGetSubImage(), XDestroyImage() frees both the data and the image structure. Note that if the image data is stored in static memory in the application, it cannot be freed--to free an image created with XCreateImage() that has statically allocated data, you must set NULL into the data field before calling XDestroyImage().

XGetPixel() 

Gets a single pixel value specified by an x,y location from an image.

XPutPixel() 

Puts a single pixel value into an image in a specified location.

XAddPixel() 

Increments each pixel in a pixmap by a constant value.

XSubImage() 

Creates a new image that is a subset of an existing image. It executes XCreateImage() and then performs multiple executions of XGetPixel() and XPutPixel(), so it may be slow.

XGetSubImage() 

Creates an image from a subsection of a drawable.

Functions to read and write images to and from disk files have not yet been defined by the X Consortium.

Example 6-10 demonstrates the use of images. See Volume Two, for more information on the image-handling functions.

Examples Using Images

Images are one of the areas of X that has not yet been extensively used. Therefore, there are few examples available that use images to their potential.

The unique feature of images is that all the data is stored and is directly accessible in Xlib, rather than in the server like Pixmap and Window resources. Since images completely represent a screen area, you can do anything you want to any of the pixel values in the image. Applications like image processing and machine vision would probably use images.

Example 6-10 shows a routine using images. This routine reads an image from the screen, manipulates it, and puts a reflected version of the contents in a new window of the same size. It uses XGetImage(), XPutImage(), and XPutPixel().

Example 6-10. Example using images -- reflect_window

/* Window and newwindow must have the same size and depth,
 * and window must be visible */
reflect_window (window, newwindow, gc, width, height)
Window window, newwindow;
GC gc;
unsigned int width, height;
{
XImage *xi;
unsigned long pixelvalue1, pixelvalue2;
int y;
int left_x, right_x;
xi = XGetImage(display, window, 0,0, width, height, AllPlanes,
      XYPixmap);
printf("calculating reflection -- this may take awhile...\n");
for (left_x=0 ; left_x<width/2 ; left_x++)
        {
        for (y=0 ; y<height ; y++)
                {
                pixelvalue1 = XGetPixel(xi, left_x, y);
                right_x = width - left_x;
                if (left_x != right_x)
                        {
                        pixelvalue2 = XGetPixel(xi, right_x, y);
                        XPutPixel(xi, left_x, y, pixelvalue2);
                        }
                XPutPixel(xi, right_x, y, pixelvalue1);
                }
        }
printf("putting image\n");
XPutImage(display, newwindow, gc, xi, 0, 0, 0, 0, width, height);
}


With sufficient understanding of the format of image data, this routine could be rewritten without XGetPixel() and XPutPixel(), which would speed it up substantially. However, there would have to be separate code for the many different image formats to make the code as portable as the version shown.

Cursors

The cursor is different from other types of output to the screen since it is transient, passing over the screen without permanently changing it. The cursor is drawn where the pointer is pointing and removed as soon as the pointer moves.

Each window can have a different cursor defined in its window attributes (using XDefineCursor()). Whenever the pointer is in a visible window, the cursor is set to the cursor defined for that window. If no cursor was defined for that window, the cursor will be the one that was defined for the parent window unless otherwise specified in the attributes.

From X's perspective, a cursor consists of a cursor shape, mask, foreground and background colors, and hotspot (defined in a moment):

  • The cursor bitmap determines the shape of the cursor.

  • The mask bitmap determines the pixels on the screen that will be modified by the cursor.

  • The pixel values determine the foreground color (the 1 bits in the cursor bitmap) and the background color (the 0 bits in the cursor bitmap).

  • The hotspot defines the point on the cursor that will be reported when a pointer event occurs. The hotspot is the actual tracking position--for example, the center for a crosshair cursor or the point of an arrow.

There usually are limitations imposed by the hardware on cursors as to size, shape, and whether a mask is implemented. XQueryBestCursor() is used to find out what sizes are possible.

You need to create a Cursor resource to call XDefineCursor(). Read on for a description of the various ways to create cursors.

The Standard Cursor Font

Many popular cursor shapes are provided in the standard cursor font, <X11/cursorfont.h>. Each of these cursor shapes can be turned into a Cursor resource using XCreateFontCursor(). Example 6-11 demonstrates this process.

The cursor font is shown in Appendix I, The Cursor Font, of Volume Two, and on the reference page for XCreateFontCursor() in Volume Two. Each of these cursors uses two characters in the cursor font, only one of which is shown. One determines the shape of the cursor, and the other is a mask which selects which pixels on the screen are disturbed by the cursor. The mask for each standard cursor is very similar to the shape for that cursor but one pixel wider in all directions. This means that when the cursor is black and over a black background, this one pixel outline of the cursor will appear in white around the cursor, making the cursor visible over any background.

Example 6-11. Creating a Cursor from the standard cursor font

#include <X11/cursorfont.h>

int cursor_shape = XC_arrow;
Window window;
Cursor cursor;
cursor = XCreateFontCursor(display, cursor_shape);
XDefineCursor(display, window, cursor);
/* Now cursor will appear when pointer is in window */

If your client is operating on a color screen and it allows the user to specify window background colors, it may also allow the user to specify cursor colors, since this could improve contrast between the window background and cursor. These pixel values may be specified in the calls to XCreateGlyphCursor() and XCreatePixmapCursor(), or XRecolorCursor() may be called for an existing cursor.

XCreateGlyphCursor() allows you to do the same thing as is done with the standard cursors but using font characters you specify from any font. The hotspot of these cursors and those created by XCreateFontCursor() is the origin of each font character (just as if it were text). Usually the hotspot is placed in a logical location, but it is not possible to determine where the hotspot is from within the program or to change its location.

XCreatePixmapCursor() allows you to create a cursor from shape and mask pixmaps and foreground and background pixel values, with an explicit hotspot. XQueryBestCursor() should be called to determine the allowed cursor sizes before preparing the pixmaps.

You can free the cursor with XFreeCursor() right after the XDefineCursor() call if no further explicit references to it are made.

Creating a Pixmap Cursor

If no cursor in the standard cursor font meets your needs, you can design one of your own. It should be 16 by 16 pixels since some servers may not be able to handle other sizes efficiently. You can design the shape of the cursor with any bitmap editing tool such as the bitmap program. You will also have to design a mask. To see the purpose of the mask, move the cursor on your X screen over various backgrounds (highlight some text if necessary to get a different background). The mask provides an outline around the cursor so that the cursor is visible over any background. Therefore, the design of the mask is typically similar to the cursor shape but simpler and more dense. The outline of the mask usually extends one pixel more in every direction than the cursor shape. Figure 6-7 shows a shape and its corresponding mask, as an example of their relationship.

Figure 6-7. A cursor shape pixmap and corresponding mask

Example 6-12 demonstrates the code for creating your own cursor.

Example 6-12. Creating a Pixmap Cursor

#include "bill"      /* shape bits */
#include "mask"      /* mask bits */
Pixmap shape, mask;
XColor magenta_def, bviolet_def;
/* shape and mask are single plane pixmaps */
shape = XCreatePixmapFromBitmapData(display, root_window,
      bill_bits, bill_width, bill_height, 1, 0, 1 );
mask = XCreatePixmapFromBitmapData(display, root_window,
      mask_bits, mask_width, mask_height, 1, 0, 1 );
XParseColor(display, colormap, "magenta", &magenta_def );
XParseColor(display, colormap, "BlueViolet", &bviolet_def );
/* colors are applied when making the cursor, not when making
 * the shape and mask pixmaps */
cursor = XCreatePixmapCursor(display, shape, mask, &magenta_def,
      &bviolet_def, bill_x_hot, bill_y_hot );
XDefineCursor(display, window, cursor);


Loading and Changing Cursors

The following routines are used to manipulate cursors:

XCreateFontCursor() 

Creates a cursor from the font of standard cursors. This is the easiest way to create a cursor.

XCreateGlyphCursor() 

Creates a cursor from a font character (glyph) and a mask.

XCreatePixmapCursor() 

Creates a cursor from pixmap data.

XDefineCursor() 

Associates a cursor with a window, so that the specified cursor is displayed in the window whenever the pointer is in the window.

XUndefineCursor() 

Reverses XDefineCursor(), so that the window uses the cursor assigned to its parent.

XFreeCursor() 

Frees memory associated with a cursor.

XQueryBestCursor() 

Returns the supported cursor sizes on the given display.

XRecolorCursor() 

Changes the foreground and background color of a cursor.

See Volume Two, for more information on these routines.



[15] The * (wildcard) notation is used occasionally in this manual to indicate a number of events or routines with similar names. In this case, there are two functions, XDrawImageString() and XDrawImageString16(), which differ only slightly in name, features, and arguments, as described in “Character Format” Instead of always listing them both, we may use the wildcard notation.

[16] Some vendors supply a Display PostScript extension that supports scaled and rotated text.

[17] Anyone thinking of trying to write their own routines to manipulate images should get access to the Xlib code that manipulates images. This code will make you think again.