cairo - graphics framework

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. Vector and raster 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 Quartz 2D on Mac OS. Cairo is an open source software library. From version 2.8, the cairo library is part of the GTK+ 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, 2012 © 2007 - 2013 Jan Bodnar

Upload: manas-george

Post on 29-Nov-2015

104 views

Category:

Documents


8 download

DESCRIPTION

Tutorial for using the Cairo graphics framework using C++

TRANSCRIPT

Page 1: Cairo - Graphics Framework

2/19/13 The Cairo graphics library

zetcode.com/gfx/cairo/cairolib/ 1/1

Home Contents

The Cairo graphics libraryWelcome to the Cairo graphics tutorial. This tutorial will teach you basics and some advancedtopics 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. Vector and ra ster graphics. Raster graphicsrepresents images as a collection of pixels. Vector graphics is the use of geometrical primitivessuch as points, lines, curves or polygons to represent images. These primitives are created usingmathematical equations.

Both types of computer graphics have advantages and disadvantages. The advantages of vectorgraphics over raster are:

smaller sizeability to zoom indefinitelymoving, 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 multiplatformlibrary, works on Linux, BSDs, OSX.

Cairo supports various backends.

X Window SystemWin32 GDIMac OS X QuartzPNGPDFPostScriptSVG

This means, we can use the library to draw on windows on Linux/BSDs, Windows, OSX and wecan 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 Qua rtz 2D onMac OS. Cairo is an open source software library. From version 2.8, the cairo library is part of theGT K+ system.

Compiling examples

The examples are created in the C programming language. We use GNU C compiler to compilethem.

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, 2012 © 2007 - 2013 Jan Bodnar

Page 2: Cairo - Graphics Framework

2/19/13 Cairo Definitions

zetcode.com/gfx/cairo/cairodefinitions/ 1/2

Home Contents

Cairo definitionsIn this part of the Cairo graphics tutorial, we will provide some useful definitions for the Cairographics 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 ofthe graphics state parameters that describe how drawing is to be done. This includes informationsuch as line width, colour, the surface to draw to, and many other things. This allows the actualdrawing 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 thesupported 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 GdkW indow. Acairo_t structure contains the current state of the rendering device, including coordinates of yet tobe 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 specificsurface. 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 todraw on GDK windows.

Path

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 closedpaths. In a closed path, starting and ending points meet. In an open path, starting and endingpoint do not meet.

In Cairo, we start with an empty path. First we define a path and then we make them visible bystroking 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 willuse 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 orPostScript 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,

Page 3: Cairo - Graphics Framework

2/19/13 Cairo Definitions

zetcode.com/gfx/cairo/cairodefinitions/ 2/2

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;

Mask

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

Pattern

A cairo pattern represents a source when drawing onto a surface. In cairo, a pattern is somethingthat you can read from, that is used as the source or mask of a drawing operation. Patterns incairo 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 12, 2012 © 2007 - 2013 Jan Bodnar

Page 4: Cairo - Graphics Framework

2/19/13 Cairo backends

zetcode.com/gfx/cairo/cairobackends/ 1/5

Home Contents

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

PNG 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.

Page 5: Cairo - Graphics Framework

2/19/13 Cairo backends

zetcode.com/gfx/cairo/cairobackends/ 2/5

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.

PDF 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 thehottest technologies these days.

#include <cairo.h>#include <cairo-svg.h>

Page 6: Cairo - Graphics Framework

2/19/13 Cairo backends

zetcode.com/gfx/cairo/cairobackends/ 3/5

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 Window

In the last example, we will draw on the GTK window. This backend will be used throughout therest 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.");

Page 7: Cairo - Graphics Framework

2/19/13 Cairo backends

zetcode.com/gfx/cairo/cairobackends/ 4/5

}

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 weperform the drawing operations. Actual drawing is delegated to the do_drawing() function. Afterthe 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 customdrawing.

g_signal_connect(G_OBJECT(darea), "draw",

G_CALLBACK(on_draw_event), NULL);

Page 8: Cairo - Graphics Framework

2/19/13 Cairo backends

zetcode.com/gfx/cairo/cairobackends/ 5/5

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 12, 2012 © 2007 - 2013 Jan Bodnar

Page 9: Cairo - Graphics Framework

2/19/13 Basic drawing in Cairo

zetcode.com/gfx/cairo/basicdrawing/ 1/8

Home Contents

Basic draw ing in CairoIn this part of the Cairo graphics tutorial, we will draw some basic primitives. We will draw simplelines, 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 isspecified 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;

Page 10: Cairo - Graphics Framework

2/19/13 Basic drawing in Cairo

zetcode.com/gfx/cairo/basicdrawing/ 2/8

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 anarray. 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 thewindow, 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 leftmouse 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.

Page 11: Cairo - Graphics Framework

2/19/13 Basic drawing in Cairo

zetcode.com/gfx/cairo/basicdrawing/ 3/8

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);

Page 12: Cairo - Graphics Framework

2/19/13 Basic drawing in Cairo

zetcode.com/gfx/cairo/basicdrawing/ 4/8

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 thecircle. 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

Pen dashes

Each line can be drawn with a different pen dash. It defines the style of the line. The dash is usedby 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 theoffset value. If the length is 0, the dashing is disabled. If it is 1, a symmetric pattern is asumedwith alternating on and off portions of the size specified by the single value in dashes.

Page 13: Cairo - Graphics Framework

2/19/13 Basic drawing in Cairo

zetcode.com/gfx/cairo/basicdrawing/ 5/8

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 4points not drawn, 21 points drawn and 2 not drawn. This pattern takes turns until the end of theline.

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 offpoints.

Figure: Dashes

Line caps

The line caps are endpoints of lines.

Page 14: Cairo - Graphics Framework

2/19/13 Basic drawing in Cairo

zetcode.com/gfx/cairo/basicdrawing/ 6/8

CAIRO_LINE_CAP_SQUARECAIRO_LINE_CAP_ROUNDCAIRO_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 thedifferences 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);

Page 15: Cairo - Graphics Framework

2/19/13 Basic drawing in Cairo

zetcode.com/gfx/cairo/basicdrawing/ 7/8

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_MITERCAIRO_LINE_JOIN_BEVELCAIRO_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.

Page 16: Cairo - Graphics Framework

2/19/13 Basic drawing in Cairo

zetcode.com/gfx/cairo/basicdrawing/ 8/8

Figure: Line joins

In this chapter, we did some basic drawing.

Home ‡ Contents ‡ Top of Page

ZetCode last modified December 12, 2012 © 2007 - 2013 Jan Bodnar

Page 17: Cairo - Graphics Framework

2/19/13 Shapes and fills in Cairo

zetcode.com/gfx/cairo/shapesfills/ 1/6

Home Contents

Shapes and fills in CairoIn this part of the Cairo tutorial, we will create some basic and more advanced shapes. We will fillthem 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.

Page 18: Cairo - Graphics Framework

2/19/13 Shapes and fills in Cairo

zetcode.com/gfx/cairo/shapesfills/ 2/6

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);

Page 19: Cairo - Graphics Framework

2/19/13 Shapes and fills in Cairo

zetcode.com/gfx/cairo/shapesfills/ 3/6

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 arecreated 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 bycalling 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 colours

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);

Page 20: Cairo - Graphics Framework

2/19/13 Shapes and fills in Cairo

zetcode.com/gfx/cairo/shapesfills/ 4/6

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 bycalling the cairo_fill() function.

Figure: Solid colours

Patterns

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 *surface1;cairo_surface_t *surface2;cairo_surface_t *surface3;cairo_surface_t *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;

Page 21: Cairo - Graphics Framework

2/19/13 Shapes and fills in Cairo

zetcode.com/gfx/cairo/shapesfills/ 5/6

cairo_pattern_t *pattern3; cairo_pattern_t *pattern4;

pattern1 = cairo_pattern_create_for_surface(surface1); pattern2 = cairo_pattern_create_for_surface(surface2); pattern3 = cairo_pattern_create_for_surface(surface3); pattern4 = 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 usefour pattern images from the Gimp image manipulation program. We must retain the originalsize 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.

Page 22: Cairo - Graphics Framework

2/19/13 Shapes and fills in Cairo

zetcode.com/gfx/cairo/shapesfills/ 6/6

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 toCAIRO_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 13, 2012 © 2007 - 2013 Jan Bodnar

Page 23: Cairo - Graphics Framework

2/19/13 Gradients

zetcode.com/gfx/cairo/gradients/ 1/5

Home Contents

GradientsIn this part of the Cairo graphics tutorial, we will cover gradients. We will mention linear andradial gradients.

In computer graphics, gradient is a smooth blending of shades from light to dark or from onecolour to another. In 2D drawing programs and paint programs, gradients are used to createcolourful 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 withthe 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);

Page 24: Cairo - Graphics Framework

2/19/13 Gradients

zetcode.com/gfx/cairo/gradients/ 2/5

} 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 drawthe 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 ofblack and yellow colours. By adding two black and one yellow stops, we create a horizontalgradient pattern. What these stops actually mean? In our case, we begin with black colour, whichwill stop at 1/10 of the size. Then we begin to gradually paint in yellow, which will culminate at thecentre of the shape. The yellow colour stops at 9/10 of the size, where we begin painting in blackagain, until the end.

Page 25: Cairo - Graphics Framework

2/19/13 Gradients

zetcode.com/gfx/cairo/gradients/ 3/5

Figure: Linear gradients

Radial gradients

Radial gradients are blendings of colours or shades of colours between two circles. Thecairo_pattern_create_radial() function s is used to create radial gradients in Cairo.

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

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);

Page 26: Cairo - Graphics Framework

2/19/13 Gradients

zetcode.com/gfx/cairo/gradients/ 4/5

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 twocircles. 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 gradientexample, 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 acommon center point.

Figure: Radial gradients

Page 27: Cairo - Graphics Framework

2/19/13 Gradients

zetcode.com/gfx/cairo/gradients/ 5/5

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

Home ‡ Contents ‡ Top of Page

ZetCode last modified December 9, 2012 © 2007 - 2013 Jan Bodnar

Page 28: Cairo - Graphics Framework

2/19/13 Transparency

zetcode.com/gfx/cairo/transparency/ 1/6

Home Contents

TransparencyIn this part of the Cairo C API tutorial, we will talk about transparency. We will provide somebasic definitions and two interesting transparency effects.

Transparency is the quality of being able to see through a material. The easiest way to understandtransparency is to imagine a piece of glass or water. Technically, the rays of light can go throughthe glass and this way we can see objects behind the glass.

In computer graphics, we can achieve transparency effects using a lpha compositing. Alphacompositing is the process of combining an image with a background to create the appearance ofpartial transparency. The composition process uses an a lpha cha nnel. Alpha channel is an 8-bitlayer in a graphics file format that is used for expressing translucency (transparency). The extraeight 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

Puff effect

In the following example, we create a puff effect. The example will display a growing centered textthat will gradually fade out from some point. This is a very common effect which we can often seein 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));

Page 29: Cairo - Graphics Framework

2/19/13 Transparency

zetcode.com/gfx/cairo/transparency/ 2/6

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);

Page 30: Cairo - Graphics Framework

2/19/13 Transparency

zetcode.com/gfx/cairo/transparency/ 3/6

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 theparent 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 startsdecreasing. 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 willbe 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;

Page 31: Cairo - Graphics Framework

2/19/13 Transparency

zetcode.com/gfx/cairo/transparency/ 4/6

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

Waiting demo

In this examle, we use transparency effect to create a waiting demo. We will draw 8 lines that willgradually fade out creating an illusion, that a line is moving. Such effects are often used to informusers, that a lengthy task is going on behind the scenes. An example is streaming video over theinternet.

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

Page 32: Cairo - Graphics Framework

2/19/13 Transparency

zetcode.com/gfx/cairo/transparency/ 5/6

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, eachfor one state. Each of the 8 lines will continuosly use these values.

cairo_set_line_width(cr, 3);

Page 33: Cairo - Graphics Framework

2/19/13 Transparency

zetcode.com/gfx/cairo/transparency/ 6/6

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 roudedcaps.

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 13, 2012 © 2007 - 2013 Jan Bodnar

Page 34: Cairo - Graphics Framework

2/19/13 Compositing

zetcode.com/gfx/cairo/compositing/ 1/3

Home Contents

CompositingIn this part of the Cairo graphics programming tutorial, we will define compositing operations.

Compositing is the combining of visual elements from separate sources into single images. Theyare used to create the illusion that all those elements are parts of the same scene. Compositing isvidely used in film industry to create crowds, entire new worlds which would be expensive orimpossible to create otherwise. (wikipedia.org)

Operations

There are several compositing operations. The Cairo graphics library has 14 different compositingoperations.

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

Page 35: Cairo - Graphics Framework

2/19/13 Compositing

zetcode.com/gfx/cairo/compositing/ 2/3

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,

Page 36: Cairo - Graphics Framework

2/19/13 Compositing

zetcode.com/gfx/cairo/compositing/ 3/3

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 13, 2012 © 2007 - 2013 Jan Bodnar

Page 37: Cairo - Graphics Framework

2/19/13 Clipping and masking

zetcode.com/gfx/cairo/clippingmasking/ 1/6

Home Contents

Clipping and maskingIn 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 createinteresting 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[]){

Page 38: Cairo - Graphics Framework

2/19/13 Clipping and masking

zetcode.com/gfx/cairo/clippingmasking/ 2/6

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 theunderlying 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 movementchanges 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 thecairo_image_surface_create_from_png() function.

Page 39: Cairo - Graphics Framework

2/19/13 Clipping and masking

zetcode.com/gfx/cairo/clippingmasking/ 3/6

Figure: Clipping image

Mask

Before the source is applied to the surface, it is filtered first. The mask is used as a filter. Themask determines, where the sourse is applied and where not. Opaque parts of the mask allow tocopy 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",

Page 40: Cairo - Graphics Framework

2/19/13 Clipping and masking

zetcode.com/gfx/cairo/clippingmasking/ 4/6

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 topaint 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 thewindow.

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 roller-blind.

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

Page 41: Cairo - Graphics Framework

2/19/13 Clipping and masking

zetcode.com/gfx/cairo/clippingmasking/ 5/6

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 ... linesof 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;

Page 42: Cairo - Graphics Framework

2/19/13 Clipping and masking

zetcode.com/gfx/cairo/clippingmasking/ 6/6

} 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 anempty image surface. It is going to be filled with lines of pixels from the image surface, that wehave 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 13, 2012 © 2007 - 2013 Jan Bodnar

Page 43: Cairo - Graphics Framework

2/19/13 Transformations

zetcode.com/gfx/cairo/transformations/ 1/7

Home Contents

TransformationsIn 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 orshear) 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 sca ling is atransformation that enlarges or diminishes objects. The scale factor is the same in all directions. Atra nsla tion is a transformation that moves every point a constant distance in a specifieddirection. A shea r is a transformation that moves an object perpendicular to a given axis, withgreater 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

Page 44: Cairo - Graphics Framework

2/19/13 Transformations

zetcode.com/gfx/cairo/transformations/ 2/7

a particular axis. There is no shear function for this operation. We need to create our owntransformation matrix. Note that each affine transformation can be performed by creating atransformation 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 wherethe 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);

Page 45: Cairo - Graphics Framework

2/19/13 Transformations

zetcode.com/gfx/cairo/transformations/ 3/7

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 afactor of 0.528 (0.6x0.8).

Figure: Scaling

Isolating transformations

Transformation operations are additive. To isolate one operation from the other one, we can usethe 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. Thecairo_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 eachother.

cairo_save(cr);

cairo_scale(cr, 0.6, 0.6);

Page 46: Cairo - Graphics Framework

2/19/13 Transformations

zetcode.com/gfx/cairo/transformations/ 4/7

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);

Page 47: Cairo - Graphics Framework

2/19/13 Transformations

zetcode.com/gfx/cairo/transformations/ 5/7

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 ourellipses.

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

Page 48: Cairo - Graphics Framework

2/19/13 Transformations

zetcode.com/gfx/cairo/transformations/ 6/7

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] = {

Page 49: Cairo - Graphics Framework

2/19/13 Transformations

zetcode.com/gfx/cairo/transformations/ 7/7

{ 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 thestar 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 13, 2012 © 2007 - 2013 Jan Bodnar

Page 50: Cairo - Graphics Framework

2/19/13 Text in Cairo

zetcode.com/gfx/cairo/cairotext/ 1/5

Home Contents

Text in CairoIn 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 andthe 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 thecairo_show_text() function.

Page 51: Cairo - Graphics Framework

2/19/13 Text in Cairo

zetcode.com/gfx/cairo/cairotext/ 2/5

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 ofthe 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.

Page 52: Cairo - Graphics Framework

2/19/13 Text in Cairo

zetcode.com/gfx/cairo/cairotext/ 3/5

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 tothe 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.

Page 53: Cairo - Graphics Framework

2/19/13 Text in Cairo

zetcode.com/gfx/cairo/cairotext/ 4/5

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 glyph is a graphic symbolwhich provides a form for a character. A character provides a meaning. It can have multipleglyphs. A character has no intrinsic appearance. A glyph has no intrinsic meaning.

Note that many common programming requirements conserning text are addressed by the Pangolibrary.

static void do_drawing(cairo_t *cr, GtkWidget *widget){ cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);

Page 54: Cairo - Graphics Framework

2/19/13 Text in Cairo

zetcode.com/gfx/cairo/cairotext/ 5/5

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 thechosen 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 10, 2012 © 2007 - 2013 Jan Bodnar

Page 55: Cairo - Graphics Framework

2/19/13 Images in Cairo

zetcode.com/gfx/cairo/cairoimages/ 1/5

Home Contents

Images in CairoIn this part of the Cairo graphics tutorial, we will talk about the images. We will show how todisplay 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.

Page 56: Cairo - Graphics Framework

2/19/13 Images in Cairo

zetcode.com/gfx/cairo/cairoimages/ 2/5

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 themain 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.

Watermark

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);

Page 57: Cairo - Graphics Framework

2/19/13 Images in Cairo

zetcode.com/gfx/cairo/cairoimages/ 3/5

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 adrawing 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 youwere loading an image into this computer, it was gradually appearing on the screen. The nextexample 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;

Page 58: Cairo - Graphics Framework

2/19/13 Images in Cairo

zetcode.com/gfx/cairo/cairoimages/ 4/5

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);

Page 59: Cairo - Graphics Framework

2/19/13 Images in Cairo

zetcode.com/gfx/cairo/cairoimages/ 5/5

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 getbigger 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 11 , 2012 © 2007 - 2013 Jan Bodnar

Page 60: Cairo - Graphics Framework

2/19/13 The root window

zetcode.com/gfx/cairo/root/ 1/5

Home Contents

Root w indowIn this part of the Cairo graphics tutorial, we will work with the root window. The root window isthe 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 aspecial kind of a window.

Transparent w indow

Our first example will create a transparent window. We will see, what it beneath of the windowobject.

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

Page 61: Cairo - Graphics Framework

2/19/13 The root window

zetcode.com/gfx/cairo/root/ 2/5

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 ofpartial 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 compositionand 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. TheCAIRO_OPERATOR_SOURCE 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 theCAIRO_OPERATOR_CLEAR 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[])

Page 62: Cairo - Graphics Framework

2/19/13 The root window

zetcode.com/gfx/cairo/root/ 3/5

{ 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 GTKwindowing 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 havecreated 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.

Page 63: Cairo - Graphics Framework

2/19/13 The root window

zetcode.com/gfx/cairo/root/ 4/5

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

Page 64: Cairo - Graphics Framework

2/19/13 The root window

zetcode.com/gfx/cairo/root/ 5/5

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 12, 2012 © 2007 - 2013 Jan Bodnar