Previous: Font Handling, 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: Font Handling, Up: Part IV Drawing Objects [Contents][Index]