Up: index.html, Prev: Section 21, Next: Section 23

GtkDrawingArea and Cairo

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:

  1. Cairo, but only briefly; and
  2. GtkDrawingArea, with a very simple example.

Cairo

Cairo is a set of two dimensional graphical drawing functions (or graphics library). There is a lot of documentation on Cairo’s website. If you aren’t familiar with Cairo, it is worth reading their tutorial.

The following is a gentle introduction to the Cairo library and how to use it. Firstly, in order to use Cairo you need to know about surfaces, sources, masks, destinations, cairo context and transformations.

Stroke a rectangle

The instruction is as follows:

  1. Create a surface. This will be the destination.
  2. Create a cairo context with the surface, the surface will be the destination of the context.
  3. Create a source pattern within the context.
  4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes in the mask.
  5. Use a drawing operator such as cairo_stroke to transfer the paint in the source to the destination.
  6. Save the destination surface to a file if necessary.

Here’s a simple example program that draws a small square and saves it as a png file.

#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;
}

To compile this, type the following.

$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
rectangle.png

See the Cairo’s website for more details.

GtkDrawingArea

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_widget_show (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_FLAGS_NONE);
  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.

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 example again.

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.

Square in the window

Up: index.html, Prev: Section 21, Next: Section 23