This section and following sections are not updated yet and the programs were checked on the older GTK version than 4.10. They will be updated in the near future.
If you want to draw dynamically on the screen, like an image window of gimp graphics editor, the GtkDrawingArea widget is the most suitable widget. You can freely draw or redraw an image in this widget. This is called custom drawing.
GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions. In this section, I will explain:
Cairo is a set of two dimensional graphical drawing functions (or graphics library). There are a lot of documents on Cairo’s website. If you aren’t familiar with Cairo, it is worth reading the tutorial.
The following is an introduction to the Cairo library and how to use it. First, you need to know about surfaces, sources, masks, destinations, cairo context and transformations.
cairo_stroke
is a function to draw a path to the destination by the transfer.The instruction is as follows:
cairo_stroke
to transfer
the paint in the source to the destination.Here’s a simple example program that draws a small square and saves
it as a png file. The path of the file is
src/misc/cairo.c
.
#include <cairo.h>
int
main (int argc, char **argv)
{
cairo_surface_t *surface;
cairo_t *cr;
int width = 100;
int height = 100;
int square_size = 40.0;
/* Create surface and cairo */
surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
cr = cairo_create (surface);
/* Drawing starts here. */
/* Paint the background white */
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
cairo_paint (cr);
/* Draw a black rectangle */
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_set_line_width (cr, 2.0);
cairo_rectangle (cr,
width/2.0 - square_size/2,
height/2.0 - square_size/2,
square_size,
square_size);
cairo_stroke (cr);
/* Write the surface to a png file and clean up cairo and surface. */
cairo_surface_write_to_png (surface, "rectangle.png");
cairo_destroy (cr);
cairo_surface_destroy (surface);
return 0;
}
cairo_surface_t
is the type of a surface.cairo_t
is the type of a cairo context.width
and height
are the size of
surface
. square_size
is the size of a square
to be drawn on the surface.cairo_image_surface_create
creates an image
surface. CAIRO_FORMAT_RGB24
is a constant which means that
each pixel has red, green and blue data, and each data point is an 8
bits number (for 24 bits in total). Modern displays have this type of
color depth. Width and height are in pixels and given as integers.cairo_set_source_rgb
creates a source pattern,
which in this case is a solid white paint. The second to fourth
arguments are red, green and blue color values respectively, and they
are of type float. The values are between zero (0.0) and one (1.0), with
black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).cairo_paint
copies everywhere in the source to
destination. The destination is filled with white pixels with this
command.cairo_set_line_width
set the width of lines. In
this case, the line width is set to be two pixels and will end up that
same size. (It is because the transformation is identity. If the
transformation isn’t identity, for example scaling with the factor
three, the actual width in destination will be six (2x3=6) pixels.)cairo_stroke
transfer the source to destination
through the rectangle in the mask.rectangle.png
.To compile this, change your current directory to
src/misc
and type the following.
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
See the Cairo’s website for further information.
The following is a very simple example.
#include <gtk/gtk.h>
static void
draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) {
int square_size = 40.0;
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
cairo_paint (cr);
cairo_set_line_width (cr, 2.0);
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
cairo_rectangle (cr,
width/2.0 - square_size/2,
height/2.0 - square_size/2,
square_size,
square_size);
cairo_stroke (cr);
}
static void
app_activate (GApplication *app, gpointer user_data) {
GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
GtkWidget *area = gtk_drawing_area_new ();
gtk_window_set_title (GTK_WINDOW (win), "da1");
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL);
gtk_window_set_child (GTK_WINDOW (win), area);
gtk_window_present (GTK_WINDOW (win));
}
#define APPLICATION_ID "com.github.ToshioCP.da1"
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}
The function main
is almost same as before. The two
functions app_activate
and draw_function
are
important in this example.
gtk_drawing_area_set_draw_func
, see Gtk
API Reference – gtk_drawing_area_set_draw_func.The drawing function has five parameters.
void drawing_function (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height,
); gpointer user_data
The first parameter is the GtkDrawingArea widget. You can’t change
any properties, for example content-width
or
content-height
, in this function. The second parameter is a
cairo context given by the widget. The destination surface of the
context is connected to the contents of the widget. What you draw to
this surface will appear in the widget on the screen. The third and
fourth parameters are the size of the destination surface. Now, look at
the program again.
The program is src/misc/da1.c. Compile and run it, then a window with a black rectangle (square) appears. Try resizing the window. The square always appears at the center of the window because the drawing function is invoked each time the window is resized.