Previous: , Up: Part IV Drawing Objects   [Contents][Index]


28.7 Drawing Functions

There are a number of routines that help you draw objects on the screen. All XForms’s internal drawing routine draws into the "current window", defined as the window the object that uses the drawing routine belongs to. If that’s not what you need, the following routines can be used to set or query the current window:

void fl_winset(Window win);
Window fl_winget(void);

One caveat about fl_winget() is that it can return None if called outside of an object’s event handler, depending on where the mouse is. Thus, the return value of this function should be checked when called outside of an object’s event handler.

It is important to remember that unless the following drawing commands are issued while handling the FL_DRAW or FL_DRAWLABEL event (which is not generally recommended), it is the application’s responsibility to set the proper drawable using fl_winset().

The most basic drawing routines are for drawing rectangles:

void fl_rectf(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
              FL_COLOR col);
void fl_rect(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
             FL_COLOR col);

Both functions draw a rectangle on the screen in color col. While fl_rectf() draws a filled rectangle, fl_rect() just draws the outline in the given color.

To draw a filled (with color col) rectangle with a black border use

void fl_rectbound(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
                  FL_COLOR col);

To draw a rectangle with rounded corners (filled or just the outlined) employ

void fl_roundrectf(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
                   FL_COLOR col);
void fl_roundrect(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
                  FL_COLOR col);

To draw a general polygon, use one of the following routines

typedef struct {
    short x,
          y;
} FL_POINT;

void fl_polyf(FL_POINT *xpoint, int n, FL_COLOR col);
void fl_polyl(FL_POINT *xpoint, int n, FL_COLOR col);
void fl_polybound(FL_POINT *xpoint, int n, FL_COLOR col);

fl_polyf() draws a filled polygon defined by n points, fl_polyl() the ouline of a polygon and fl_polybound() a filled polygon with a black outline.

Note: all polygon routines require that the array xpoint has spaces for n+1 points, i.e., one more than then number of points you intend to draw!

To draw an ellipse. either filled, open (with the outline drawn in the given color), or filled with a black border the following routines can be used (use w equal to h to get a circle):

void fl_ovalf(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
              FL_COLOR col);
void fl_ovall(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
              FL_COLOR col);
void fl_ovalbound(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
                  FL_COLOR col);

The x and y arguments are the upper left hand corner of the ellipse, while w and h are its width and height.

Note: fl_ovall() (with two ’l’) isn’t a typo, the trailing ’l’ it’s meant indicate that only a line will be drawn. And there’s also the function

void fl_ovalf(int fill, FL_Coord x, FL_Coord y, FL_Coord w,
              FL_Coord h, FL_COLOR col);

which is invoked by both (the macros) fl_ovalf() and fl_ovall() with the first argument fill set to either 1 or 0.

To simplify drawing circles there are three additional functions. The first one draws an (open) circle (with the circumfence in the given color), the second one a filled circle, and the last one a filled circle with a black circumfence:

void fl_circ(FL_Coord x, FL_Coord y, FL_Coord r, FL_COLOR col);
void fl_circf(FL_Coord x, FL_Coord y, FL_Coord r, FL_COLOR col);
void fl_circbound(FL_Coord x, FL_Coord y, FL_Coord r, FL_COLOR col);

Here x and y are the coordinates of the center of the circle, r is its radius and col the color to be used.

To draw circular arcs, either open or filled, the following routines can be used

void fl_arc(FL_Coord x, FL_Coord y, FL_Coord radius,
            int start_theta, int end_theta, FL_COLOR col);
void fl_arcf(FL_Coord x, FL_Coord y, FL_Coord radius,
             int start_theta, int end_theta, FL_COLOR col);

x and y are the coordinates of the center and r is the radius. start_theta and end_theta are the starting and ending angles of the arc in units of tenths of a degree (where 0 stands for a direction of 3 o’clock, i.e., the right-most point of a circle), and x and y are the center of the arc. If the difference between theta_end and theta_start is larger than 3600 (360 degrees), drawing is truncated to 360 degrees.

To draw elliptical arcs the following routine can be used:

void fl_pieslice(int fill, FL_Coord x, FL_Coord y, FL_Coord w,
                 FL_Coord h, int start_theta, int end_theta,
                 FL_COLOR col);

x and y are the upper left hand corner of the box enclosing the ellipse that the pieslice is part of and w and h the width and height of that box. start_theta and end_theta, to be given in tenth of a degree, specify the starting and ending angles measured from zero degrees (3 o’clock).

Depending on circumstance, elliptical arc may be more easily drawn using the following routine

void fl_ovalarc(int fill, FL_Coord x, FL_Coord y, FL_Coord w,
                FL_Coord h, int theta, int dtheta, FL_COLOR col);

Here theta specifies the starting angle (again measured in tenth of a degree and with 0 at the 3 o’clock position), and dtheta specifies both the direction and extent of the arc. If dtheta is positive the arc is drawn in counter-clockwise direction from the starting point defined by theta, otherwise in clockwise direction. If dtheta is larger than 3600 it is truncated to 3600.

To connect two points with a straight line, use

void fl_line(FL_Coord x1, FL_Coord y1,
             FL_Coord x2, FL_Coord y2, FL_COLOR col);

There is also a macro for drawing a line along the diagonal of a box (to draw a horizontal line set h to 1, not to 0):

void fl_diagline(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
                 FL_COLOR col);

To draw connected line segments between n points use

void fl_lines(FL_POINT *points, int n, FL_COLOR col);

All coordinates in points are relative to the origin of the drawable.

There are also routines to draw one or more pixels

void fl_point(FL_Coord x, FL_Coord y, FL_COLOR col);
void fl_points(FL_POINT *p, int np, FL_COLOR col);

As usual, all coordinates are relative to the origin of the drawable. Note that these routines are meant for you to draw a few pixels, not images consisting of tens of thousands of pixels of varying colors. For that kind of drawing XPutImage(3) should be used. Or better yet, use the image support in the Forms Library (see Images). Also it’s usually better when drawing multiple points to use fl_points(), even if that means that the application program has to pre-sort and group the pixels of the same color.

To change the line width or style, the following convenience functions are available

void fl_linewidth(int lw);
void fl_linestyle(int style);

Set lw to 0 to reset the line width to the servers default. Line styles can take on the following values (also see XChangeGC(3))

FL SOLID

Solid line. Default and most efficient.

FL DOT

Dotted line.

FL DASH

Dashed line.

FL DOTDASH

Dash-dot-dash line.

FL LONGDASH

Long dashed line.

FL USERDASH

Dashed line, but the dash pattern is user definable via fl_dashedlinestyle(). Only the odd numbered segments are drawn with the foreground color.

FL USERDOUBLEDASH

Similar to FL_LINE_USERDASH but both even and odd numbered segments are drawn, with the even numbered segments drawn in the background color (as set by fl_bk_color()).

The following routine can be used to change the dash pattern for FL_USERDASH and FL USERDOUBLEDASH:

void fl_dashedlinestyle(const char *dash, int ndashes)

Each element of the array dash is the length of a segment of the pattern in pixels (0 is not allowed). Dashed lines are drawn as alternating segments, each with the length of an element in dash. Thus the overall length of the dash pattern, in pixels, is the sum of all elements of dash. When the pattern is used up but the line to draw is longer it used from the start again. The following example code specifies a long dash (9 pixels) to come first, then a skip (3 pixels), a short dash (2 pixels) and then again a skip (3 pixels). After this sequence, the pattern repeats.

char ldash_sdash[] = {9, 3, 2, 3};
fl_dashedlinestyle(ldash_sdash, 4);

If dash is NULL or ndashes is 0 (or the dash array contains an element set to 0) a default pattern of 4 pixels on and 4 fixels off is set.

It is important to remember to call fl_dashedlinestyle() whenever FL_USERDASH is used to set the dash pattern, otherwise whatever the last pattern was will be used. To use the default dash pattern you can pass NULL as the dash parameter to fl_dashedlinestyle().

By default, all lines are drawn so they overwrite the destination pixel values. It is possible to change the drawing mode so the destination pixel values play a role in the final pixel value.

void fl_drawmode(int mode);

There are 16 different possible settings for mode (see a Xlib programming manual for all the gory details). A of the more useful ones are

GXcopy

Default overwrite mode. Final pixel value = Src

GXxor

Bitwise XOR (exclusive-or) of the pixel value to be drawn with the pixel value already on the screen. Useful for rubber-banding.

GXand

Bitwise AND of the pixel value to be drawn with the pixel value already on the screen.

GXor

Bitwise OR of the pixel value to be drawn with the pixel value already on the screen.

GXinvert

Just invert the pixel values already on the screen.

To obtain the current settings of the line drawing attributes use the following routines

int fl_get_linewidth(void);
int fl_get_linestyle(void);
int fl_get_drawmode(void);

There are also a number of high-level drawing routines available. To draw boxes the following routine exists. Almost any object class will use it to draw the bounding box of the object.

void fl_draw_box(int style, FL_Coord x, FL_Coord y,
                 FL_Coord w, FL_Coord h,
                 FL_COLOR col, int bw);

style is the type of the box, e.g., FL_DOWN_BOX. x, y, w, and h indicate the size of the box. col is the color and bw is the width of the boundary, which typically should be given the value obj->bw or FL_BOUND_WIDTH. Note that a negative border width indicates a "softer" up box. See the demo program borderwidth.c for the visual effect of different border widths.

There is also a routine for drawing a frame:

void fl_draw_frame(int style, FL_Coord x, FL_Coord y,
                   FL_Coord w, FL_Coord h, FL_COLOR col, int bw)

All parameters have the usual meaning except that the frame is drawn outside of the bounding box specified.

For drawing text there are two routines:

void fl_draw_text(int align, FL_Coord x, FL_Coord y, FL_Coord w,
                  FL_Coord h, FL_COLOR col, int style, int size,
                  const char *str);
void fl_draw_text_beside(int align, FL_Coord x, FL_Coord y,
                         FL_Coord w, FL_Coord h, FL_COLOR col,
                         int style, int size, const char *str);

where align is the alignment, namely, FL ALIGN LEFT, FL ALIGN CENTER etc. x, y, w and h indicate the bounding box, col is the color of the text, size is the size of the font to use (in points) and style is the font style to be used (see Label Attributes and Fonts, for valid styles). Finally, str is the string itself, possibly containing embedded newline characters.

fl_draw_text() draws the text inside the bounding box according to the alignment requested while fl_draw_text_beside() draws the text aligned outside of the box. These two routines interpret a text string starting with the character @ differently in drawing some symbols instead. Note that fl_draw_text() puts a padding of 5 pixels in vertical direction and 4 in horizontal around the text. Thus the bounding box should be 10 pixels wider and 8 pixels higher than required for the text to be drawn.

The following routine can also be used to draw text and, in addition, a cursor can optionally be drawn

void fl_draw_text_cursor(int align, FL_Coord x, FL_Coord y,
                         FL_Coord w, FL_Coord h, FL_COLOR col,
                         int style, int size, char *str,
                         FL_COLOR ccol, int pos);

where ccol is the color of the cursor and pos is its position which indicates the index of the character in str before which to draw the cursor (-1 means show no cursor). This routine does no interpretion of the special character @ nor does it add padding around the text.

Given a bounding box and the size of an object (e.g., a label) to draw, the following routine can be used to obtain the position of where to draw it with a certain alignment and including padding:

void fl_get_align_xy(int align, int x, int y, int w, int h,
                     int obj_xsize, int obj_ysize,
                     int xmargin, int ymargin,
                     int *xpos, int *ypos);

This routine works regardless if the object is to be drawn inside or outside of the bounding box specified by x, y, w and h. obj_xsize and obj->ysize are the width and height of the object to be drawn and xmargin and ymargin is the additional padding to use. xpos and ypos return the position to be used for drawing the object.

For drawing object labels the following routines might be more convenient:

void fl_draw_object_label(FL_OBJECT *obj)
void fl_draw_object_label_outside(FL_OBJECT *obj);

Both routines assume that the alignment is relative to the full bounding box of the object. The first routine draws the label according to the alignment, which could be inside or outside of the bounding box. The second routine will always draw the label outside of the bounding box.

An important aspect of (re)drawing an object is efficiency which can result in flicker and non-responsiveness if not handled with care. For simple objects like buttons or objects that do not have "movable parts", drawing efficiency is not a serious issue although you can never be too fast. For complex objects, especially those that a user can interactively change, special care should be taken.

The most important rule for efficient drawing is not to draw if you don’t have to, regardless how simple the drawing is. Given the networking nature of X, simple or not depends not only on the host/server speed but also the connection. What this strategy entails is that the drawing should be broken into blocks and depending on the context, draw/update only those parts that need to.


Previous: , Up: Part IV Drawing Objects   [Contents][Index]