Chapter XXX - Drawing Things in FLTK
When can you draw things in FLTK?
There are only certain places you can execute drawing code in FLTK.
Calling these functions at other places will result in undefined
behavior!
- The most common is inside the virtual method Fl_Widget::draw(). To write code here,
you must subclass one of the existing Fl_Widget classes and implement
your own version of draw().
- You can also write boxtypes and labeltypes. These are small procedures that can be
called by existing Fl_Widget draw() methods. These "types" are
identified by an 8-bit index that is stored in the widget's box(),
labeltype(), and possibly other properties.
- You can call Fl_Window::make_current() to do
incremental update of a widget (use Fl_Widget::window() to find the
window). Under X this only works for the base Fl_Window class, not
for double buffered, overlay, or OpenGL windows!
FLTK Drawing functions
#include <FL/fl_draw.H>
Clipping
You can limit all your drawing to a rectangular region by calling
fl_clip, and put the drawings back by using fl_pop_clip. This
rectangle is measured in pixels (it is unaffected by the current
transformation matrix).
In addition, the system may provide clipping when updating windows,
this clip region may be more complex than a simple rectangle.
void fl_clip(int x, int y, int w, int h);
Intesect the current clip region with a rectangle and push this new
region onto the stack.
void fl_pop_clip();
Restore the previous clip region. You must call fl_pop_clip() once
for every time you call fl_clip(). If you return to FLTK with the
clip stack not empty unpredictable results occur.
int fl_not_clipped(int x, int y, int w, int h);
Returns true if any of the rectangle intersects the current clip
region. If this returns false you don't have to draw the object.
On X this returns 2 if the rectangle is partially clipped, and 1 if
it is entirely inside the clip region.
int fl_clip_box(int x, int y, int w, int h,
    int& X, int& Y, int& W, int& H);
Intersect the rectangle x,y,w,h with the current clip region and
returns the bounding box of the result in X,Y,W,H. Returns non-zero
if the resulting rectangle is different than the original. This can
be used to limit the necessary drawing to a rectangle. W and H are set to
zero if the rectangle is completely outside the region.
Colors
void fl_color(Fl_Color);
Set the color for all subsequent drawing operations. Fl_Color is
an enumeration type, all values are in the range 0-255. This is
not the X pixel, it is an internal table! The table provides
several general colors, a 24-entry gray ramp, and a 5x8x5 color cube.
All of these are named with poorly-documented symbols in <FL/Enumerations.H>.
Under X, a color cell will be allocated out of fl_colormap each
time you request an fl_color the first time. If the colormap fills up
then a least-squares algorithim is used to find the closest color.
Fl_Color fl_color();
Returns the last fl_color() that was set. This can be used for state
save/restore.
void Fl::set_color(Fl_Color, uchar r, uchar g, uchar b);
void Fl::get_color(Fl_Color, uchar &, uchar &, uchar &);
Set or get an entry in the fl_color index table. You can set it to
any 8-bit rgb color. On X, if the index has been requested before,
the pixel is free'd. No pixel is allocated until fl_color(i) is used
again, and there is no guarantee that the same pixel will be used next
time.
void fl_color(uchar r, uchar g, uchar
b);
Set the color for all subsequent drawing operations. The closest
possible match to the rgb color is used. Under X this works
perfectly for TrueColor visuals. For colormap visuals the nearest index
in the gray ramp or color cube is figured out, and fl_color(i) is done
with that, this can result in two approximations of the color and is
very inaccurate!
Fast Shapes
These are used to draw almost all the FLTK widgets. They draw on
exact pixel boundaries and are as fast as possible, and their behavior
will be duplicated exactly on any platform FLTK is ported to. It is
undefined whether these are affected by the transformation matrix, so you should only call these
while it is the identity.
All arguments are integers.
void fl_rectf(x, y, w, h);
Color a rectangle that exactly fills the given bounding box.
void fl_rectf(x, y, w, h, uchar r, uchar g, uchar b);
Color a rectangle with "exactly" the passed r,g,b color. On screens
with less than 24 bits of color this is done by drawing a
solid-colored block using fl_draw_image() so that dithering
is produced. If you have 24 bit color, this fills the rectangle with a
single pixel value and is about 1 zillion times faster.
void fl_rect(x, y, w, h);
Draw a 1-pixel border inside this bounding box.
void fl_line(x, y, x1, y1);
void fl_line(x, y, x1, y1, x2, y2);
Draw one or two 1-pixel thick lines between the given points.
void fl_loop(x, y, x1, y1, x2, y2);
void fl_loop(x, y, x1, y1, x2, y2, x3, y3);
Outline a 3 or 4-sided polygon with 1-pixel thick lines.
void fl_polygon(x, y, x1, y1, x2, y2);
void fl_polygon(x, y, x1, y1, x2, y2, x3, y3);
Fill a 3 or 4-sided polygon. The polygon must be convex.
void fl_xyline(x, y, x1, y1);
void fl_xyline(x, y, x1, y1, x2);
void fl_xyline(x, y, x1, y1, x2, y3);
Draw 1-pixel wide horizontal and vertical lines. A horizontal line is
drawn first, then a vertical, then a horizontal.
void fl_yxline(x, y, y1);
void fl_yxline(x, y, y1, x2);
void fl_yxline(x, y, y1, x2, y3);
Draw 1-pixel wide vertical and horizontal lines. A vertical line is
drawn first, then a horizontal, then a vertical.
void fl_arc(x, y, w, h, double a1, double a2);
void fl_pie(x, y, w, h, double a1, double a2);
void fl_chord(x, y, w, h, double a1, double a2);
High-speed ellipse sections. These functions match the rather limited
circle drawing code provided by X and MSWindows. The advantage over using fl_arc is that they are faster because they often use
the hardware, and they draw much nicer small circles, since the small
sizes are often hard-coded bitmaps.
If a complete circle is drawn it will fit inside the passed
bounding box. The two angles are measured in degrees counterclockwise
from 3'oclock and are the starting and ending angle of the arc, a2
must be greater or equal to a1.
fl_arc draws a 1-pixel thick line (notice this has a different
number of arguments than the fl_arc described
below.
fl_pie draws a filled-in pie slice. This slice may extend outside
the line drawn by fl_arc, to avoid this use w-1 and h-1.
fl_chord is not yet implemented.
Complex Shapes
These functions let you draw arbitrary shapes with 2-D linear
transformations. The functionality matches PostScript. The exact
pixels filled in is less defined than for the above calls, so that FLTK
can take advantage of drawing hardware. (Both Xlib and MSWindows round
all the transformed verticies to integers before drawing the line
segments. This severely limits the accuracy of these functions for
complex graphics. Try using OpenGL instead)
All arguments are float.
void fl_push_matrix();
void fl_pop_matrix();
Save and restore the current transformation. The maximum depth of the
stack is 4.
void fl_scale(x, y);
void fl_scale(x);
void fl_translate(x, y);
void fl_rotate(d);
void fl_mult_matrix(a, b, c, d, x, y);
Concat another transformation to the current one. The rotation angle
is in degrees (not radians) counter-clockwise.
void fl_begin_line();
void fl_end_line();
Start and end drawing 1-pixel thick lines.
void fl_begin_loop();
void fl_end_loop();
Start and end drawing a closed sequence of 1-pixel thick lines.
void fl_begin_polygon();
void fl_end_polygon();
Start and end drawing a convex filled polygon.
void fl_begin_complex_polygon();
void fl_gap();
void fl_end_complex_polygon();
Start and end drawing a complex filled polygon. This polygon may be
concave, may have holes in it, or may be several disconnected pieces.
Call fl_gap() to seperate loops of the path (it is unnecessary but
harmless to call fl_gap() before the first vertex, after the last one,
or several times in a row). For portability, you should only draw
polygons that appear the same whether "even/odd" or "non-zero"
"winding rules" are used to fill them. This mostly means that holes
should be drawn in the opposite direction of the outside.
fl_gap() should only be called between
fl_begin/end_complex_polygon(). To outline the polygon, use
fl_begin_loop() and replace each fl_gap() with
fl_end_loop();fl_begin_loop().
void fl_vertex(x, y);
Add a single vertex to the current path.
void fl_curve(int x,int y,int x1,int y1,int x2,int
y2,int x3,int y3);
Add a series of points on a Bezier curve to the path. The curve ends
(and two of the points) are at x,y and x3,y3.
void fl_arc(x, y, r, start, end);
Add a series of points to the current path on the arc of a circle (you
can get elliptical paths by using scale and rotate before calling
this). x,y are the center of the circle, and r is it's
radius. fl_arc() takes start and end angles that are
measured in degrees counter-clockwise from 3 o'clock. If end
is less than start then it draws clockwise.
void fl_circle(x, y, r);
fl_circle() is equivalent to fl_arc(...,0,360) but may be faster. It
must be the only thing in the path: if you want a circle as
part of a complex polygon you must use fl_arc(). Under Xlib and
MSWindows this draws incorrectly if the transformation is both rotated
and non-square scaled.
Text
All text is drawn in the current font. It is
undefined whether this location or the characters are modified by the
current transformation.
void fl_draw(const char*, float x, float y);
void fl_draw(const char*, int n, float x, float y);
Draw a null terminated string or an array of n characters
starting at the given location.
void fl_draw(const char*, int x,int y,int w,int h, Fl_Align);
Fancy string drawing function which is used to draw all the labels.
The string is formatted and aligned inside the passed box. Handles
'\t' and '\n', expands all other control characters to ^X, and aligns
inside or against the edges of the box. See Fl_Widget::align() for values for
align. The value FL_ALIGN_INSIDE is ignored, this always
prints inside the box.
void fl_measure(const char*, int& w, int& h);
Measure how wide and tall the string will be when printed by the
fl_draw(...align) function. If the incoming w is non-zero it will
wrap to that width.
int fl_height();
Recommended minimum line spacing for the current font. You can also
just use the value of size passed to fl_font().
int fl_descent();
Recommended distance above the bottom of a fl_height() tall box to
draw the text at so it looks centered vertically in that box.
float fl_width(const char*);
float fl_width(const char*, int n);
float fl_width(uchar);
Return the width of a null-terminated string, a sequence of n
characters, and a single character.
const char* fl_shortcut_label(ulong);
Unparse a shortcut value as used by Fl_Button or Fl_Menu_Item into a human-readable string like
"Alt+N". This only works if the shortcut is a character key or a
numbered Function key. If the shortcut is zero an empty string is
returned. The return value points at a static buffer that is
overwritten with each call.
Fonts
void fl_font(int face, int size);
Set the current font, which is then used by the routines described
above. You may call this outside a draw context if necessary to call
fl_width(), but on X this will open the display.
The font is identified by a face and a size. The
size of the font is measured in pixels (ie. it is not
"resolution [in]dependent"). Lines should be spaced size
pixels apart (or more).
The face is an index into an internal table. Initially only
the first 16 faces are filled in. There are symbolic names for them:
FL_HELVETICA, FL_TIMES, FL_COURIER, and modifier values FL_BOLD and
FL_ITALIC which can be added to these, and FL_SYMBOL and
FL_ZAPF_DINGBATS. Faces greater than 255 cannot be used in Fl_Widget
labels, since it stores the index as a byte.
int fl_font();
int fl_size();
Returns the face and size set by the most recent fl_font(a,b). This
can be used to save/restore the font.
const char* Fl::get_font(int face);
Get the string for this face. This string is different for each face.
Under X this value is passed to XListFonts to get all the sizes of
this face.
const char* Fl::get_font_name(int face, int* attributes=0);
Get a human-readable string describing the family of this face. This
is useful if you are presenting a choice to the user. There is no
guarantee that each face has a different name. The return value
points to a static buffer that is overwritten each call.
The integer pointed to by attributes (if the pointer is not
zero) is set to zero, FL_BOLD(1) or
FL_ITALIC(2) or FL_BOLD|FL_ITALIC (maybe
more attributes will be defined in the future). To locate a "family"
of fonts, search forward and back for a set with non-zero attributes,
these faces along with the face with a zero attribute before them
constitute a family.
int get_font_sizes(int face, int*& sizep);
Return an array of sizes in sizep. The return value is the
length of this array. The sizes are sorted from smallest to largest
and indicate what sizes can be given to fl_font() that will be matched
exactly (fl_font() will pick the closest size for other sizes). A
zero in the first location of the array indicates a scalable font,
where any size works, although the array may list sizes that work
"better" than others. Warning: the returned array points at a static
buffer that is overwritten each call. Under X this will open the
display.
int Fl::set_font(int face, const char*);
Change a face. The string pointer is simply stored, the string is not
copied, so the string must be in static memory.
int Fl::set_font(int face, int from);
Copy one face to another.
int Fl::set_fonts(const char* = 0);
FLTK will open the display, and add every font on the server to the
face table. It will attempt to put "families" of faces together, so
that the normal one is first, followed by bold, italic, and bold
italic.
The optional argument is a string to describe the set of fonts to
add. Passing NULL will select only fonts that have the ISO8859-1
character set (and are thus usable by normal text). Passing "-*" will
select all fonts with any encoding as long as they have normal X font
names with dashes in them. Passing "*" will list every font that
exists (on X this may produce some strange output). Other values may
be useful but are system dependent. On MSWindows NULL selects fonts
with ISO8859-1 encoding and non-NULL selects all fonts.
Return value is how many faces are in the table after this is done.
Bitmaps, Pixmaps and Images
Click here for information on drawing images
Cursor
void fl_cursor(Fl_Cursor, Fl_Color=FL_WHITE, Fl_Color=FL_BLACK);
Change the cursor. Depending on the system this may affect the cursor
everywhere, or only when it is pointing at the window that is current
when you call this. For portability you should change the cursor back
to the default in response to FL_LEAVE events.
The type Fl_Cursor is an enumeration defined in <Enumerations.H>. The
double-headed arrows are bitmaps provided by FLTK on X, the others are
provided by system-defined cursors. Under X you can get any XC_cursor
value by passing Fl_Cursor((XC_foo/2)+1).
FL_CURSOR_DEFAULT (0) usually an arrow
FL_CURSOR_ARROW
FL_CURSOR_CROSS - crosshair
FL_CURSOR_WAIT - watch or hourglass
FL_CURSOR_INSERT - I-beam
FL_CURSOR_HAND - hand (uparrow on MSWindows)
FL_CURSOR_HELP - question mark
FL_CURSOR_MOVE - 4-pointed arrow
FL_CURSOR_NS - up/down arrow
FL_CURSOR_WE - left/right arrow
FL_CURSOR_NWSE - diagonal arrow
FL_CURSOR_NESW - diagonal arrow
FL_CURSOR_NONE - invisible
Overlay rectangle
void fl_overlay_rect(int x, int y, int w, int h);
void fl_overlay_clear();
Big kludge to draw interactive selection rectangles without using
the overlay. FLTK will xor a single rectangle outline over a window.
Calling this will erase any previous rectangle (by xor'ing it), and
then draw the new one. Calling fl_overlay_clear() will erase the
rectangle without drawing a new one. Using this is tricky. You
should make a widget with both a handle() and draw() method. draw()
should call fl_overlay_clear() before doing anything else. Your
handle() method should call window()->make_current() and then
fl_overlay_rect() after FL_DRAG events, and should call
fl_overlay_clear() after a FL_RELEASE event.
(back to contents)