Sie sind auf Seite 1von 64

The Cairo graphics library

Home Contents

The Cairo graphics library


Welcome to the Cairo graphics tutorial. This tutorial will teach you basics and some advanced topics of the Cairo 2D vector drawing library. In most examples we will use the GTK+ programming library. This tutorial is done in C programming language.

2D Vector Graphics
There are two different computer graphics. Vec tor and ra ster graphics. Raster graphics represents images as a collection of pixels. Vector graphics is the use of geometrical primitives such as points, lines, curves or polygons to represent images. These primitives are created using mathematical equations. Both types of computer graphics have advantages and disadvantages. The advantages of vector graphics over raster are: smaller size ability to zoom indefinitely moving, scaling, filling or rotating does not degrade the quality of an image

Cairo
Cairo is a library for creating 2D vector graphics. It is written in the C programming language. Bindings for other computer languages exist. Python, Perl, C++, C#, Java. Cairo is a multiplatform library, works on Linux, BSDs, OSX. Cairo supports various backends. X Window System Win32 GDI Mac OS X Quartz PNG PDF PostScript SVG This means, we can use the library to draw on windows on Linux/BSDs, Windows, OSX and we can use the library to create PNG images, PDF files, PostScript files and SVG files. We can compare the cairo library to the GDI + library on Windows OS and the Qu a rtz 2D on Mac OS. Cairo is an open source software library. From version 2.8, the cairo library is part of the GT K+ system.

Compiling examples
The examples are created in the C programming language. We use GNU C compiler to compile them.
gcc example.c -o example `pkg-config --cflags --libs gtk+-3.0`

Note that the order of compiling options is important. Home Contents Top of Page

ZetCode last modified December 8, 201 2

2007 - 201 3 Jan Bodnar

Cairo Definitions

Home Contents

Cairo definitions
In this part of the Cairo graphics tutorial, we will provide some useful definitions for the Cairo graphics library. This will help us better understand the Cairo drawing model.

Context
To do some drawing in Cairo, we must first create a Cairo context. The Cairo context holds all of the graphics state parameters that describe how drawing is to be done. This includes information such as line width, colour, the surface to draw to, and many other things. This allows the actual drawing functions to take fewer arguments to simplify the interface. The gdk_cairo_create() function call creates a cairo context for drawing on a GDK window. A GDK window is one of the supported Cairo backends.
cairo_t *cr; cr = gdk_cairo_create(gtk_widget_get_window(widget));

These two lines create a cairo context. In this example, the context is tied to a Gd kW ind ow. A cairo_t structure contains the current state of the rendering device, including coordinates of yet to be drawn shapes. Technically speaking, the cairo_t objects are called the Cairo contexts. All drawing with Cairo is always done to a cairo_t object. A Cairo context is tied to a specific surface. A PDF, SVG, PNG, GdkWindow etc. The GDK does not wrap the Cairo API. It allows to create a Cairo context, which can be used to draw on GDK windows.

P ath
A path is made up of one or more lines. These lines are connected by two or more anchor points. Paths can be made up of straight lines and curves. There are two kinds of paths. Open and closed paths. In a closed path, starting and ending points meet. In an open path, starting and ending point do not meet. In Cairo, we start with an empty path. First we define a path and then we make them visible by stroking and filling them. One important note. After each cairo_stroke() or cairo_fill() function calls, the path is emptied. We have to define a new path. A path is made of subpaths.

Source
The source is the paint we use in drawing. We can compare the source to a pen or ink, that we will use to draw the outlines and fill the shapes. There are four kinds of basic sources. Colors, gradients, patterns and images.

Surface
A surface is a destination that we are drawing to. We can render documents using the PDF or PostScript surfaces, directly draw to a platform via the Xlib and Win32 surfaces. The documentation mentions the following surfaces:
typedef enum _cairo_surface_type { CAIRO_SURFACE_TYPE_IMAGE, CAIRO_SURFACE_TYPE_PDF, CAIRO_SURFACE_TYPE_PS,

Cairo Definitions
CAIRO_SURFACE_TYPE_XLIB, CAIRO_SURFACE_TYPE_XCB, CAIRO_SURFACE_TYPE_GLITZ, CAIRO_SURFACE_TYPE_QUARTZ, CAIRO_SURFACE_TYPE_WIN32, CAIRO_SURFACE_TYPE_BEOS, CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_SURFACE_TYPE_SVG, CAIRO_SURFACE_TYPE_OS2 } cairo_surface_type_t;

M ask
Before the source is applied to the surface, it is filtered first. The mask is used as a filter. The mask determines, where the sourse is applied and where not. Opaque parts of the mask allow to copy the source. Transparent parts do not let to copy the source to the surface.

P attern
A cairo pattern represents a source when drawing onto a surface. In cairo, a pattern is something that you can read from, that is used as the source or mask of a drawing operation. Patterns in cairo can be solid, surface-based or gradients patterns. In this chapter of the Cairo tutorial, we have given some basic definitions.

Home Contents Top of Page

ZetCode last modified December 1 2, 201 2

2007 - 201 3 Jan Bodnar

Cairo backends

Home Contents

Cairo backends
The Cairo library supports various backends. In this section of the Cairo graphics tutorial, we will use Cairo to create a PNG image, PDF file, SVG file and we will draw on a GTK window.

P NG image
In the first example, we will create a PNG image.
#include <cairo.h> int main(void) { cairo_surface_t *surface; cairo_t *cr; surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 390, 60); cr = cairo_create(surface); cairo_set_source_rgb(cr, 0, 0, 0); cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cr, 40.0); cairo_move_to(cr, 10.0, 50.0); cairo_show_text(cr, "Disziplin ist Macht."); cairo_surface_write_to_png(surface, "image.png"); cairo_destroy(cr); cairo_surface_destroy(surface); } return 0;

This example is a small console application, that will create a PNG image.
#include <cairo.h>

In this header file, we will find declarations of our functions and constants.
cairo_surface_t *surface; cairo_t *cr;

Here we declare a surface and a Cairo context.


surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 390, 60); cr = cairo_create(surface);

We create a surface and a Cairo context. The surface is an 390x60 px image.


cairo_set_source_rgb(cr, 0, 0, 0);

We will draw in black ink.


cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cr, 40.0);

We choose a font type and set its size.


cairo_move_to(cr, 10.0, 50.0); cairo_show_text(cr, "Disziplin ist Macht.");

We move to a (10.0, 50.0) position within the image and draw the text.

Cairo backends
cairo_surface_write_to_png(surface, "image.png");

This function call creates the PNG image.


cairo_destroy(cr); cairo_surface_destroy(surface);

In the end, we clean the resources.

P DF file
In the second example, we will use the Cairo library to create a simple PDF file.
#include <cairo.h> #include <cairo-pdf.h> int main(void) { cairo_surface_t *surface; cairo_t *cr; surface = cairo_pdf_surface_create("pdffile.pdf", 504, 648); cr = cairo_create(surface); cairo_set_source_rgb(cr, 0, 0, 0); cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size (cr, 40.0); cairo_move_to(cr, 10.0, 50.0); cairo_show_text(cr, "Disziplin ist Macht."); cairo_show_page(cr); cairo_surface_destroy(surface); cairo_destroy(cr); } return 0;

We must open the pdf file in a pdf viewer. Linux users can use KPDF or Evince viewers.
surface = cairo_pdf_surface_create("pdffile.pdf", 504, 648);

To render a pdf file, we must create a pdf surface using the cairo_pdf_surface_create() function call. The size of the pdf file is specified in points, which is a standard in typesetting.
cairo_show_page(cr);

The cairo_show_page() finishes rendering of the pdf file.

Figure: PDF file in Evince

SVG file
The next example creates a simple SVG (Scalable Vector Graphics) file. The SVG is one of the hottest technologies these days.
#include <cairo.h> #include <cairo-svg.h>

Cairo backends
int main(void) { cairo_surface_t *surface; cairo_t *cr; surface = cairo_svg_surface_create("svgfile.svg", 390, 60); cr = cairo_create(surface); cairo_set_source_rgb(cr, 0, 0, 0); cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cr, 40.0); cairo_move_to(cr, 10.0, 50.0); cairo_show_text(cr, "Disziplin ist Macht."); cairo_surface_destroy(surface); cairo_destroy(cr); } return 0;

We can use Firefox, Opera or Inkscape programs to open the svgfile.svg file.
surface = cairo_svg_surface_create("svgfile.svg", 390, 60);

To create a SVG file in Cairo, we must create a svg surface using the cairo_svg_surface_create() function call.
cr = cairo_create(surface);

A Cairo context is created from a SVG surface. The Rest of the code is identical to the previous examples.

SVG file in Chrome

GTK W indow
In the last example, we will draw on the GTK window. This backend will be used throughout the rest of the tutorial.
#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0, 0, 0); cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cr, 40.0); cairo_move_to(cr, 10.0, 50.0); cairo_show_text(cr, "Disziplin ist Macht.");

Cairo backends
} int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 400, 90); gtk_window_set_title(GTK_WINDOW(window), "GTK window"); gtk_widget_show_all(window); gtk_main(); } return 0;

The example pops up a centered GTK window, on which we draw the "Disziplin ist Macht" text.
#include <cairo.h> #include <gtk/gtk.h>

We include the necessary Cairo and GTK headers.


static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); return FALSE;

In the on_draw_event() function, we create a Cairo context. It is a graphics object on which we perform the drawing operations. Actual drawing is delegated to the do_drawing() function. After the drawing, the cairo context is destroyed.
static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0, 0, 0); cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cr, 40.0); cairo_move_to(cr, 10.0, 50.0); cairo_show_text(cr, "Disziplin ist Macht.");

The drawing is done in the do_drawing() function.


darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), darea);

We create a GtkDrawingArea widget and add it to the container window. It is used for custom drawing.
g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL);

Cairo backends

When the GtkDrawingArea widget needs to be redrawn, it emits the draw signal. We connect that signal to the on_draw_event() callback.

Figure: GTK window In this chapter we have covered supported Cairo backends.

Home Contents Top of Page

ZetCode last modified December 1 2, 201 2

2007 - 201 3 Jan Bodnar

Basic drawing in Cairo

Home Contents

Basic draw ing in Cairo


In this part of the Cairo graphics tutorial, we will draw some basic primitives. We will draw simple lines, use fill and stroke operations, we will talk about dashes, line caps and line joins.

Lines
Lines are very basic vector objects. To draw a line, we use two function calls. The starting point is specified with the cairo_move_to() call. The ending point of a line is specified with the
cairo_line_to()

call.

#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *); struct { int count; double coordx[100]; double coordy[100]; } glob; static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0, 0, 0); cairo_set_line_width(cr, 0.5); int i, j; for (i = 0; i <= glob.count - 1; i++ ) { for (j = 0; j <= glob.count - 1; j++ ) { cairo_move_to(cr, glob.coordx[i], glob.coordy[i]); cairo_line_to(cr, glob.coordx[j], glob.coordy[j]); } } glob.count = 0; cairo_stroke(cr);

static gboolean clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data) { if (event->button == 1) { glob.coordx[glob.count] = event->x; glob.coordy[glob.count++] = event->y; } if (event->button == 3) { gtk_widget_queue_draw(widget); } } return TRUE;

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; glob.count = 0;

Basic drawing in Cairo


gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), darea); gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(window, "button-press-event", G_CALLBACK(clicked), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 400, 300); gtk_window_set_title(GTK_WINDOW(window), "Lines"); gtk_widget_show_all(window); gtk_main(); } return 0;

In our example, we click randomly on a window with a left mouse button. Each click is stored in an array. When we right click on the window, all points are connected with every point in the array. This way, we can create some interesting objects. Right clicking on the drawn object clears the window, and we can click another object.
cairo_set_source_rgb(cr, 0, 0, 0); cairo_set_line_width (cr, 0.5);

The lines will be drawn in black ink and will be 0.5 points wide.
int i, j; for (i = 0; i <= glob.count - 1; i++ ) { for (j = 0; j <= glob.count - 1; j++ ) { cairo_move_to(cr, glob.coordx[i], glob.coordy[i]); cairo_line_to(cr, glob.coordx[j], glob.coordy[j]); } }

We connect every point from the array to every other point.


cairo_stroke(cr);

The cairo_stroke() call draws the lines.


g_signal_connect(window, "button-press-event", G_CALLBACK(clicked), NULL);

We connect the button-press-event to the clicked callback.


if (event->button == 1) { glob.coordx[glob.count] = event->x; glob.coordy[glob.count++] = event->y; }

Inside the clicked callback, we determine, if we there was a left or right click. If we click with a left mouse button, we store the x, y coordinates into the arrays.
if (event->button == 3) { gtk_widget_queue_draw(widget); }

By right clicking, we redraw the window.

Basic drawing in Cairo

Figure: Lines

Fill and stroke


The stroke operation draws the outlines of shapes and the fill operation fills the insides of shapes.
#include <cairo.h> #include <gtk/gtk.h> #include <math.h> static void do_drawing(cairo_t *, GtkWidget *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr, widget); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr, GtkWidget *widget) { GtkWidget *win = gtk_widget_get_toplevel(widget); int width, height; gtk_window_get_size(GTK_WINDOW(win), &width, &height); cairo_set_line_width(cr, 9); cairo_set_source_rgb(cr, 0.69, 0.19, 0); cairo_translate(cr, width/2, height/2); cairo_arc(cr, 0, 0, 50, 0, 2 * M_PI); cairo_stroke_preserve(cr); cairo_set_source_rgb(cr, 0.3, 0.4, 0.6); cairo_fill(cr);

int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

Basic drawing in Cairo


gtk_window_set_default_size(GTK_WINDOW(window), 300, 200); gtk_window_set_title(GTK_WINDOW(window), "Fill & stroke"); gtk_widget_show_all(window); gtk_main(); } return 0;

In our example, we will draw a circle and fill it with a solid color.
#include <math.h>

This header file is needed for the M_PI constant.


GtkWidget *win = gtk_widget_get_toplevel(widget); int width, height; gtk_window_get_size(GTK_WINDOW(win), &width, &height);

Here we get the width and height of the window. We will need these values, when we draw the circle. The circle will be resized, when we resize the window.
cairo_set_line_width(cr, 9); cairo_set_source_rgb(cr, 0.69, 0.19, 0);

We set a line width with the set_line_width() method. We set the source to some dark red colour using the set_source_rgb() method.
cairo_translate(cr, width/2, height/2); cairo_arc(cr, 0, 0, 50, 0, 2 * M_PI); cairo_stroke_preserve(cr);

With the cairo_translate() method, we move the drawing origin to the center of the window. We want our circle to be centered. The arc() method adds a new circular path to the cairo drawing context. Finally, the stroke_preserve() method draws the outline of the circle. Unlike the
stroke()

method, it also preserves the shape for later drawing.

cairo_set_source_rgb(cr, 0.3, 0.4, 0.6); cairo_fill(cr);

Here we fill the cirle with blue color.

Figure: Fill and stroke

P en dashes
Each line can be drawn with a different pen dash. It defines the style of the line. The dash is used by the cairo_stroke() function call. The dash pattern is specified by the cairo_set_dash() function. The pattern is set by the dash array, which is an array of positive floating point values. They set the on and off parts of the dash pattern. We also specify the length of the array and the offset value. If the length is 0, the dashing is disabled. If it is 1, a symmetric pattern is asumed with alternating on and off portions of the size specified by the single value in dashes.

Basic drawing in Cairo


static void do_drawing(cairo_t *cr) { cairo_set_source_rgba(cr, 0, 0, 0, 1); static const double dashed1[] = {4.0, 21.0, 2.0}; static int len1 = sizeof(dashed1) / sizeof(dashed1[0]); static const double dashed2[] = {14.0, 6.0}; static int len2 = sizeof(dashed2) / sizeof(dashed2[0]); static const double dashed3[] = {1.0}; cairo_set_line_width(cr, 1.5); cairo_set_dash(cr, dashed1, len1, 0); cairo_move_to(cr, 40, 30); cairo_line_to(cr, 200, 30); cairo_stroke(cr); cairo_set_dash(cr, dashed2, len2, 1); cairo_move_to(cr, 40, 50); cairo_line_to(cr, 200, 50); cairo_stroke(cr); cairo_set_dash(cr, dashed3, 1, 0); cairo_move_to(cr, 40, 70); cairo_line_to(cr, 200, 70); cairo_stroke(cr);

In this example, we will draw three lines with different dash patterns.
static const double dashed1[] = {4.0, 21.0, 2.0};

We have a pattern of three numbers. We have 4 points drawn, 21 not drawn and 2 drawn. Then 4 points not drawn, 21 points drawn and 2 not drawn. This pattern takes turns until the end of the line.
static int len1 = sizeof(dashed1) / sizeof(dashed1[0]);

We get the size of the array.


cairo_set_dash(cr, dashed1, len1, 0);

We set the dash.


static const double dashed3[] = {1.0}; ... cairo_set_dash(cr, dashed3, 1, 0); cairo_move_to(cr, 40, 70); cairo_line_to(cr, 200, 70); cairo_stroke(cr);

These lines create a line with a pen dash of a symmetric pattern of alternating single on and off points.

Figure: Dashes

Line caps
The line caps are endpoints of lines.

Basic drawing in Cairo

CAIRO_LINE_CAP_SQUARE CAIRO_LINE_CAP_ROUND CAIRO_LINE_CAP_BUTT There are three different line cap styles in Cairo.

Figure: Square, round and butt caps A line with a CAIRO_LINE_CAP_SQUARE cap will have a different size, than a line with a
CAIRO_LINE_CAP_BUTT

cap. If a line is width px wide, the line with a CAIRO_LINE_CAP_SQUARE cap

will be exactly width px greater in size. width/2 px at the beginning and width/2 px at the end.
static void do_drawing(cairo_t *cr) { cairo_set_line_width(cr, 10); cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); cairo_move_to(cr, 30, 50); cairo_line_to(cr, 150, 50); cairo_stroke(cr); cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); cairo_move_to(cr, 30, 90); cairo_line_to(cr, 150, 90); cairo_stroke(cr); cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE); cairo_move_to(cr, 30, 130); cairo_line_to(cr, 150, 130); cairo_stroke(cr); cairo_set_line_width(cr, 1.5); cairo_move_to(cr, 30, 40); cairo_line_to(cr, 30, 140); cairo_stroke(cr); cairo_move_to(cr, 150, 40); cairo_line_to(cr, 150, 140); cairo_stroke(cr); cairo_move_to(cr, 155, 40); cairo_line_to(cr, 155, 140); cairo_stroke(cr);

The example draws three lines with three different caps. It will also graphically demonstrate the differences is size of the lines.
cairo_set_line_width(cr, 10);

Our lines will be 10 px wide.


cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); cairo_move_to(cr, 30, 90); cairo_line_to(cr, 150, 90); cairo_stroke(cr);

Here we draw a horizontal line with a CAIRO_LINE_CAP_ROUND cap.


cairo_set_line_width(cr, 1.5); cairo_move_to(cr, 30, 40); cairo_line_to(cr, 30, 140); cairo_stroke(cr);

Basic drawing in Cairo

This is one of the three vertical lines used to demostrate the differences in size.

Figure: Line caps

Line joins
The lines can be joined using three different join styles. CAIRO_LINE_JOIN_MITER CAIRO_LINE_JOIN_BEVEL CAIRO_LINE_JOIN_ROUND

Figure: Bevel, Round, Miter line joins

static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0.1, 0, 0); cairo_rectangle(cr, 30, 30, 100, 100); cairo_set_line_width(cr, 14); cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER); cairo_stroke(cr); cairo_rectangle(cr, 160, 30, 100, 100); cairo_set_line_width(cr, 14); cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL); cairo_stroke(cr); cairo_rectangle(cr, 100, 160, 100, 100); cairo_set_line_width(cr, 14); cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); cairo_stroke(cr);

In this example, we draw three thick rectangles with various line joins.
cairo_rectangle(cr, 30, 30, 100, 100); cairo_set_line_width(cr, 14); cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER); cairo_stroke(cr);

In this code example, we draw a rectangle with CAIRO_LINE_JOIN_MITER join style. The lines are 14 px wide.

Basic drawing in Cairo

Figure: Line joins In this chapter, we did some basic drawing.

Home Contents Top of Page

ZetCode last modified December 1 2, 201 2

2007 - 201 3 Jan Bodnar

Shapes and fills in Cairo

Home Contents

Shapes and fills in Cairo


In this part of the Cairo tutorial, we will create some basic and more advanced shapes. We will fill them with solid colours, patterns and gradients. Gradients will be covered in a separate chapter.

Basic shapes
The Cairo API has some basic functions to create simple shapes.
static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); cairo_set_line_width(cr, 1); cairo_rectangle(cr, 20, 20, 120, 80); cairo_rectangle(cr, 180, 20, 80, 80); cairo_stroke_preserve(cr); cairo_fill(cr); cairo_arc(cr, 330, 60, 40, 0, 2*M_PI); cairo_stroke_preserve(cr); cairo_fill(cr); cairo_arc(cr, 90, 160, 40, M_PI/4, M_PI); cairo_close_path(cr); cairo_stroke_preserve(cr); cairo_fill(cr); cairo_translate(cr, 220, 180); cairo_scale(cr, 1, 0.7); cairo_arc(cr, 0, 0, 50, 0, 2*M_PI); cairo_stroke_preserve(cr); cairo_fill(cr);

In this example, we will create a rectangle, a square, a circle, an arc and an ellipse.
cairo_rectangle(cr, 20, 20, 120, 80); cairo_rectangle(cr, 180, 20, 80, 80);

The cairo_rectangle() is used to create both squares and rectangles. A square is just a specific type of a rectangle.
cairo_arc(cr, 330, 60, 40, 0, 2*M_PI);

This line creates a circle.


cairo_scale(cr, 1, 0.7); cairo_arc(cr, 0, 0, 50, 0, 2*M_PI);

We use the cairo_scale() function call to create an ellipse.

Shapes and fills in Cairo

Figure: Basic shapes Other shapes can be created using a combination of basic primitives.
#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *); int points[11][2] = { { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 }, { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 }, { 40, 190 }, { 50, 125 }, { 0, 85 } }; static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); cairo_set_line_width(cr, 1); gint i; for (i = 0; i < 10; i++) { cairo_line_to(cr, points[i][0], points[i][1]); } cairo_close_path(cr); cairo_stroke_preserve(cr); cairo_fill(cr); cairo_move_to(cr, 240, 40); cairo_line_to(cr, 240, 160); cairo_line_to(cr, 350, 160); cairo_close_path(cr); cairo_stroke_preserve(cr); cairo_fill(cr); cairo_move_to(cr, 380, 40); cairo_line_to(cr, 380, 160); cairo_line_to(cr, 450, 160); cairo_curve_to(cr, 440, 155, 380, 145, 380, 40); cairo_stroke_preserve(cr); cairo_fill(cr);

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

Shapes and fills in Cairo


gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 460, 240); gtk_window_set_title(GTK_WINDOW(window), "Other shapes"); gtk_widget_show_all(window); gtk_main(); } return 0;

In this example, we create a star object a triangle and a modified triangle. These objects are created using lines and one curve.
gint i; for (i = 0; i < 10; i++ ) { cairo_line_to(cr, points[i][0], points[i][1]); } cairo_close_path(cr);

The star is drawn by joining all the points that are in the points array. The star is finished by calling the cairo_close_path() function, which joins the last two points of a star.
cairo_move_to(cr, 380, 40); cairo_line_to(cr, 380, 160); cairo_line_to(cr, 450, 160); cairo_curve_to(cr, 440, 155, 380, 145, 380, 40);

The modified triangle is a simple combination of two lines and one curve.

Figure: Other shapes

Fills
Fills fill the interiors of shapes. Fills can be solid colours, patters or gradients.

Solid colou r s
A colour is an object representing a combination of Red, Green, and Blue (RGB) intensity values. Cairo valid RGB values are in the range 0 to 1.
static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0.5, 0.5, 1); cairo_rectangle(cr, 20, 20, 100, 100); cairo_fill(cr); cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); cairo_rectangle(cr, 150, 20, 100, 100); cairo_fill(cr); cairo_set_source_rgb(cr, 0, 0.3, 0); cairo_rectangle(cr, 20, 140, 100, 100); cairo_fill(cr);

Shapes and fills in Cairo


cairo_set_source_rgb(cr, 1, 0, 0.5); cairo_rectangle(cr, 150, 140, 100, 100); cairo_fill(cr);

In the example we draw four coloured rectangles.


cairo_set_source_rgb(cr, 0.5, 0.5, 1); cairo_rectangle(cr, 20, 20, 100, 100); cairo_fill(cr);

The cairo_set_source_rgb() function call sets the source to an opaque colour. The parameters are the Red, Green, Blue intensity values. The source is used to fill the interior of a rectangle by calling the cairo_fill() function.

Figure: Solid colours

Patter ns
Patterns are complex graphical objects that can fill the shapes.
#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *); cairo_surface_t cairo_surface_t cairo_surface_t cairo_surface_t *surface1; *surface2; *surface3; *surface4;

static void create_surfaces() { surface1 = cairo_image_surface_create_from_png("blueweb.png"); surface2 = cairo_image_surface_create_from_png("maple.png"); surface3 = cairo_image_surface_create_from_png("crack.png"); surface4 = cairo_image_surface_create_from_png("chocolate.png"); } static void destroy_surfaces() { cairo_surface_destroy(surface1); cairo_surface_destroy(surface2); cairo_surface_destroy(surface3); cairo_surface_destroy(surface4); } static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr) { cairo_pattern_t *pattern1; cairo_pattern_t *pattern2;

Shapes and fills in Cairo


cairo_pattern_t *pattern3; cairo_pattern_t *pattern4; pattern1 pattern2 pattern3 pattern4 = = = = cairo_pattern_create_for_surface(surface1); cairo_pattern_create_for_surface(surface2); cairo_pattern_create_for_surface(surface3); cairo_pattern_create_for_surface(surface4);

cairo_set_source(cr, pattern1); cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); cairo_rectangle(cr, 20, 20, 100, 100); cairo_fill(cr); cairo_set_source(cr, pattern2); cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); cairo_rectangle(cr, 150, 20, 100, 100); cairo_fill(cr); cairo_set_source(cr, pattern3); cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); cairo_rectangle(cr, 20, 140, 100, 100); cairo_fill(cr); cairo_set_source(cr, pattern4); cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); cairo_rectangle(cr, 150, 140, 100, 100); cairo_fill(cr); cairo_pattern_destroy(pattern1); cairo_pattern_destroy(pattern2); cairo_pattern_destroy(pattern3); cairo_pattern_destroy(pattern4);

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); create_surfaces(); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 270, 260); gtk_window_set_title(GTK_WINDOW(window), "Patterns"); gtk_widget_show_all(window); gtk_main(); destroy_surfaces(); } return 0;

In this example we draw four rectangles again. This time, we fill them with some patterns. We use four pattern images from the Gim p image manipulation program. We must retain the original size of those patterns, because we are going to tile them. We create image surfaces outside the on_draw_event() function. It would not be efficient to read from harddisk each time, the window needs to be redrawn.
pattern1 = cairo_pattern_create_for_surface(surface1);

We create a pattern from the surface by calling the cairo_pattern_create_for_surface() function.

Shapes and fills in Cairo


cairo_set_source(cr, pattern1); cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); cairo_rectangle(cr, 20, 20, 100, 100); cairo_fill(cr);

Here we draw our first rectangle. The cairo_set_source() tells the Cairo context to use a pattern as a source for drawing. The image patterns may not fit exactly the shape. We set the mode to
CAIRO_EXTEND_REPEAT, which causes

the pattern to be tiled by repeating. The cairo_rectangle()

creates a rectangular path. Finally, cairo_fill() fills the path with the source. This chapter covered Cairo shapes and fills.

Home Contents Top of Page

ZetCode last modified December 1 3, 201 2

2007 - 201 3 Jan Bodnar

Gradients

Home Contents

Gradients
In this part of the Cairo graphics tutorial, we will cover gradients. We will mention linear and radial gradients. In computer graphics, gradient is a smooth blending of shades from light to dark or from one colour to another. In 2D drawing programs and paint programs, gradients are used to create colourful backgrounds and special effects as well as to simulate lights and shadows. (answers.com)

Linear gradients
Linear gradients are blendings of colours or shades of colours along a line. They are created with the cairo_pattern_create_linear() function.
#include <cairo.h> #include <gtk/gtk.h> void draw_gradient1(cairo_t *); void draw_gradient2(cairo_t *); void draw_gradient3(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); draw_gradient1(cr); draw_gradient2(cr); draw_gradient3(cr); cairo_destroy(cr); } return FALSE;

void draw_gradient1(cairo_t *cr) { cairo_pattern_t *pat1; pat1 = cairo_pattern_create_linear(0.0, 0.0,

350.0, 350.0);

gdouble j; gint count = 1; for ( j = 0.1; j < 1; j += 0.1 ) { if (( count % 2 )) { cairo_pattern_add_color_stop_rgb(pat1, j, 0, 0, 0); } else { cairo_pattern_add_color_stop_rgb(pat1, j, 1, 0, 0); } count++; } cairo_rectangle(cr, 20, 20, 300, 100); cairo_set_source(cr, pat1); cairo_fill(cr); } cairo_pattern_destroy(pat1);

void draw_gradient2(cairo_t *cr) { cairo_pattern_t *pat2; pat2 = cairo_pattern_create_linear(0.0, 0.0,

350.0, 0.0);

gdouble i; gint count = 1; for ( i = 0.05; i < 0.95; i += 0.025 ) { if (( count % 2 )) { cairo_pattern_add_color_stop_rgb(pat2, i, 0, 0, 0); } else { cairo_pattern_add_color_stop_rgb(pat2, i, 0, 0, 1);

Gradients
} count++;

cairo_rectangle(cr, 20, 140, 300, 100); cairo_set_source(cr, pat2); cairo_fill(cr); } cairo_pattern_destroy(pat2);

void draw_gradient3(cairo_t *cr) { cairo_pattern_t *pat3; pat3 = cairo_pattern_create_linear(20.0, 260.0, 20.0, 360.0); cairo_pattern_add_color_stop_rgb(pat3, 0.1, 0, 0, 0); cairo_pattern_add_color_stop_rgb(pat3, 0.5, 1, 1, 0); cairo_pattern_add_color_stop_rgb(pat3, 0.9, 0, 0, 0); cairo_rectangle(cr, 20, 260, 300, 100); cairo_set_source(cr, pat3); cairo_fill(cr); } cairo_pattern_destroy(pat3);

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 340, 390); gtk_window_set_title(GTK_WINDOW(window), "Linear gradients"); gtk_widget_set_app_paintable(window, TRUE); gtk_widget_show_all(window); gtk_main(); } return 0;

The example draws three rectangles filled with linear gradients.


pat3 = cairo_pattern_create_linear(20.0, 260.0, 20.0, 360.0);

Here we create a linear gradient pattern. The parameters specify the line, along which we draw the gradient. In our case it is a vertical line.
cairo_pattern_add_color_stop_rgb(pat3, 0.1, 0, 0, 0); cairo_pattern_add_color_stop_rgb(pat3, 0.5, 1, 1, 0); cairo_pattern_add_color_stop_rgb(pat3, 0.9, 0, 0, 0);

We define colour stops to produce our gradient pattern. In this case, the gradient is a blending of black and yellow colours. By adding two black and one yellow stops, we create a horizontal gradient pattern. What these stops actually mean? In our case, we begin with black colour, which will stop at 1/10 of the size. Then we begin to gradually paint in yellow, which will culminate at the centre of the shape. The yellow colour stops at 9/10 of the size, where we begin painting in black again, until the end.

Gradients

Figure: Linear gradients

Radial gradients
Radial gradients are blendings of colours or shades of colours between two circles. The
cairo_pattern_create_radial() #include <cairo.h> #include <math.h> #include <gtk/gtk.h> void draw_gradient1(cairo_t *); void draw_gradient2(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); draw_gradient1(cr); draw_gradient2(cr); cairo_destroy(cr); } return FALSE;

function s is used to create radial gradients in Cairo.

void draw_gradient1(cairo_t *cr) { cairo_pattern_t *r1; cairo_set_source_rgba(cr, 0, 0, 0, 1); cairo_set_line_width(cr, 12); cairo_translate(cr, 60, 60); r1 = cairo_pattern_create_radial(30, 30, 10, 30, 30, 90); cairo_pattern_add_color_stop_rgba(r1, 0, 1, 1, 1, 1); cairo_pattern_add_color_stop_rgba(r1, 1, 0.6, 0.6, 0.6, 1); cairo_set_source(cr, r1); cairo_arc(cr, 0, 0, 40, 0, M_PI * 2); cairo_fill(cr); } cairo_pattern_destroy(r1);

void draw_gradient2(cairo_t *cr) { cairo_pattern_t *r2; cairo_translate(cr, 120, 0); r2 = cairo_pattern_create_radial(0, 0, 10, 0, 0, 40); cairo_pattern_add_color_stop_rgb(r2, 0, 1, 1, 0);

Gradients
cairo_pattern_add_color_stop_rgb(r2, 0.8, 0, 0, 0); cairo_set_source(cr, r2); cairo_arc(cr, 0, 0, 40, 0, M_PI * 2); cairo_fill(cr);

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 300, 200); gtk_window_set_title(GTK_WINDOW(window), "Radial gradients"); gtk_widget_set_app_paintable(window, TRUE); gtk_widget_show_all(window); gtk_main(); } return 0;

In the example, we draw two radial gradients.


r1 = cairo_pattern_create_radial(30, 30, 10, 30, 30, 90); cairo_pattern_add_color_stop_rgba(r1, 0, 1, 1, 1, 1); cairo_pattern_add_color_stop_rgba(r1, 1, 0.6, 0.6, 0.6, 1); cairo_set_source(cr, r1); cairo_arc(cr, 0, 0, 40, 0, M_PI * 2); cairo_fill(cr);

We draw a circle and fill its inside with a radial gradient. The radial gradient is defined by two circles. The cairo_pattern_add_color_stop_rgba() function defines the colours. We can experiment with the position of the circles or the length of their radius. In the first gradient example, we have created an object which resembles a 3D shape.
r2 = cairo_pattern_create_radial(0, 0, 10, 0, 0, 40); cairo_pattern_add_color_stop_rgb(r2, 0, 1, 1, 0); cairo_pattern_add_color_stop_rgb(r2, 0.8, 0, 0, 0); cairo_set_source(cr, r2); cairo_arc(cr, 0, 0, 40, 0, M_PI * 2); cairo_fill(cr);

In this example, the circles that define the radial gradient and the custom drawn circle have a common center point.

Figure: Radial gradients

Gradients

In this chapter of the Cairo graphics tutorial we have covered gradients.

Home Contents Top of Page

ZetCode last modified December 9, 201 2

2007 - 201 3 Jan Bodnar

Transparency

Home Contents

Transparency
In this part of the Cairo C API tutorial, we will talk about transparency. We will provide some basic definitions and two interesting transparency effects. Transparency is the quality of being able to see through a material. The easiest way to understand transparency is to imagine a piece of glass or water. Technically, the rays of light can go through the glass and this way we can see objects behind the glass. In computer graphics, we can achieve transparency effects using a lp h a c om p ositing. Alpha compositing is the process of combining an image with a background to create the appearance of partial transparency. The composition process uses an a lp h a c h a nnel. Alpha channel is an 8-bit layer in a graphics file format that is used for expressing translucency (transparency). The extra eight bits per pixel serves as a mask and represents 256 levels of translucency. (answers.com, wikipedia.org)

Transparent rectangles
The first example will draw ten rectangles with different levels of transparency.
static void do_drawing(cairo_t *cr) { gint i; for ( i = 1; i <= 10; i++) { cairo_set_source_rgba(cr, 0, 0, 1, i*0.1); cairo_rectangle(cr, 50*i, 20, 40, 40); cairo_fill(cr); } }

The cairo_set_source_rgba() has an optional alpha parameter to provide transparency. This code creates ten rectangles with alpha values from 0.1 ... 1.

Figure: Transparency

P uff effect
In the following example, we create a puff effect. The example will display a growing centered text that will gradually fade out from some point. This is a very common effect which we can often see in flash animations. The cairo_paint_with_alpha() method is crucial to create the effect.
#include <cairo.h> #include <gtk/gtk.h> void do_drawing(cairo_t *, GtkWidget *); struct { gboolean timer; gdouble alpha; gdouble size; } glob; static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget));

Transparency
do_drawing(cr, widget); cairo_destroy(cr); } return FALSE;

void do_drawing(cairo_t *cr, GtkWidget *widget) { cairo_text_extents_t extents; GtkWidget *win = gtk_widget_get_toplevel(widget); gint width, height; gtk_window_get_size(GTK_WINDOW(win), &width, &height); gint x = width/2; gint y = height/2; cairo_set_source_rgb(cr, 0.5, 0, 0); cairo_paint(cr); cairo_select_font_face(cr, "Courier", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); glob.size += 0.8; if (glob.size > 20) { glob.alpha -= 0.01; } cairo_set_font_size(cr, glob.size); cairo_set_source_rgb(cr, 1, 1, 1); cairo_text_extents(cr, "ZetCode", &extents); cairo_move_to(cr, x - extents.width/2, y); cairo_text_path(cr, "ZetCode"); cairo_clip(cr); cairo_paint_with_alpha(cr, glob.alpha); if (glob.alpha <= 0) { glob.timer = FALSE; }

static gboolean time_handler(GtkWidget *widget) { if (!glob.timer) return FALSE; gtk_widget_queue_draw(widget); } return TRUE;

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; glob.timer = TRUE; glob.alpha = 1.0; glob.size = 1.0; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 350, 200); gtk_window_set_title(GTK_WINDOW(window), "Puff"); g_timeout_add(14, (GSourceFunc) time_handler, (gpointer) window);

Transparency
gtk_widget_show_all(window); gtk_main(); } return 0;

The example creates a growing and fading text on the window.


struct { gboolean timer; gdouble alpha; gdouble size; } glob;

Here we define some variables inside a structure. This is used to avoid using globals.
draw_text(cr, widget);

The actual drawing of the text is delegated to the draw_text() function.


GtkWidget *win = gtk_widget_get_toplevel(widget); gint width, height; gtk_window_get_size(GTK_WINDOW(win), &width, &height); gint x = width/2; gint y = height/2;

The text is going to be centered on the window. Therefore we need to find out the size of the parent widget.
cairo_set_source_rgb(cr, 0.5, 0, 0); cairo_paint(cr);

The background of the window is filled with some dark red colour.
cairo_select_font_face(cr, "Courier", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);

The text is going to be in Courier bold font.


glob.size += 0.8; if (glob.size > 20) { glob.alpha -= 0.01; }

The size of the text is increased by 0.8 units. After it has reached 20 units, the alpha value starts decreasing. And the text fades away slowly.
cairo_text_extents(cr, "ZetCode", &extents); cairo_move_to(cr, x - extents.width/2, y);

We get the text metrics. We will use only the text width. We move to a position where the text will be centered on the window.
cairo_text_path(cr, "ZetCode"); cairo_clip(cr); cairo_paint_with_alpha(cr, glob.alpha);

We get the path of the text with the cairo_text_path() method. We restrict the painting to the current path using the cairo_clip() method. The cairo_paint_with_alpha() method paints the current source everywhere within the current clip region using a mask of the alpha value.
glob.timer = TRUE; glob.alpha = 1.0;

Transparency
glob.size = 1.0;

We initiate three variables.


static gboolean time_handler(GtkWidget *widget) { if (!glob.timer) return FALSE; gtk_widget_queue_draw(widget); return TRUE;

The main function of the time_handler call is to redraw the window regularly. When the function returns FALSE, the timeout function will cease to work.
g_timeout_add(14, (GSourceFunc) time_handler, (gpointer) window);

We create a timer function. This function will call time_handler every 14 ms.

Figure: Puff effect

W aiting demo
In this examle, we use transparency effect to create a waiting demo. We will draw 8 lines that will gradually fade out creating an illusion, that a line is moving. Such effects are often used to inform users, that a lengthy task is going on behind the scenes. An example is streaming video over the internet.
#include <cairo.h> #include <gtk/gtk.h> #include <math.h> static void do_drawing(cairo_t *, GtkWidget *); struct { gushort count; } glob; static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr, widget); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr, GtkWidget *widget) { static gdouble const trs[8][8] = { { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 }, { 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 }, { 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 }, { 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65}, { 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 }, { 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 }, { 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 }, { 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, } };

Transparency
GtkWidget *win = gtk_widget_get_toplevel(widget); gint width, height; gtk_window_get_size(GTK_WINDOW(win), &width, &height); cairo_translate(cr, width/2, height/2); gint i = 0; for (i = 0; i < 8; i++) { cairo_set_line_width(cr, 3); cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); cairo_set_source_rgba(cr, 0, 0, 0, trs[glob.count%8][i]); cairo_move_to(cr, 0.0, -10.0); cairo_line_to(cr, 0.0, -40.0); cairo_rotate(cr, M_PI/4); } cairo_stroke(cr);

static gboolean time_handler(GtkWidget *widget) { glob.count += 1; gtk_widget_queue_draw(widget); } return TRUE;

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; glob.count = 0; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 250, 150); gtk_window_set_title(GTK_WINDOW(window), "Waiting demo"); g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) window); gtk_widget_show_all(window); gtk_main(); } return 0;

We draw eight lines with eight different alpha values.


static gdouble const trs[8][8] = { { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 }, { 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 }, { 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 }, { 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65}, { 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 }, { 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 }, { 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 }, { 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, } };

This is a two dimensional array of transparency values used in this demo. There are 8 rows, each for one state. Each of the 8 lines will continuosly use these values.
cairo_set_line_width(cr, 3);

Transparency
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);

We make the lines a bit thicker, so that they are better visible. We draw the lines with rouded caps.
cairo_set_source_rgba(cr, 0, 0, 0, trs[glob.count%8][i]);

Here we define the transparency value for a line.


cairo_move_to(cr, 0.0, -10.0); cairo_line_to(cr, 0.0, -40.0); cairo_rotate(cr, M_PI/4);

These code will draw each of the eight lines.


g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) window);

We use a timer function to create animation.

Figure: Waiting demo In this part of the Cairo tutorial, we have covered transparency.

Home Contents Top of Page

ZetCode last modified December 1 3, 201 2

2007 - 201 3 Jan Bodnar

Compositing

Home Contents

Compositing
In this part of the Cairo graphics programming tutorial, we will define compositing operations. Com p ositing is the combining of visual elements from separate sources into single images. They are used to create the illusion that all those elements are parts of the same scene. Compositing is videly used in film industry to create crowds, entire new worlds which would be expensive or impossible to create otherwise. (wikipedia.org)

Operations
There are several compositing operations. The Cairo graphics library has 14 different compositing operations.
#include <cairo.h> #include <gtk/gtk.h> void do_drawing(cairo_t *cr, gint x, gint w, gint h, cairo_operator_t op) { cairo_t *first_cr, *second_cr; cairo_surface_t *first, *second; first = cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, w, h); second = cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, w, h); first_cr = cairo_create(first); cairo_set_source_rgb(first_cr, 0, 0, 0.4); cairo_rectangle(first_cr, x, 20, 50, 50); cairo_fill(first_cr); second_cr = cairo_create(second); cairo_set_source_rgb(second_cr, 0.5, 0.5, 0); cairo_rectangle(second_cr, x+10, 40, 50, 50); cairo_fill(second_cr); cairo_set_operator(first_cr, op); cairo_set_source_surface(first_cr, second, 0, 0); cairo_paint(first_cr); cairo_set_source_surface(cr, first, 0, 0); cairo_paint(cr); cairo_surface_destroy(first); cairo_surface_destroy(second); cairo_destroy(first_cr); cairo_destroy(second_cr); } static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); cairo_operator_t oper[] = { CAIRO_OPERATOR_DEST_OVER, CAIRO_OPERATOR_DEST_IN, CAIRO_OPERATOR_OUT, CAIRO_OPERATOR_ADD, CAIRO_OPERATOR_ATOP, CAIRO_OPERATOR_DEST_ATOP, }; GtkWidget *win = gtk_widget_get_toplevel(widget);

Compositing
gint width, height; gtk_window_get_size(GTK_WINDOW(win), &width, &height); gint i; gint x, y; for(x=20, y=20, i=0; i < 6; x+=80, i++) { do_drawing(cr, x, width, height, oper[i] ); } cairo_destroy(cr); } return FALSE;

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 510, 120); gtk_window_set_title(GTK_WINDOW(window), "Compositing operations"); gtk_widget_show_all(window); gtk_main(); } return 0;

In our example, we will show 6 different compositing operations on two squares.


first = cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, w, h); second = cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, w, h);

We create two surfaces.


first_cr = cairo_create(first); cairo_set_source_rgb(first_cr, 0, 0, 0.4); cairo_rectangle(first_cr, x, 20, 50, 50); cairo_fill(first_cr);

We draw a rectangle into the surface.


cairo_set_operator(first_cr, op); cairo_set_source_surface(first_cr, second, 0, 0); cairo_paint(first_cr);

We apply the compositing operation on the surfaces.


cairo_set_source_surface(cr, first, 0, 0); cairo_paint(cr);

Finally we draw the outcome onto the GTK+ window.


cairo_operator_t oper[] = { CAIRO_OPERATOR_DEST_OVER, CAIRO_OPERATOR_DEST_IN, CAIRO_OPERATOR_OUT, CAIRO_OPERATOR_ADD,

Compositing
CAIRO_OPERATOR_ATOP, CAIRO_OPERATOR_DEST_ATOP,

};

In our example, we use these six compositing operations.

Figure: Compositing operations This chapter covered Cairo compositing.

Home Contents Top of Page

ZetCode last modified December 1 3, 201 2

2007 - 201 3 Jan Bodnar

Clipping and masking

Home Contents

Clipping and masking


In this part of the Cairo tutorial, we will talk about clipping and masking.

Clipping
Clipping is restricting of drawing to a certain area. This is done for effeciency reasons and to create interesting effects. In the following example we will be clipping an image.
#include <cairo.h> #include <gtk/gtk.h> #include <math.h> static void do_drawing(cairo_t *, GtkWidget *); struct { cairo_surface_t *image; } glob; static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr, widget); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr, GtkWidget *widget) { static gint pos_x = 128; static gint pos_y = 128; static gint radius = 40; static gint delta[] = { 3, 3 }; GtkWidget *win = gtk_widget_get_toplevel(widget); gint width, height; gtk_window_get_size(GTK_WINDOW(win), &width, &height); if (pos_x < 0 + radius) { delta[0] = rand() % 4 + 5; } else if (pos_x > width - radius) { delta[0] = -(rand() % 4 + 5); } if (pos_y < 0 + radius) { delta[1] = rand() % 4 + 5; } else if (pos_y > height - radius) { delta[1] = -(rand() % 4 + 5); } pos_x += delta[0]; pos_y += delta[1]; cairo_set_source_surface(cr, glob.image, 1, 1); cairo_arc(cr, pos_x, pos_y, radius, 0, 2*M_PI); cairo_clip(cr); cairo_paint(cr);

static gboolean time_handler(GtkWidget *widget) { gtk_widget_queue_draw(widget); return TRUE; } int main(int argc, char *argv[]) {

Clipping and masking


GtkWidget *window; GtkWidget *darea; gint width, height; glob.image = cairo_image_surface_create_from_png("turnacastle.png"); width = cairo_image_surface_get_width(glob.image); height = cairo_image_surface_get_height(glob.image); gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), width+2, height+2); gtk_window_set_title(GTK_WINDOW(window), "Clip image"); gtk_widget_show_all(window); g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) window); gtk_main(); cairo_surface_destroy(glob.image); } return 0;

In this example, we will clip an image. A circle is moving on the screen and showing a part of the underlying image. This is as if we looked through a hole.
if (pos_x < 0 + radius) { delta[0] = rand() % 4 + 5; } else if (pos_x > width - radius) { delta[0] = -(rand() % 4 + 5); }

If the circle hits the left or the right side of the window, the direction of the circle movement changes randomly. Same for the top and bottom sides.
cairo_set_source_surface(cr, glob.image, 1, 1); cairo_arc(cr, pos_x, pos_y, radius, 0, 2*M_PI);

Here we draw the image and a circle. Notice that we do not draw onto the window at the moment, but only in memory.
cairo_clip(cr);

The cairo_clip() sets a clipping region. The clipping region is the current path used. The current path was created by the cairo_arc() function call.
cairo_paint(cr);

The cairo_paint() paints the current source everywhere within the current clip region.
glob.image = cairo_image_surface_create_from_png("turnacastle.png");

An image surface is created from a PNG image using the


cairo_image_surface_create_from_png()

function.

Clipping and masking

Figure: Clipping image

M ask
Before the source is applied to the surface, it is filtered first. The mask is used as a filter. The mask determines, where the sourse is applied and where not. Opaque parts of the mask allow to copy the source. Transparent parts do not let to copy the source to the surface.
#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *); struct { cairo_surface_t *surface; } glob; static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0, 0, 0); cairo_mask_surface(cr, glob.surface, 0, 0); cairo_fill(cr); } static void create_surface() { glob.surface = cairo_image_surface_create_from_png("omen.png"); } static void destroy_surface() { cairo_surface_destroy(glob.surface); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); create_surface(); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy",

Clipping and masking


G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 305, 100); gtk_window_set_title(GTK_WINDOW(window), "Mask"); gtk_widget_show_all(window); gtk_main(); destroy_surface(); } return 0;

This small example clearly shows the basic idea behind the mask. The mask determines where to paint and where not to paint.
static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0, 0, 0); cairo_mask_surface(cr, glob.surface, 0, 0); cairo_fill(cr); }

In the do_drawing() function, we use an image as a mask. And it is therefore displayed on the window.

Figure: Applying a mask

Blind dow n effect


In this code example, we will blind down our image. This is similar to what we do with a rollerblind.
#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *); struct { cairo_surface_t *image; cairo_surface_t *surface; gboolean timer; gint img_width; gint img_height; } glob; static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); } return FALSE;

static gboolean time_handler(GtkWidget *widget) { if (!glob.timer) return FALSE; gtk_widget_queue_draw(widget); return TRUE;

Clipping and masking


static void do_drawing(cairo_t *cr) { cairo_t *ic; static gint h = 0; ic = cairo_create(glob.surface); cairo_rectangle(ic, 0, 0, glob.img_width, h); cairo_fill(ic); h += 1; if ( h == glob.img_height) glob.timer = FALSE; cairo_set_source_surface(cr, glob.image, 10, 10); cairo_mask_surface(cr, glob.surface, 10, 10); } cairo_destroy(ic);

static void init_vars() { glob.timer = TRUE; glob.image = cairo_image_surface_create_from_png("beckov.png"); glob.img_width = cairo_image_surface_get_width(glob.image); glob.img_height = cairo_image_surface_get_height(glob.image); glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, glob.img_width, glob.img_height); } static void cleanup() { cairo_surface_destroy(glob.image); cairo_surface_destroy(glob.surface); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); init_vars(); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 325, 250); gtk_window_set_title(GTK_WINDOW(window), "Blind down"); g_timeout_add(15, (GSourceFunc) time_handler, (gpointer) window); gtk_widget_show_all(window); gtk_main(); cleanup(); } return 0;

The idea behind the blind down effect is simple. The image is h pixels high. We draw 0, 1, 2 ... lines of 1px height. Each cycle the portion of the image is 1px higher, until the whole image is visible.
struct { cairo_surface_t *image; cairo_surface_t *surface; gboolean timer; gint img_width; gint img_height;

Clipping and masking


} glob;

In the glob structure, we will store two surfaces, a timer and the image width and height variables.
static void init_vars() { glob.timer = TRUE; glob.image = cairo_image_surface_create_from_png("beckov.png"); glob.img_width = cairo_image_surface_get_width(glob.image); glob.img_height = cairo_image_surface_get_height(glob.image); glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, glob.img_width, glob.img_height); }

In the init_vars() function, we initiate the previously declared variables. The last line creates an empty image surface. It is going to be filled with lines of pixels from the image surface, that we have created earlier.
ic = cairo_create(glob.surface);

We create a cairo context from the empty image source.


cairo_rectangle(ic, 0, 0, glob.img_width, h); cairo_fill(ic);

We draw a rectangle into the initially empty image. The rectangle will be 1px higher each cycle. The image created this way will serve as a mask later.
h += 1;

The height of the image to show is increased by one unit.


if ( h == glob.img_height) glob.timer = FALSE;

We stop the timer function when we draw the whole image on the GTK window.
cairo_set_source_surface(cr, glob.image, 10, 10); cairo_mask_surface(cr, glob.surface, 10, 10);

The image of a castle is set as a source for painting. The cairo_mask_surface() paints the current source using the alpha channel of surface as a mask.
static void cleanup() { cairo_surface_destroy(glob.image); cairo_surface_destroy(glob.surface); }

In the cleanup() function we destroy the created surfaces. This chapter was about clipping and masking in Cairo.

Home Contents Top of Page

ZetCode last modified December 1 3, 201 2

2007 - 201 3 Jan Bodnar

Transformations

Home Contents

Transformations
In this part of the Cairo graphics programming tutorial, we will talk about transformations. An a ffine tra nsform is composed of zero or more linear transformations (rotation, scaling or shear) and translation (shift). Several linear transformations can be combined into a single matrix. A rota tion is a transformation that moves a rigid body around a fixed point. A sc a ling is a transformation that enlarges or diminishes objects. The scale factor is the same in all directions. A tra nsla tion is a transformation that moves every point a constant distance in a specified direction. A sh ea r is a transformation that moves an object perpendicular to a given axis, with greater value on one side of the axis than the other. sources: (wikipedia.org, freedictionary.com)

Translation
The following example describes a simple translation.
static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0.2, 0.3, 0.8); cairo_rectangle(cr, 10, 10, 30, 30); cairo_fill(cr); cairo_translate(cr, 20, 20); cairo_set_source_rgb(cr, 0.8, 0.3, 0.2); cairo_rectangle(cr, 0, 0, 30, 30); cairo_fill(cr); cairo_translate(cr, 30, 30); cairo_set_source_rgb(cr, 0.8, 0.8, 0.2); cairo_rectangle(cr, 0, 0, 30, 30); cairo_fill(cr); cairo_translate(cr, 40, 40); cairo_set_source_rgb(cr, 0.3, 0.8, 0.8); cairo_rectangle(cr, 0, 0, 30, 30); cairo_fill(cr);

The examle draws a rectangle. Then we do a translation and draw the same rectangle again.
cairo_translate(cr, 20, 20);

The cairo_translate() function modifies the current transormation matrix by tranlating the user space origin. In our case we shift the origin by 20 units in both directions.

Figure: Translation

Shear
In the following example, we perform a shearing operation. A shearing is an object distortion along

Transformations

a particular axis. There is no shear function for this operation. We need to create our own transformation matrix. Note that each affine transformation can be performed by creating a transformation matrix.
static void do_drawing(cairo_t *cr) { cairo_matrix_t matrix; cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); cairo_rectangle(cr, 20, 30, 80, 50); cairo_fill(cr); cairo_matrix_init(&matrix, 1.0, 0.5, 0.0, 1.0, 0.0, 0.0); cairo_transform(cr, &matrix); cairo_rectangle(cr, 130, 30, 80, 50); cairo_fill(cr);

In this code example, we perform a simple shearing operation.


cairo_matrix_t matrix;

The cairo_matrix_t is a structure that holds an affine transformation.


cairo_matrix_init(&matrix, 1.0, 0.5, 0.0, 1.0, 0.0, 0.0);

This transformation shears y values by 0.5 of the x values.


cairo_transform(cr, &matrix);

We perform the transformation with the transform() method.

Figure: Shearing

Scaling
The next example demonstrates a scaling operation. Scaling is a transformation operation where the object is enlarged or shrinken.
static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0.2, 0.3, 0.8); cairo_rectangle(cr, 10, 10, 90, 90); cairo_fill(cr); cairo_scale(cr, 0.6, 0.6); cairo_set_source_rgb(cr, 0.8, 0.3, 0.2); cairo_rectangle(cr, 30, 30, 90, 90); cairo_fill(cr); cairo_scale(cr, 0.8, 0.8);

Transformations
cairo_set_source_rgb(cr, 0.8, 0.8, 0.2); cairo_rectangle(cr, 50, 50, 90, 90); cairo_fill(cr);

We draw three rectangles of 90x90px size. On two of them, we perform a scaling operation.
cairo_scale(cr, 0.6, 0.6); cairo_set_source_rgb(cr, 0.8, 0.3, 0.2); cairo_rectangle(cr, 30, 30, 90, 90); cairo_fill(cr);

We uniformly scale a rectangle by a factor of 0.6.


cairo_scale(cr, 0.8, 0.8); cairo_set_source_rgb(cr, 0.8, 0.8, 0.2); cairo_rectangle(cr, 50, 50, 90, 90); cairo_fill(cr);

Here we perform another scaling operation by a factor of 0.8. If we look at the picture, we see, that the third yellow rectangle is the smallest one. Even if we have used a smaller scaling factor. This is because transformation operations are additive. In fact, the third rectangle was scaled by a factor of 0.528 (0.6x0.8).

Figure: Scaling

I solating transformations
Transformation operations are additive. To isolate one operation from the other one, we can use the cairo_save() and cairo_restore() functions. The cairo_save() function makes a copy of the current state of the drawing context and saves it on an internal stack of saved states. The
cairo_restore()

function will re-establish the context to the saved state.

static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0.2, 0.3, 0.8); cairo_rectangle(cr, 10, 10, 90, 90); cairo_fill(cr); cairo_save(cr); cairo_scale(cr, 0.6, 0.6); cairo_set_source_rgb(cr, 0.8, 0.3, 0.2); cairo_rectangle(cr, 30, 30, 90, 90); cairo_fill(cr); cairo_restore(cr); cairo_save(cr); cairo_scale(cr, 0.8, 0.8); cairo_set_source_rgb(cr, 0.8, 0.8, 0.2); cairo_rectangle(cr, 50, 50, 90, 90); cairo_fill(cr); cairo_restore(cr);

In the example we scale two rectangles. This time we isolate the scaling operations from each other.
cairo_save(cr); cairo_scale(cr, 0.6, 0.6);

Transformations
cairo_set_source_rgb(cr, 0.8, 0.3, 0.2); cairo_rectangle(cr, 30, 30, 90, 90); cairo_fill(cr); cairo_restore(cr);

We isolate the scaling operation by putting the cairo_scale() function between the cairo_save() and cairo_restore() functions.

Figure: Isolating transformations Now the third yellow rectangle is bigger than the second red one.

Donut
In the following example we create an complex shape by rotating a bunch of ellipses.
#include <cairo.h> #include <gtk/gtk.h> #include <math.h> static void do_drawing(cairo_t *, GtkWidget *widget); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr, widget); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr, GtkWidget *widget) { GtkWidget *win = gtk_widget_get_toplevel(widget); gint width, height; gtk_window_get_size(GTK_WINDOW(win), &width, &height); cairo_set_line_width(cr, 0.5); cairo_translate(cr, width/2, height/2); cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI); cairo_stroke(cr); gint i; for (i = 0; i < 36; i++) { cairo_save(cr); cairo_rotate(cr, i*M_PI/36); cairo_scale(cr, 0.3, 1); cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI); cairo_restore(cr); cairo_stroke(cr); }

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv);

Transformations
window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 350, 250); gtk_window_set_title(GTK_WINDOW(window), "Donut"); gtk_widget_show_all(window); gtk_main(); } return 0;

We will do rotation and scaling operations. We will also save and restore Cairo contexts.
cairo_translate(cr, width/2, height/2); cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI); cairo_stroke(cr);

In the middle of the GTK+ window, we create a circle. This will be a bounding circle for our ellipses.
gint i; for (i = 0; i < 36; i++) { cairo_save(cr); cairo_rotate(cr, i*M_PI/36); cairo_scale(cr, 0.3, 1); cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI); cairo_restore(cr); cairo_stroke(cr); }

We create 36 ellipses along the path of our bounding circle. We insulate each rotate and scale operation from one another with the cairo_save() and cairo_restore() methods.

Star
The next example shows a rotating and scaling star.
#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *, GtkWidget *widget); int points[11][2] = { { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 }, { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 }, { 40, 190 }, { 50, 125 }, { 0, 85 } }; static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr, widget); cairo_destroy(cr); } return FALSE;

Transformations
static void do_drawing(cairo_t *cr, GtkWidget *widget) { static gdouble angle = 0; static gdouble scale = 1; static gdouble delta = 0.01; GtkWidget *win = gtk_widget_get_toplevel(widget); gint width, height; gtk_window_get_size(GTK_WINDOW(win), &width, &height); cairo_set_source_rgb(cr, 0, 0.44, 0.7); cairo_set_line_width(cr, 1); cairo_translate(cr, width/2, height/2 ); cairo_rotate(cr, angle); cairo_scale(cr, scale, scale); gint i; for ( i = 0; i < 10; i++ ) { cairo_line_to(cr, points[i][0], points[i][1]); } cairo_close_path(cr); cairo_fill(cr); cairo_stroke(cr); if ( scale < 0.01 ) { delta = -delta; } else if (scale > 0.99) { delta = -delta; } scale += delta; angle += 0.01;

static gboolean time_handler(GtkWidget *widget) { gtk_widget_queue_draw(widget); } return TRUE;

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 400, 300); gtk_window_set_title(GTK_WINDOW(window), "Star"); g_timeout_add(10, (GSourceFunc) time_handler, (gpointer) window); gtk_widget_show_all(window); gtk_main(); } return 0;

In this example, we create a star object. We will translate it, rotate it and scale it.
int points[11][2] = {

Transformations
{ 0, 85 }, { 75, 75 }, { 100, 10 },

...

The star object will be constructed from these points.


static gdouble angle = 0; static gdouble scale = 1; static gdouble delta = 0.01;

We initialize three important variables. The angle is used in the rotation, the scale in scaling the star object. The delta variable controls when the star is growing and when it is shrinking.
cairo_translate(cr, width/2, height/2); cairo_rotate(cr, angle); cairo_scale(cr, scale, scale);

We shift the star into the middle of the window. Rotate it and scale it.
gint i; for ( i = 0; i < 10; i++ ) { cairo_line_to(cr, points[i][0], points[i][1]); } cairo_close_path(cr); cairo_fill(cr); cairo_stroke(cr);

Here we draw the star object.


if ( scale < 0.01 ) { delta = -delta; } else if (scale > 0.99) { delta = -delta; }

These lines control the growing or shrinking of the star object. In this part of the Cairo graphics tutorial, we talked about transformations.

Home Contents Top of Page

ZetCode last modified December 1 3, 201 2

2007 - 201 3 Jan Bodnar

Text in Cairo

Home Contents

Text in Cairo
In this part of the Cairo graphics tutorial, we will work with text.

Soulmate
In the first example, we will display some lyrics on the GTK+ window.
static void do_drawing(cairo_t *cr) { cairo_set_source_rgb(cr, 0.1, 0.1, 0.1); cairo_select_font_face(cr, "Purisa", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, 13); cairo_move_to(cr, 20, 30); cairo_show_text(cr, "Most relationships seem so transitory"); cairo_move_to(cr, 20, 60); cairo_show_text(cr, "They're all good but not the permanent one"); cairo_move_to(cr, 20, 120); cairo_show_text(cr, "Who doesn't long for someone to hold"); cairo_move_to(cr, 20, 150); cairo_show_text(cr, "Who knows how to love you without being told"); cairo_move_to(cr, 20, 180); cairo_show_text(cr, "Somebody tell me why I'm on my own"); cairo_move_to(cr, 20, 210); cairo_show_text(cr, "If there's a soulmate for everyone");

In this example, we display part of the lyrics from the Natasha Bedingfield's Soulmate song.
cairo_select_font_face(cr, "Purisa", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);

Here we select the font face. The function takes three parameters, the font family, font slant and the font weight.
cairo_set_font_size(cr, 13);

Here we specify the font size.


cairo_move_to(cr, 20, 30); cairo_show_text(cr, "Most relationships seem so transitory");

We display the text on the window by specifying the position of the text and calling the
cairo_show_text()

function.

Text in Cairo

Figure: Soulmate

Centered text
Next we will show, how to center text on the window.
static void do_drawing(cairo_t *cr, GtkWidget *widget) { cairo_text_extents_t extents; GtkWidget *win = gtk_widget_get_toplevel(widget); gint w, h; gtk_window_get_size(GTK_WINDOW(win), &w, &h); cairo_select_font_face(cr, "Courier", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, 60); cairo_text_extents(cr, "ZetCode", &extents); cairo_move_to(cr, w/2 - extents.width/2, h/2); cairo_show_text(cr, "ZetCode");

The code will center a text on the window. It remains centered, even if we resize the window.
GtkWidget *win = gtk_widget_get_toplevel(widget); gint w, h; gtk_window_get_size(GTK_WINDOW(win), &w, &h);

To center a text on the window, it is necessary to get the size of of the parent window.
cairo_select_font_face(cr, "Courier", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, 60);

We select a font and its size to be displayed.


cairo_text_extents(cr, "ZetCode", &extents);

We get the text extents. These are some numbers that describe the text. We need the width of the text for our example.
cairo_move_to(cr, w/2 - extents.width/2, h/2); cairo_show_text(cr, "ZetCode");

We position the text into the middle of the window and show it using the cairo_show_text() method.

Text in Cairo

Figure: Centered text

Shaded text
Now we will show a shaded text on the window.
static void do_drawing(cairo_t *cr, GtkWidget *widget) { cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, 50); cairo_set_source_rgb(cr, 0, 0, 0); cairo_move_to(cr, 40, 60); cairo_show_text(cr, "ZetCode"); cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); cairo_move_to(cr, 43, 63); cairo_show_text(cr, "ZetCode");

To create a shade, we draw the text twice. In different colours. The second text is moved a bit to the right and bottom.
cairo_set_source_rgb(cr, 0, 0, 0); cairo_move_to(cr, 40, 60); cairo_show_text(cr, "ZetCode");

The first text is drawn in black ink. It serves as a shade.


cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); cairo_move_to(cr, 43, 63); cairo_show_text(cr, "ZetCode");

The second text is drawn in some gray ink. It is moved by 3px to the right and to the bottom.

Figure: Shaded text

Text filled w ith gradient


The following example will create a nice effect. We will fill a text with some linear gradient.

Text in Cairo
static void do_drawing(cairo_t *cr, GtkWidget *widget) { cairo_pattern_t *pat; cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); cairo_paint(cr); gint h = 90; cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, h); pat = cairo_pattern_create_linear(0, 15, 0, h*0.8); cairo_pattern_set_extend(pat, CAIRO_EXTEND_REPEAT); cairo_pattern_add_color_stop_rgb(pat, 0.0, 1, 0.6, 0); cairo_pattern_add_color_stop_rgb(pat, 0.5, 1, 0.3, 0); cairo_move_to(cr, 15, 80); cairo_text_path(cr, "ZetCode"); cairo_set_source(cr, pat); cairo_fill(cr);

We draw a text on the window filled with a linear gradient. The colours are some orange colours.
cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); cairo_paint(cr);

To make it more visually appealing, we paint the background in dark gray colour.
pat = cairo_pattern_create_linear(0, 15, 0, h*0.8); cairo_pattern_set_extend(pat, CAIRO_EXTEND_REPEAT); cairo_pattern_add_color_stop_rgb(pat, 0.0, 1, 0.6, 0); cairo_pattern_add_color_stop_rgb(pat, 0.5, 1, 0.3, 0);

The linear gradient is created.


cairo_move_to(cr, 15, 80); cairo_text_path(cr, "ZetCode"); cairo_set_source(cr, pat); cairo_fill(cr);

The text is displayed on the window. We use the gradient as a source for painting.

Figure: Text filled with gradient

Glyphs
The cairo_show_text() method is only suitable for simple text rendering. Cairo developers call it a toy method. More professional text rendering is done with glyphs. A glyp h is a graphic symbol which provides a form for a character. A character provides a meaning. It can have multiple glyphs. A character has no intrinsic appearance. A glyph has no intrinsic meaning. Note that many common programming requirements conserning text are addressed by the Pango library.
static void do_drawing(cairo_t *cr, GtkWidget *widget) { cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);

Text in Cairo
cairo_set_font_size(cr, 13); const int n_glyphs = 20 * 35; cairo_glyph_t glyphs[n_glyphs]; gint i = 0; gint x, y; for (y=0; y<20; y++) { for (x=0; x<35; x++) { glyphs[i] = (cairo_glyph_t) {i, x*15 + 20, y*18 + 20}; i++; } } } cairo_show_glyphs(cr, glyphs, n_glyphs);

This code shows 700 glyphs of a chosen font.


const int n_glyphs = 20 * 35; cairo_glyph_t glyphs[n_glyphs];

The glyphs array will store three integer values. The first value is the index of the glyph to the chosen font type. The second and the third values are x, y positions of a glyph.
cairo_show_glyphs(cr, glyphs, n_glyphs);

The cairo_show_glyphs() method shows the glyphs on the window. This chapter covered text in Cairo.

Home Contents Top of Page

ZetCode last modified December 1 0, 201 2

2007 - 201 3 Jan Bodnar

Images in Cairo

Home Contents

I mages in Cairo
In this part of the Cairo graphics tutorial, we will talk about the images. We will show how to display an image on the GTK window. We will also create some effects with images.

Displaying an image
In the first example, we will display an image.
#include <cairo.h> #include <gtk/gtk.h> struct { cairo_surface_t *image; } glob; static void do_drawing(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr) { cairo_set_source_surface(cr, glob.image, 10, 10); cairo_paint(cr); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; glob.image = cairo_image_surface_create_from_png("stmichaelschurch.png"); gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK (gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 300, 220); gtk_window_set_title(GTK_WINDOW(window), "Image"); gtk_widget_show_all(window); gtk_main(); cairo_surface_destroy(glob.image); } return 0;

The example displays an image.

Images in Cairo
glob.image = cairo_image_surface_create_from_png("stmichaelschurch.png");

We create an image surface from a png image. For efficiency reasons, the function is called in the main function.
cairo_set_source_surface(cr, glob.image, 10, 10);

We create a source for painting from the created image surface.


cairo_paint(cr);

We paint the source on the window.


cairo_surface_destroy(glob.image);

In the end, the surface is destroyed.

W atermark
It is common to draw information on images. The text written on an image is called a watermark. Watermarks are used to identify images. They could be copyright notices or image creation times.
#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *, GtkWidget *widget); struct { cairo_surface_t *image; } glob; static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr, widget); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr, GtkWidget *widget) { cairo_set_source_surface(cr, glob.image, 10, 10); cairo_paint(cr); } static void load_image() { glob.image = cairo_image_surface_create_from_png("beckov.png"); } static void draw_mark() { cairo_t *ic; ic = cairo_create(glob.image); cairo_set_font_size(ic, 11); cairo_set_source_rgb(ic, 0.9 , 0.9 , 0.9); cairo_move_to(ic, 20, 30); cairo_show_text(ic, " Beckov 2012 , (c) Jan Bodnar "); cairo_stroke(ic);

int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; load_image(); draw_mark(); gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

Images in Cairo
darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 350, 250); gtk_window_set_title(GTK_WINDOW(window), "Watermark"); gtk_widget_show_all(window); gtk_main(); cairo_surface_destroy(glob.image); } return 0;

We draw copyright information on an image.


static void load_image() { glob.image = cairo_image_surface_create_from_png("beckov.png"); }

In the load_image() method, we create an image surface from a PNG image.


static void draw_mark() { cairo_t *ic; ic = cairo_create(glob.image); ...

In the draw_mark() funciton, we draw the copyright message on the image. First we create a drawing context from the image surface.
cairo_set_font_size(ic, 11); cairo_set_source_rgb(ic, 0.9 , 0.9 , 0.9); cairo_move_to(ic, 20, 30); cairo_show_text(ic, " Beckov 2012 , (c) Jan Bodnar "); cairo_stroke(ic);

Then we draw a small text in white colour.


static void do_drawing(cairo_t *cr, GtkWidget *widget) { cairo_set_source_surface(cr, glob.image, 10, 10); cairo_paint(cr); }

The image surface is drawn on the window.

The spectrum effect


We call this a spectrum effect, because it resembles and old ZX Spectrum computer. When you were loading an image into this computer, it was gradually appearing on the screen. The next example is loosly based on this experience.
#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *); struct { gboolean timer; cairo_surface_t *image; cairo_surface_t *surface;

Images in Cairo
gint img_width; gint img_height; } glob; static void init_vars() { glob.image = cairo_image_surface_create_from_png("beckov.png"); glob.img_width = cairo_image_surface_get_width(glob.image); glob.img_height = cairo_image_surface_get_height(glob.image); glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, glob.img_width, glob.img_height); glob.timer = TRUE;

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr) { cairo_t *ic; static gint count = 0; ic = cairo_create(glob.surface); gint i, j; for (i = 0; i <= glob.img_height; i+=7) { for (j = 0 ; j < count; j++) { cairo_move_to(ic, 0, i+j); cairo_line_to(ic, glob.img_width, i+j); } } count++; if (count == 8) glob.timer = FALSE; cairo_set_source_surface(cr, glob.image, 10, 10); cairo_mask_surface(cr, glob.surface, 10, 10); cairo_stroke(ic); } cairo_destroy(ic);

static gboolean time_handler(GtkWidget *widget) { if (!glob.timer) return FALSE; gtk_widget_queue_draw(widget); return TRUE;

int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; init_vars(); gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);

Images in Cairo
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 325, 250); gtk_window_set_title(GTK_WINDOW(window), "Spectrum"); g_timeout_add(400, (GSourceFunc) time_handler, (gpointer) window); gtk_widget_show_all(window); gtk_main(); cairo_surface_destroy(glob.image); cairo_surface_destroy(glob.surface); } return 0;

We divide the image into n parts consisting of 8 lines. Each cycle each part of the image will get bigger by one pixel. The created image will serve as a mask for displaying the image of the castle.
struct { gboolean timer; cairo_surface_t *image; cairo_surface_t *surface; gint img_width; gint img_height; } glob;

The glob structure stores variables that are used within more functions.
static void init_vars() { glob.image = cairo_image_surface_create_from_png("beckov.png"); glob.img_width = cairo_image_surface_get_width(glob.image); glob.img_height = cairo_image_surface_get_height(glob.image); glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, glob.img_width, glob.img_height); glob.timer = TRUE;

In the init_vars() function, we initiate the aforementioned variables.


gint i, j; for (i = 0; i <= glob.img_height; i+=7) { for (j = 0 ; j < count; j++) { cairo_move_to(ic, 0, i+j); cairo_line_to(ic, glob.img_width, i+j); } }

We gradully draw lines into each of the n parts.


count++; if (count == 8) glob.timer = FALSE;

After 8 steps, the animation finishes.


cairo_set_source_surface(cr, glob.image, 10, 10); cairo_mask_surface(cr, glob.surface, 10, 10); cairo_stroke(ic);

Using the mask operation, we draw the portions of the image on the window. This chapter covered images in Cairo.

Home Contents Top of Page

ZetCode last modified December 1 1 , 201 2

2007 - 201 3 Jan Bodnar

The root window

Home Contents

Root w indow
In this part of the Cairo graphics tutorial, we will work with the root window. The root window is the desktop window where we usually have icon shortcuts. It is possible to manipulate with the root window. From the programmer's perspective, it is just a special kind of a window.

Transparent w indow
Our first example will create a transparent window. We will see, what it beneath of the window object.
#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *); static void tran_setup(GtkWidget *win) { GdkScreen *screen; GdkVisual *visual; gtk_widget_set_app_paintable(win, TRUE); screen = gdk_screen_get_default(); visual = gdk_screen_get_rgba_visual(screen); if (visual != NULL && gdk_screen_is_composited(screen)) { gtk_widget_set_visual(win, visual); }

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr) { cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_paint(cr); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); tran_setup(window); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 300, 250);

The root window


gtk_window_set_title(GTK_WINDOW(window), "Transparent window"); gtk_widget_show_all(window); gtk_main(); } return 0;

To create a transparent window, we get the visual of the screen object and set it for our window. In the on_draw() method, we draw over the screen's visual object. This createas an illusion of partial transparency.
gtk_widget_set_app_paintable(win, TRUE);

We must set the application to be painted on.


screen = gdk_screen_get_default();

The gdk_screen_get_default() method returns the screen object.


visual = gdk_screen_get_rgba_visual(screen);

From the screen window, we get its visual. The visual contains the low level display information.
if (visual != NULL && gdk_screen_is_composited(screen)) { gtk_widget_set_visual(win, visual); }

Not all displays support this operation. Therefore, we check if our screen supports composition and the returned visual is not None. We set the screen's visual to be the visual of our window.
static void do_drawing(cairo_t *cr) { cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_paint(cr); }

We use a partially transparent source to draw over the screen window. The
CAIRO_OPERATOR_SOURCE CAIRO_OPERATOR_CLEAR

creates a composition operation where we draw over the source. Which is

the screen window. To get full transparency, we set the alpha value to 0 or use the operator.

Figure: Transparent window

Taking a screenshot
The root window is also essential in taking a screenshot.
#include <cairo.h> #include <gdk/gdk.h> int main (int argc, char *argv[])

The root window


{ gdk_init(&argc, &argv); GdkWindow *root_win = gdk_get_default_root_window(); gint width = gdk_window_get_width(root_win); gint height = gdk_window_get_height(root_win); cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height); cairo_t *cr = cairo_create(surface); gdk_cairo_set_source_pixbuf(cr, pb, 0, 0); cairo_paint(cr); cairo_surface_write_to_png(surface, "image.png"); cairo_destroy(cr); cairo_surface_destroy(surface); } return 0;

The example captures a snapshot of the entire screen. In this example, we do not use the full GTK windowing system. We use Cairo and GDK libraries to do the job.
gdk_init(&argc, &argv);

The gdk_init() initializes the GDK library and connects to the windowing system.
GdkWindow *root_win = gdk_get_default_root_window();

We get the root window with the gdk_get_default_root_window() function call.


gint width = gdk_window_get_width(root_win); gint height = gdk_window_get_height(root_win);

We determine the width and the height of the root window.


cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);

An empty image surface is created. It has the size of the root window.
GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height);

We get a pixbuf from the root window using the gdk_pixbuf_get_from_window() function call. A pixbuf is an object that describes an image in memory.
cairo_t *cr = cairo_create(surface); gdk_cairo_set_source_pixbuf(cr, pb, 0, 0); cairo_paint(cr);

In the above code lines, we create a Cairo drawing context on the image surface that we have created earlier. We place the pixbuf on the drawing context and paint it on the surface.
cairo_surface_write_to_png(surface, "image.png");

The image surface is written to a PNG image using the write_to_png() method.
cairo_destroy(cr); cairo_surface_destroy(surface);

We clean up resources.

Show ing a message


In the third example, we will show a message on the desktop window.

The root window


#include <cairo.h> #include <gtk/gtk.h> #include <pango/pango.h> static void do_drawing(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { cr = gdk_cairo_create(gtk_widget_get_window(widget)); do_drawing(cr); cairo_destroy(cr); } return FALSE;

static void do_drawing(cairo_t *cr) { cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); } static void setup(GtkWidget *win) { gtk_widget_set_app_paintable(win, TRUE); gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK); gtk_window_set_keep_below(GTK_WINDOW(win), TRUE); GdkScreen *screen = gdk_screen_get_default(); GdkVisual *visual = gdk_screen_get_rgba_visual(screen); if (visual != NULL && gdk_screen_is_composited(screen)) { gtk_widget_set_visual(win, visual); }

int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *lbl; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); setup(window); lbl = gtk_label_new("ZetCode, tutorials for programmers"); PangoFontDescription *fd = pango_font_description_from_string("Serif 20"); gtk_widget_modify_font(lbl, fd); gtk_container_add(GTK_CONTAINER(window), lbl); GdkColor color; gdk_color_parse("white", &color); gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(window), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 350, 250); gtk_widget_show_all(window); gtk_main(); } return 0;

The code displays a message label on the root window.


static void do_drawing(cairo_t *cr) { cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr);

The root window


} cairo_set_operator(cr, CAIRO_OPERATOR_OVER);

We use the CAIRO_OPERATOR_CLEAR operator to clear the background of the window. Then we set the CAIRO_OPERATOR_OVER to let the label widget be drawn.
gtk_widget_set_app_paintable(win, TRUE);

We will be manipulating the application window, so we make it paintable.


gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK);

Implementing this window hint removes window borders and decoration.


gtk_window_set_keep_below(GTK_WINDOW(win), TRUE);

We keep the application always at the bottom, just over the root window.
GdkScreen *screen = gdk_screen_get_default(); GdkVisual *visual = gdk_screen_get_rgba_visual(screen); if (visual != NULL && gdk_screen_is_composited(screen)) { gtk_widget_set_visual(win, visual); }

We set the visual of the screen to be the visual of our application.


lbl = gtk_label_new("ZetCode, tutorials for programmers");

We create a message label.


PangoFontDescription *fd = pango_font_description_from_string("Serif 20"); gtk_widget_modify_font(lbl, fd);

With the help of the Pango module, we select a specific font for the text.
gtk_container_add(GTK_CONTAINER(window), lbl);

The label is put onto the window.


GdkColor color; gdk_color_parse("white", &color); gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color);

We modify the text to be in white colour.

Figure: Message on the root window In this chapter we have worked with the desktop window in Cairo.

Home Contents Top of Page

ZetCode last modified December 1 2, 201 2

2007 - 201 3 Jan Bodnar

Das könnte Ihnen auch gefallen