GTK+ / Gnome Application Development | |||
---|---|---|---|
<<< Previous | Home | Next >>> |
Once you understand drawables, colors, visuals, graphics contexts, and fonts, actually drawing is very simple. This section is a quick summary of the GDK drawing routines. Remember that drawing is a server-side operation; for example, if you ask to draw a line, Xlib will send the line's endpoints to the server, and the server will do the actual drawing using the specified GC (the GC is also a server-side resource). Often this is an important performance consideration.
You can draw a single point with gdk_draw_point(), or multiple points with gdk_draw_points() (Figure 17). The point is drawn in the current foreground color. Multiple points are given as an array. A GdkPoint looks like this:
typedef struct _GdkPoint GdkPoint; struct _GdkPoint { gint16 x; gint16 y; }; |
Remember that X coordinates start in the top left corner, are relative to the drawable, and may not overflow a signed sixteen-bit integer.
To draw a single line, pass its endpoints as arguments to gdk_draw_line() (Figure 18). To draw connected lines, you pass a list of points to gdk_draw_lines(); GDK will "connect the dots." To draw multiple lines that aren't necessarily connected, pass a list of segments to gdk_draw_segments(); a GdkSegment is:
typedef struct _GdkSegment GdkSegment; struct _GdkSegment { gint16 x1; gint16 y1; gint16 x2; gint16 y2; }; |
If lines or segments drawn in the same request meet at their endpoints, they are joined with the join style from the GC.
#include <gdk/gdk.h> |
void gdk_draw_line
(GdkDrawable* drawable, GdkGC* gc, gint x1, gint y1, gint x2, gint y2);
void gdk_draw_lines
(GdkDrawable*
drawable, GdkGC*
gc, GdkPoint* points, gint npoints);
void gdk_draw_segments
(GdkDrawable*
drawable, GdkGC*
gc, GdkSegment* segments, gint nsegments);
Figure 18. Drawing Lines
Rectangles are drawn with gdk_draw_rectangle() (Figure 19). The filled argument indicates whether to fill the rectangle; TRUE means to fill it.
gdk_draw_arc() draws an ellipse or a portion of one. (Figure 20). The arc can be filled or unfilled; the third argument to the function toggles fill. The fourth through seventh arguments describe a rectangle; the ellipse is inscribed in this rectangle. angle1 is the angle at which to start drawing; it is relative to the 3 o'clock position (that is, 0 radians). angle2 is the distance to travel around the arc; if positive, travel is counterclockwise, otherwise travel is clockwise. Both angle1 and angle2 are specified in sixty-fourths of a degree; so, 360 degrees is given as 360*64. This allows more precise specification of the arc's size and shape, without using floating point numbers. angle2 should not exceed 360 degrees, since it is nonsensical to move more than 360 degrees around the ellipse.
To draw a circle, draw from 0 to 360*64 inside a square:
gdk_draw_arc(drawable, gc, TRUE, 0, 0, 50, 50, 0, 360*64); |
To draw half an ellipse, change the aspect ratio and halve the span of the arc:
gdk_draw_arc(drawable, gc, TRUE, 0, 0, 100, 50, 0, 180*64); |
Many X servers draw the edges of filled arcs in an aesthetically unpleasing way; in particular, very small circles may not look very circular. You can work around this by also drawing the circle's outline.
gdk_draw_polygon() draws a filled or unfilled polygon (Figure 21). Notice that gdk_draw_lines() can also be used to draw an unfilled polygon (there is no reason to prefer one or the other). The arguments to gdk_draw_polygon() are the same as those to gdk_draw_lines(). The polygon does not have to be convex. It may also be self-intersecting. Self-intersecting polygons are filled with an "Even-Odd Rule," which means regions with an odd number of polygon areas overlapping them are not filled. That is, if the polygon does not overlap itself, it is entirely filled; if a region is overlapped once, it is not filled; if it's overlapped twice, it is filled; and so on.
There are two functions to draw strings; as an optimization, gdk_draw_text() takes the length of the string to draw as an argument. gdk_draw_string() uses strlen() to compute the string length for you. Otherwise the two are identical. The x and y coordinates specify the location of the left side of the text's baseline. See the section called Fonts for more information on fonts and font metrics. Text is drawn in the foreground color.
There is no way to draw scaled or rotated text with GDK. GnomeCanvasText offers a slow and low-quality way to render scaled and rotated text (see the section called Text Item in the chapter called GnomeCanvas). If you need high-quality scaling and rotating, you will need to use additional libraries, such as t1lib for Type 1 fonts or FreeType for True Type fonts. Another possibility is the Display Postscript extension to X (XDPS); the GNU Project is working on a free implementation of XDPS. The Gnome project also has a text solution in development, as part of the gnome-print library.
gdk_draw_pixmap() copies a region from a pixmap to another drawable (pixmap or window). The source and destination drawables must have the same depth and visual. If you pass -1 for the width or height, the full size of the source pixmap is substituted. The source can actually be any drawable, including a window, but gdk_window_copy_area() will make your code clearer if the source is a window. Figure 23 shows gdk_draw_pixmap().
GDK's GdkRGB module allows you to copy a client-side buffer of image data to a drawable. If you need to manipulate images extensively, or copy image data to the server, this is the correct way to do it. You can't directly manipulate a GdkPixmap because a pixmap is a server-side object. Copying image data to the server with gdk_draw_point() would be unbelievably slow, since each point would require a server request (probably more than one, since you will need to change the GC for each point).
Internally, GdkRGB uses an object called GdkImage to rapidly copy image data to the server in a single request. This is still somewhat slow---sizeable data does have to be copied---but GdkRGB is highly tuned and uses shared memory if the client and server happen to be on the same machine. So it's the fastest way to perform this task, given the X architecture. It will also handle some tricky issues for you (such as adapting to the colormaps and visuals available on a given X server).
The GdkRGB functions are in a separate header, gdk/gdkrgb.h. Before using any GdkRGB functions, you must initialize the module with gdk_rgb_init() (Figure 24); this sets up the visual and colormap GdkRGB will use, and some internal data structures.
The drawable you intend to copy the RGB buffer to must use GdkRGB's visual and colormap. If the drawable is a part of a widget, the easiest way to ensure this is to push the GdkRGB visual and colormap when you create the widget:
GtkWidget* widget; gtk_widget_push_visual(gdk_rgb_get_visual()); gtk_widget_push_colormap(gdk_rgb_get_cmap()); widget = gtk_whatever_new(); gtk_widget_pop_visual(); gtk_widget_pop_colormap(); |
The current version of GTK+ will be better-behaved if you do this when creating the toplevel window containing the drawable, instead of when creating the drawable itself. However, in principle you can do it for only the drawable.
GdkRGB understands several kinds of image data, including 24- and 32-bit RGB data, 8-bit grayscale, and 8-bit indexes into an array of RGB values (a client-side GdkRgbCmap). This section describes only the simplest, 24-bit RGB data; this kind of buffer is rendered with gdk_draw_rgb_image(). There are separate functions to render the other buffer types, but all of them work in essentially the same way.
A 24-bit RGB buffer is a one-dimensional array of bytes; every byte triplet makes up a pixel (byte 0 is red, byte 1 is green, byte 2 is blue). Three numbers describe the size of the array and the location of bytes within it:
The width is the number of pixels (byte triplets) per row of the image.
The height is the number of rows in the image.
The rowstride is the number of bytes between rows. That is, for a buffer with rowstride r, if row n starts at array index i row n+1 starts at array index i+r. The rowstride is not necessarily three times the buffer's width; GdkRGB is faster if both the source pointer and the rowstride are aligned to a 4-byte boundary. Specifying a rowstride allows you to use padding to achieve this.
The x, y, width, and height arguments to gdk_rgb_draw_image() define a region of the target drawable to copy the RGB buffer to. The RGB buffer must have at least width columns and height rows. Row 0, column 0 of the RGB buffer will be copied to point (x, y) on the drawable.
Dithering simulates a larger number of colors on displays with a limited palette. Dithering only matters on 8- and 16-bit displays; 24-bit displays do not have a limited palette. The dither argument is an enumerated type; it has three possible values:
GDK_RGB_DITHER_NONE specifies that no dithering will be done. It's appropriate for text or line drawings with few colors, but inappropriate for photographic images.
GDK_RGB_DITHER_NORMAL specifies dithering on 8-bit displays, but not 16-bit displays. This is usually the best quality/performance tradeoff.
GDK_RGB_DITHER_MAX specifies that dithering will always be done on 8- and 16-bit displays. The quality gain on 16-bit displays is probably not worth the speed penalty.
The gc argument to gdk_draw_rgb_image() is simply passed through to gdk_draw_image() (recall that GdkRGB uses GdkImage internally). The gc components that make sense are used (such as the clip mask, drawing function, and subwindow mode).