Showing posts with label Cairo graphics. Show all posts
Showing posts with label Cairo graphics. Show all posts

Monday, April 8, 2013

Step By Step Cairo graphics Tutorials For Beginners

Cairo graphics tutorial

This is Cairo graphics tutorial. The tutorial will teach you the basics of graphics programming in Cairo with the C programming language. This tutorial is for beginners and intermediate developers.

Table of contents

Cairo

Cairo is a library for creating 2D vector graphics. It is written in the C programming language. There are bindings for other computer languages. Python, Perl, C++, C# or Java. Cairo is a multiplatform library. It works on Linux, BSDs, OSX.

Root window in Cairo graphics

Root window

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

Transparent window

Our first example will create a transparent window. We will see, what it beneath of the window object.
#include <cairo.h>
#include <gtk/gtk.h>

static void do_drawing(cairo_t *);


static void tran_setup(GtkWidget *win)
{
GdkScreen *screen;
GdkVisual *visual;

gtk_widget_set_app_paintable(win, TRUE);
screen = gdk_screen_get_default();
visual = gdk_screen_get_rgba_visual(screen);

if (visual != NULL && gdk_screen_is_composited(screen)) {
gtk_widget_set_visual(win, visual);
}
}

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

return FALSE;
}

static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
}

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

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

tran_setup(window);

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

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 250);
gtk_window_set_title(GTK_WINDOW(window), "Transparent window");

gtk_widget_show_all(window);

gtk_main();

return 0;
}
To create a transparent window, we get the visual of the screen object and set it for our window. In the on_draw() method, we draw over the screen's visual object. This createas an illusion of partial transparency.
gtk_widget_set_app_paintable(win, TRUE);
We must set the application to be painted on.
screen = gdk_screen_get_default();
The gdk_screen_get_default() method returns the screen object.
visual = gdk_screen_get_rgba_visual(screen);
From the screen window, we get its visual. The visual contains the low level display information.
if (visual != NULL && gdk_screen_is_composited(screen)) {
gtk_widget_set_visual(win, visual);
}
Not all displays support this operation. Therefore, we check if our screen supports composition and the returned visual is not None. We set the screen's visual to be the visual of our window.
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
}
We use a partially transparent source to draw over the screen window. The CAIRO_OPERATOR_SOURCE creates a composition operation where we draw over the source. Which is the screen window. To get full transparency, we set the alpha value to 0 or use the CAIRO_OPERATOR_CLEAR operator.
Transparent window
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[])
{
gdk_init(&argc, &argv);

GdkWindow *root_win = gdk_get_default_root_window();
gint width = gdk_window_get_width(root_win);
gint height = gdk_window_get_height(root_win);

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

GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height);

cairo_t *cr = cairo_create(surface);
gdk_cairo_set_source_pixbuf(cr, pb, 0, 0);
cairo_paint(cr);

cairo_surface_write_to_png(surface, "image.png");

cairo_destroy(cr);
cairo_surface_destroy(surface);

return 0;
}
The example captures a snapshot of the entire screen. In this example, we do not use the full GTK windowing system. We use Cairo and GDK libraries to do the job.
gdk_init(&argc, &argv);
The gdk_init() initializes the GDK library and connects to the windowing system.
GdkWindow *root_win = gdk_get_default_root_window();
We get the root window with the gdk_get_default_root_window()function call.
gint width = gdk_window_get_width(root_win);
gint height = gdk_window_get_height(root_win);
We determine the width and the height of the root window.
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
width, height);
An empty image surface is created. It has the size of the root window.
GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height);
We get a pixbuf from the root window using the gdk_pixbuf_get_from_window()function call. A pixbuf is an object that describes an image in memory.
cairo_t *cr = cairo_create(surface);        
gdk_cairo_set_source_pixbuf(cr, pb, 0, 0);
cairo_paint(cr);
In the above code lines, we create a Cairo drawing context on the image surface that we have created earlier. We place the pixbuf on the drawing context and paint it on the surface.
cairo_surface_write_to_png(surface, "image.png");
The image surface is written to a PNG image using the write_to_png() method.
cairo_destroy(cr);
cairo_surface_destroy(surface);
We clean up resources.

Showing a message

In the third example, we will show a message on the desktop window.
#include <cairo.h>
#include <gtk/gtk.h>
#include <pango/pango.h>

static void do_drawing(cairo_t *);

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

return FALSE;
}

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

static void setup(GtkWidget *win)
{
gtk_widget_set_app_paintable(win, TRUE);
gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK);
gtk_window_set_keep_below(GTK_WINDOW(win), TRUE);

GdkScreen *screen = gdk_screen_get_default();
GdkVisual *visual = gdk_screen_get_rgba_visual(screen);

if (visual != NULL && gdk_screen_is_composited(screen)) {
gtk_widget_set_visual(win, visual);
}
}

int main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *lbl;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

setup(window);

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

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

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

g_signal_connect(G_OBJECT(window), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 250);

gtk_widget_show_all(window);

gtk_main();

return 0;
}
The code displays a message label on the root window.
static void do_drawing(cairo_t *cr)
{
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_paint(cr);
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_OVERto 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.
In this chapter we have worked with the desktop window in Cairo.

Images in Cairo graphics

Images in Cairo

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

Displaying an image

In the first example, we will display an image.
#include <cairo.h>
#include <gtk/gtk.h>

struct {
cairo_surface_t *image;
} glob;


static void do_drawing(cairo_t *);

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

return FALSE;
}

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


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

glob.image = cairo_image_surface_create_from_png("stmichaelschurch.png");

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

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

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK (gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 220);
gtk_window_set_title(GTK_WINDOW(window), "Image");

gtk_widget_show_all(window);

gtk_main();

cairo_surface_destroy(glob.image);

return 0;
}
The example displays an image.
glob.image = cairo_image_surface_create_from_png("stmichaelschurch.png");
We create an image surface from a png image. For efficiency reasons, the function is called in the main function.
cairo_set_source_surface(cr, glob.image, 10, 10);
We create a source for painting from the created image surface.
cairo_paint(cr);   
We paint the source on the window.
cairo_surface_destroy(glob.image);
In the end, the surface is destroyed.

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

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

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 250);
gtk_window_set_title(GTK_WINDOW(window), "Watermark");

gtk_widget_show_all(window);

gtk_main();

cairo_surface_destroy(glob.image);

return 0;
}
We draw copyright information on an image.
static void load_image()
{
glob.image = cairo_image_surface_create_from_png("beckov.png");
}
In the load_image() method, we create an image surface from a PNG image.
static void draw_mark() 
{
cairo_t *ic;
ic = cairo_create(glob.image);
...
In the draw_mark() funciton, we draw the copyright message on the image. First we create a drawing context from the image surface.
cairo_set_font_size(ic, 11);

cairo_set_source_rgb(ic, 0.9 , 0.9 , 0.9);
cairo_move_to(ic, 20, 30);
cairo_show_text(ic, " Beckov 2012 , (c) Jan Bodnar ");
cairo_stroke(ic);
Then we draw a small text in white colour.
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_paint(cr);
}
The image surface is drawn on the window.

The spectrum effect

We call this a spectrum effect, because it resembles and old ZX Spectrum computer. When you were loading an image into this computer, it was gradually appearing on the screen. The next example is loosly based on this experience.
#include <cairo.h>
#include <gtk/gtk.h>

static void do_drawing(cairo_t *);

struct {
gboolean timer;
cairo_surface_t *image;
cairo_surface_t *surface;
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);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 325, 250);
gtk_window_set_title(GTK_WINDOW(window), "Spectrum");

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

gtk_widget_show_all(window);

gtk_main();

cairo_surface_destroy(glob.image);
cairo_surface_destroy(glob.surface);

return 0;
}
We divide the image into n parts consisting of 8 lines. Each cycle each part of the image will get bigger by one pixel. The created image will serve as a mask for displaying the image of the castle.
struct {
gboolean timer;
cairo_surface_t *image;
cairo_surface_t *surface;
gint img_width;
gint img_height;
} glob;
The glob structure stores variables that are used within more functions.
static void init_vars()
{
glob.image = cairo_image_surface_create_from_png("beckov.png");

glob.img_width = cairo_image_surface_get_width(glob.image);
glob.img_height = cairo_image_surface_get_height(glob.image);

glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
glob.img_width, glob.img_height);
glob.timer = TRUE;
}
In the init_vars() function, we initiate the aforementioned variables.
gint i, j;
for (i = 0; i <= glob.img_height; i+=7) {
for (j = 0 ; j < count; j++) {
cairo_move_to(ic, 0, i+j);
cairo_line_to(ic, glob.img_width, i+j);
}
}
We gradully draw lines into each of the n parts.
count++;
if (count == 8) glob.timer = FALSE;
After 8 steps, the animation finishes.
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_mask_surface(cr, glob.surface, 10, 10);
cairo_stroke(ic);
Using the mask operation, we draw the portions of the image on the window.
This chapter covered images in Cairo.

Text in Cairo graphics

Text in Cairo

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

Soulmate

In the first example, we will display some lyrics on the GTK+ window.
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);

cairo_select_font_face(cr, "Purisa",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);

cairo_set_font_size(cr, 13);

cairo_move_to(cr, 20, 30);
cairo_show_text(cr, "Most relationships seem so transitory");
cairo_move_to(cr, 20, 60);
cairo_show_text(cr, "They're all good but not the permanent one");

cairo_move_to(cr, 20, 120);
cairo_show_text(cr, "Who doesn't long for someone to hold");

cairo_move_to(cr, 20, 150);
cairo_show_text(cr, "Who knows how to love you without being told");
cairo_move_to(cr, 20, 180);
cairo_show_text(cr, "Somebody tell me why I'm on my own");
cairo_move_to(cr, 20, 210);
cairo_show_text(cr, "If there's a soulmate for everyone");
}
In this example, we display part of the lyrics from the Natasha Bedingfield's Soulmate song.
cairo_select_font_face(cr, "Purisa",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);
Here we select the font face. The function takes three parameters, the font family, font slant and the font weight.
cairo_set_font_size(cr, 13);
Here we specify the font size.
cairo_move_to(cr, 20, 30);
cairo_show_text(cr, "Most relationships seem so transitory");
We display the text on the window by specifying the position of the text and calling the cairo_show_text() function.
Soulmate
Figure: Soulmate

Centered text

Next we will show, how to center text on the window.
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_text_extents_t extents;

GtkWidget *win = gtk_widget_get_toplevel(widget);

gint w, h;
gtk_window_get_size(GTK_WINDOW(win), &w, &h);

cairo_select_font_face(cr, "Courier",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);

cairo_set_font_size(cr, 60);

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

cairo_move_to(cr, w/2 - extents.width/2, h/2);
cairo_show_text(cr, "ZetCode");
}
The code will center a text on the window. It remains centered, even if we resize the window.
GtkWidget *win = gtk_widget_get_toplevel(widget);

gint w, h;
gtk_window_get_size(GTK_WINDOW(win), &w, &h);
To center a text on the window, it is necessary to get the size of of the parent window.
cairo_select_font_face(cr, "Courier",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);

cairo_set_font_size(cr, 60);
We select a font and its size to be displayed.
cairo_text_extents(cr, "ZetCode", &extents);
We get the text extents. These are some numbers that describe the text. We need the width of the text for our example.
cairo_move_to(cr, w/2 - extents.width/2, h/2);  
cairo_show_text(cr, "ZetCode");
We position the text into the middle of the window and show it using the cairo_show_text() method.

Shaded text

Now we will show a shaded text on the window.
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size(cr, 50);

cairo_set_source_rgb(cr, 0, 0, 0);
cairo_move_to(cr, 40, 60);
cairo_show_text(cr, "ZetCode");

cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_move_to(cr, 43, 63);
cairo_show_text(cr, "ZetCode");
}
To create a shade, we draw the text twice. In different colours. The second text is moved a bit to the right and bottom.
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_move_to(cr, 40, 60);
cairo_show_text(cr, "ZetCode");
The first text is drawn in black ink. It serves as a shade.
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_move_to(cr, 43, 63);
cairo_show_text(cr, "ZetCode");
The second text is drawn in some gray ink. It is moved by 3px to the right and to the bottom.

Text filled with gradient

The following example will create a nice effect. We will fill a text with some linear gradient.
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.

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 symbol which provides a form for a character. A character provides a meaning. It can have multiple glyphs. A character has no intrinsic appearance. A glyph has no intrinsic meaning.
Note that many common programming requirements conserning text are addressed by the Pango library.
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 13);

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

gint i = 0;
gint x, y;

for (y=0; y<20; y++) {
for (x=0; x<35; x++) {
glyphs[i] = (cairo_glyph_t) {i, x*15 + 20, y*18 + 20};
i++;
}
}

cairo_show_glyphs(cr, glyphs, n_glyphs);
}
This code shows 700 glyphs of a chosen font.
const int n_glyphs = 20 * 35;
cairo_glyph_t glyphs[n_glyphs];
The glyphs array will store three integer values. The first value is the index of the glyph to the chosen font type. The second and the third values are x, y positions of a glyph.
cairo_show_glyphs(cr, glyphs, n_glyphs);
The cairo_show_glyphs() method shows the glyphs on the window.
This chapter covered text in Cairo.

Transformations in Cairo graphics

Transformations

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

Translation

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

cairo_translate(cr, 20, 20);
cairo_set_source_rgb(cr, 0.8, 0.3, 0.2);
cairo_rectangle(cr, 0, 0, 30, 30);
cairo_fill(cr);

cairo_translate(cr, 30, 30);
cairo_set_source_rgb(cr, 0.8, 0.8, 0.2);
cairo_rectangle(cr, 0, 0, 30, 30);
cairo_fill(cr);

cairo_translate(cr, 40, 40);
cairo_set_source_rgb(cr, 0.3, 0.8, 0.8);
cairo_rectangle(cr, 0, 0, 30, 30);
cairo_fill(cr);
}
The examle draws a rectangle. Then we do a translation and draw the same rectangle again.
cairo_translate(cr, 20, 20);
The cairo_translate() function modifies the current transormation matrix by tranlating the user space origin. In our case we shift the origin by 20 units in both directions.
Translation
Figure: Translation

Shear

In the following example, we perform a shearing operation. A shearing is an object distortion along a particular axis. There is no shear function for this operation. We need to create our own transformation matrix. Note that each affine transformation can be performed by creating a transformation matrix.
static void do_drawing(cairo_t *cr)
{
cairo_matrix_t matrix;

cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
cairo_rectangle(cr, 20, 30, 80, 50);
cairo_fill(cr);
cairo_matrix_init(&matrix,
1.0, 0.5,
0.0, 1.0,
0.0, 0.0);

cairo_transform(cr, &matrix);
cairo_rectangle(cr, 130, 30, 80, 50);
cairo_fill(cr);
}
In this code example, we perform a simple shearing operation.
cairo_matrix_t matrix;
The cairo_matrix_t is a structure that holds an affine transformation.
cairo_matrix_init(&matrix,
1.0, 0.5,
0.0, 1.0,
0.0, 0.0);
This transformation shears y values by 0.5 of the x values.
cairo_transform(cr, &matrix);
We perform the transformation with the transform() method.
Shearing
Figure: Shearing

Scaling

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

cairo_scale(cr, 0.6, 0.6);
cairo_set_source_rgb(cr, 0.8, 0.3, 0.2);
cairo_rectangle(cr, 30, 30, 90, 90);
cairo_fill(cr);

cairo_scale(cr, 0.8, 0.8);
cairo_set_source_rgb(cr, 0.8, 0.8, 0.2);
cairo_rectangle(cr, 50, 50, 90, 90);
cairo_fill(cr);
}
We draw three rectangles of 90x90px size. On two of them, we perform a scaling operation.
cairo_scale(cr, 0.6, 0.6);
cairo_set_source_rgb(cr, 0.8, 0.3, 0.2);
cairo_rectangle(cr, 30, 30, 90, 90);
cairo_fill(cr);
We uniformly scale a rectangle by a factor of 0.6.
cairo_scale(cr, 0.8, 0.8);
cairo_set_source_rgb(cr, 0.8, 0.8, 0.2);
cairo_rectangle(cr, 50, 50, 90, 90);
cairo_fill(cr);
Here we perform another scaling operation by a factor of 0.8. If we look at the picture, we see, that the third yellow rectangle is the smallest one. Even if we have used a smaller scaling factor. This is because transformation operations are additive. In fact, the third rectangle was scaled by a factor of 0.528 (0.6x0.8).
Scaling
Figure: Scaling

Isolating transformations

Transformation operations are additive. To isolate one operation from the other one, we can use the cairo_save() and cairo_restore()functions. The cairo_save() function makes a copy of the current state of the drawing context and saves it on an internal stack of saved states. The cairo_restore() function will re-establish the context to the saved state.
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0.2, 0.3, 0.8);
cairo_rectangle(cr, 10, 10, 90, 90);
cairo_fill(cr);

cairo_save(cr);
cairo_scale(cr, 0.6, 0.6);
cairo_set_source_rgb(cr, 0.8, 0.3, 0.2);
cairo_rectangle(cr, 30, 30, 90, 90);
cairo_fill(cr);
cairo_restore(cr);

cairo_save(cr);
cairo_scale(cr, 0.8, 0.8);
cairo_set_source_rgb(cr, 0.8, 0.8, 0.2);
cairo_rectangle(cr, 50, 50, 90, 90);
cairo_fill(cr);
cairo_restore(cr);
}
In the example we scale two rectangles. This time we isolate the scaling operations from each other.
cairo_save(cr);
cairo_scale(cr, 0.6, 0.6);
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.
Isolating transformations
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);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

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

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 250);
gtk_window_set_title(GTK_WINDOW(window), "Donut");

gtk_widget_show_all(window);

gtk_main();

return 0;
}
We will do rotation and scaling operations. We will also save and restore Cairo contexts.
cairo_translate(cr, width/2, height/2);
cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);
cairo_stroke(cr);
In the middle of the GTK+ window, we create a circle. This will be a bounding circle for our ellipses.
gint i;
for (i = 0; i < 36; i++) {
cairo_save(cr);
cairo_rotate(cr, i*M_PI/36);
cairo_scale(cr, 0.3, 1);
cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);
cairo_restore(cr);
cairo_stroke(cr);
}
We create 36 ellipses along the path of our bounding circle. We insulate each rotate and scale operation from one another with the cairo_save() and cairo_restore() methods.

Star

The next example shows a rotating and scaling star.
#include <cairo.h>
#include <gtk/gtk.h>

static void do_drawing(cairo_t *, GtkWidget *widget);

int points[11][2] = {
{ 0, 85 },
{ 75, 75 },
{ 100, 10 },
{ 125, 75 },
{ 200, 85 },
{ 150, 125 },
{ 160, 190 },
{ 100, 150 },
{ 40, 190 },
{ 50, 125 },
{ 0, 85 }
};

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

return FALSE;
}

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] = { 
{ 0, 85 },
{ 75, 75 },
{ 100, 10 },
...
The star object will be constructed from these points.
static gdouble angle = 0;
static gdouble scale = 1;
static gdouble delta = 0.01;
We initialize three important variables. The angle is used in the rotation, the scale in scaling the star object. The delta variable controls when the star is growing and when it is shrinking.
cairo_translate(cr, width/2, height/2);
cairo_rotate(cr, angle);
cairo_scale(cr, scale, scale);
We shift the star into the middle of the window. Rotate it and scale it.
gint i;
for ( i = 0; i < 10; i++ ) {
cairo_line_to(cr, points[i][0], points[i][1]);
}

cairo_close_path(cr);
cairo_fill(cr);
cairo_stroke(cr);
Here we draw the star object.
if ( scale < 0.01 ) {
delta = -delta;
} else if (scale > 0.99) {
delta = -delta;
}
These lines control the growing or shrinking of the star object.
In this part of the Cairo graphics tutorial, we talked about transformations.

Clipping and masking in Cairo graphics

Clipping and masking

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

Clipping

Clipping is restricting of drawing to a certain area. This is done for effeciency reasons and to create interesting effects.
In the following example we will be clipping an image.
#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>

static void do_drawing(cairo_t *, GtkWidget *);

struct {
cairo_surface_t *image;
} glob;

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

return FALSE;
}

static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
static gint pos_x = 128;
static gint pos_y = 128;
static gint radius = 40;
static gint delta[] = { 3, 3 };

GtkWidget *win = gtk_widget_get_toplevel(widget);

gint width, height;
gtk_window_get_size(GTK_WINDOW(win), &width, &height);

if (pos_x < 0 + radius) {
delta[0] = rand() % 4 + 5;
} else if (pos_x > width - radius) {
delta[0] = -(rand() % 4 + 5);
}

if (pos_y < 0 + radius) {
delta[1] = rand() % 4 + 5;
} else if (pos_y > height - radius) {
delta[1] = -(rand() % 4 + 5);
}

pos_x += delta[0];
pos_y += delta[1];

cairo_set_source_surface(cr, glob.image, 1, 1);
cairo_arc(cr, pos_x, pos_y, radius, 0, 2*M_PI);
cairo_clip(cr);
cairo_paint(cr);
}

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

int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
gint width, height;

glob.image = cairo_image_surface_create_from_png("turnacastle.png");
width = cairo_image_surface_get_width(glob.image);
height = cairo_image_surface_get_height(glob.image);

gtk_init(&argc, &argv);

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

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), width+2, height+2);
gtk_window_set_title(GTK_WINDOW(window), "Clip image");

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

gtk_main();

cairo_surface_destroy(glob.image);

return 0;
}
In this example, we will clip an image. A circle is moving on the screen and showing a part of the underlying image. This is as if we looked through a hole.
if (pos_x < 0 + radius) {
delta[0] = rand() % 4 + 5;
} else if (pos_x > width - radius) {
delta[0] = -(rand() % 4 + 5);
}
If the circle hits the left or the right side of the window, the direction of the circle movement changes randomly. Same for the top and bottom sides.
cairo_set_source_surface(cr, glob.image, 1, 1);
cairo_arc(cr, pos_x, pos_y, radius, 0, 2*M_PI);
Here we draw the image and a circle. Notice that we do not draw onto the window at the moment, but only in memory.
cairo_clip(cr);
The cairo_clip() sets a clipping region. The clipping region is the current path used. The current path was created by the cairo_arc() function call.
cairo_paint(cr);
The cairo_paint() paints the current source everywhere within the current clip region.
glob.image = cairo_image_surface_create_from_png("turnacastle.png");
An image surface is created from a PNG image using the cairo_image_surface_create_from_png() function.
Clipping image
Figure: Clipping image

Mask

Before the source is applied to the surface, it is filtered first. The mask is used as a filter. The mask determines, where the sourse is applied and where not. Opaque parts of the mask allow to copy the source. Transparent parts do not let to copy the source to the surface.
#include <cairo.h>
#include <gtk/gtk.h>


static void do_drawing(cairo_t *);

struct {
cairo_surface_t *surface;
} glob;

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

return FALSE;
}

static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_mask_surface(cr, glob.surface, 0, 0);
cairo_fill(cr);
}

static void create_surface()
{
glob.surface = cairo_image_surface_create_from_png("omen.png");
}

static void destroy_surface()
{
cairo_surface_destroy(glob.surface);
}


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

gtk_init(&argc, &argv);

create_surface();

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

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

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 305, 100);
gtk_window_set_title(GTK_WINDOW(window), "Mask");

gtk_widget_show_all(window);

gtk_main();

destroy_surface();

return 0;
}
This small example clearly shows the basic idea behind the mask. The mask determines where to paint and where not to paint.
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_mask_surface(cr, glob.surface, 0, 0);
cairo_fill(cr);
}
In the do_drawing() function, we use an image as a mask. And it is therefore displayed on the window.
Applying a mask
Figure: Applying a mask

Blind down 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;
}

static void do_drawing(cairo_t *cr)
{
cairo_t *ic;
static gint h = 0;

ic = cairo_create(glob.surface);

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

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

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

cairo_destroy(ic);
}

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

static void cleanup()
{
cairo_surface_destroy(glob.image);
cairo_surface_destroy(glob.surface);
}

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

gtk_init(&argc, &argv);

init_vars();

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

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

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 325, 250);
gtk_window_set_title(GTK_WINDOW(window), "Blind down");

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

gtk_widget_show_all(window);

gtk_main();

cleanup();

return 0;
}
The idea behind the blind down effect is simple. The image is h pixels high. We draw 0, 1, 2 ... lines of 1px height. Each cycle the portion of the image is 1px higher, until the whole image is visible.
struct {
cairo_surface_t *image;
cairo_surface_t *surface;
gboolean timer;
gint img_width;
gint img_height;
} glob;
In the glob structure, we will store two surfaces, a timer and the image width and height variables.
static void init_vars()
{
glob.timer = TRUE;
glob.image = cairo_image_surface_create_from_png("beckov.png");
glob.img_width = cairo_image_surface_get_width(glob.image);
glob.img_height = cairo_image_surface_get_height(glob.image);
glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
glob.img_width, glob.img_height);
}
In the init_vars() function, we initiate the previously declared variables. The last line creates an empty image surface. It is going to be filled with lines of pixels from the image surface, that we have created earlier.
ic = cairo_create(glob.surface);
We create a cairo context from the empty image source.
cairo_rectangle(ic, 0, 0, glob.img_width, h);
cairo_fill(ic);
We draw a rectangle into the initially empty image. The rectangle will be 1px higher each cycle. The image created this way will serve as a mask later.
h += 1;
The height of the image to show is increased by one unit.
if ( h == glob.img_height) glob.timer = FALSE;
We stop the timer function when we draw the whole image on the GTK window.
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_mask_surface(cr, glob.surface, 10, 10);
The image of a castle is set as a source for painting. The cairo_mask_surface() paints the current source using the alpha channel of surface as a mask.
static void cleanup()
{
cairo_surface_destroy(glob.image);
cairo_surface_destroy(glob.surface);
}
In the cleanup() function we destroy the created surfaces.
This chapter was about clipping and masking in Cairo.

Sunday, April 7, 2013

Compositing in Cairo graphics

Compositing

In 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. They are used to create the illusion that all those elements are parts of the same scene. Compositing is videly used in film industry to create crowds, entire new worlds which would be expensive or impossible to create otherwise. (wikipedia.org)

Operations

There are several compositing operations. The Cairo graphics library has 14 different compositing operations.
#include <cairo.h>
#include <gtk/gtk.h>

void do_drawing(cairo_t *cr, gint x, gint w,
gint h, cairo_operator_t op)
{
cairo_t *first_cr, *second_cr;
cairo_surface_t *first, *second;

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

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

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

second_cr = cairo_create(second);
cairo_set_source_rgb(second_cr, 0.5, 0.5, 0);
cairo_rectangle(second_cr, x+10, 40, 50, 50);
cairo_fill(second_cr);

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

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

cairo_surface_destroy(first);
cairo_surface_destroy(second);

cairo_destroy(first_cr);
cairo_destroy(second_cr);

}

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

cairo_operator_t oper[] = {
CAIRO_OPERATOR_DEST_OVER,
CAIRO_OPERATOR_DEST_IN,
CAIRO_OPERATOR_OUT,
CAIRO_OPERATOR_ADD,
CAIRO_OPERATOR_ATOP,
CAIRO_OPERATOR_DEST_ATOP,
};

GtkWidget *win = gtk_widget_get_toplevel(widget);

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,
CAIRO_OPERATOR_ATOP,
CAIRO_OPERATOR_DEST_ATOP,
};
In our example, we use these six compositing operations.
Compositing operations
Figure: Compositing operations
This chapter covered Cairo compositing.

Transparency in Cairo graphics

Transparency

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

Transparent rectangles

The first example will draw ten rectangles with different levels of transparency.
static void do_drawing(cairo_t *cr)
{
gint i;
for ( i = 1; i <= 10; i++) {
cairo_set_source_rgba(cr, 0, 0, 1, i*0.1);
cairo_rectangle(cr, 50*i, 20, 40, 40);
cairo_fill(cr);
}
}
The cairo_set_source_rgba() has an optional alpha parameter to provide transparency. This code creates ten rectangles with alpha values from 0.1 ... 1.
Transparency
Figure: Transparency

Puff effect

In the following example, we create a puff effect. The example will display a growing centered text that will gradually fade out from some point. This is a very common effect which we can often see in flash animations. The cairo_paint_with_alpha() method is crucial to create the effect.
#include <cairo.h>
#include <gtk/gtk.h>

void do_drawing(cairo_t *, GtkWidget *);

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

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
cr = gdk_cairo_create(gtk_widget_get_window(widget));
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);

gtk_widget_show_all(window);

gtk_main();

return 0;
}
The example creates a growing and fading text on the window.
struct {
gboolean timer;
gdouble alpha;
gdouble size;
} glob;
Here we define some variables inside a structure. This is used to avoid using globals.
draw_text(cr, widget); 
The actual drawing of the text is delegated to the draw_text() function.
GtkWidget *win = gtk_widget_get_toplevel(widget);

gint width, height;
gtk_window_get_size(GTK_WINDOW(win), &width, &height);

gint x = width/2;
gint y = height/2;
The text is going to be centered on the window. Therefore we need to find out the size of the parent widget.
cairo_set_source_rgb(cr, 0.5, 0, 0); 
cairo_paint(cr);
The background of the window is filled with some dark red colour.
cairo_select_font_face(cr, "Courier",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);
The text is going to be in Courier bold font.
glob.size += 0.8;

if (glob.size > 20) {
glob.alpha -= 0.01;
}
The size of the text is increased by 0.8 units. After it has reached 20 units, the alpha value starts decreasing. And the text fades away slowly.
cairo_text_extents(cr, "ZetCode", &extents);
cairo_move_to(cr, x - extents.width/2, y);
We get the text metrics. We will use only the text width. We move to a position where the text will be centered on the window.
cairo_text_path(cr, "ZetCode");
cairo_clip(cr);

cairo_paint_with_alpha(cr, glob.alpha);
We get the path of the text with the cairo_text_path() method. We restrict the painting to the current path using the cairo_clip() method. The cairo_paint_with_alpha() method paints the current source everywhere within the current clip region using a mask of the alpha value.
glob.timer = TRUE;
glob.alpha = 1.0;
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.

Waiting demo

In this examle, we use transparency effect to create a waiting demo. We will draw 8 lines that will gradually fade out creating an illusion, that a line is moving. Such effects are often used to inform users, that a lengthy task is going on behind the scenes. An example is streaming video over the internet.
#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>

static void do_drawing(cairo_t *, GtkWidget *);

struct {
gushort count;
} glob;

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

return FALSE;
}

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

GtkWidget *win = gtk_widget_get_toplevel(widget);

gint width, height;
gtk_window_get_size(GTK_WINDOW(win), &width, &height);

cairo_translate(cr, width/2, height/2);

gint i = 0;
for (i = 0; i < 8; i++) {
cairo_set_line_width(cr, 3);
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
cairo_set_source_rgba(cr, 0, 0, 0, trs[glob.count%8][i]);

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

cairo_stroke(cr);
}
}

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

return TRUE;
}

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

glob.count = 0;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

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

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 150);
gtk_window_set_title(GTK_WINDOW(window), "Waiting demo");

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

gtk_main();

return 0;
}
We draw eight lines with eight different alpha values.
 static gdouble const trs[8][8] = {
{ 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
{ 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
{ 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 },
{ 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65},
{ 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 },
{ 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 },
{ 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 },
{ 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, }
};
This is a two dimensional array of transparency values used in this demo. There are 8 rows, each for one state. Each of the 8 lines will continuosly use these values.
cairo_set_line_width(cr, 3);
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
We make the lines a bit thicker, so that they are better visible. We draw the lines with rouded caps.
cairo_set_source_rgba(cr, 0, 0, 0, trs[glob.count%8][i]);
Here we define the transparency value for a line.
cairo_move_to(cr, 0.0, -10.0);
cairo_line_to(cr, 0.0, -40.0);
cairo_rotate(cr, M_PI/4);
These code will draw each of the eight lines.
g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) window);
We use a timer function to create animation.
Waiting demo
Figure: Waiting demo
In this part of the Cairo tutorial, we have covered transparency.

Gradients in Cairo graphics

Gradients

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

Linear gradients

Linear gradients are blendings of colours or shades of colours along a line. They are created with the cairo_pattern_create_linear() function.
#include <cairo.h>
#include <gtk/gtk.h>

void draw_gradient1(cairo_t *);
void draw_gradient2(cairo_t *);
void draw_gradient3(cairo_t *);

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

draw_gradient1(cr);
draw_gradient2(cr);
draw_gradient3(cr);

cairo_destroy(cr);

return FALSE;
}

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

gdouble j;
gint count = 1;
for ( j = 0.1; j < 1; j += 0.1 ) {
if (( count % 2 )) {
cairo_pattern_add_color_stop_rgb(pat1, j, 0, 0, 0);
} else {
cairo_pattern_add_color_stop_rgb(pat1, j, 1, 0, 0);
}
count++;
}

cairo_rectangle(cr, 20, 20, 300, 100);
cairo_set_source(cr, pat1);
cairo_fill(cr);

cairo_pattern_destroy(pat1);
}

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

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

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

cairo_pattern_destroy(pat2);
}

void draw_gradient3(cairo_t *cr)
{
cairo_pattern_t *pat3;
pat3 = cairo_pattern_create_linear(20.0, 260.0, 20.0, 360.0);

cairo_pattern_add_color_stop_rgb(pat3, 0.1, 0, 0, 0);
cairo_pattern_add_color_stop_rgb(pat3, 0.5, 1, 1, 0);
cairo_pattern_add_color_stop_rgb(pat3, 0.9, 0, 0, 0);

cairo_rectangle(cr, 20, 260, 300, 100);
cairo_set_source(cr, pat3);
cairo_fill(cr);

cairo_pattern_destroy(pat3);
}

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

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

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

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 340, 390);
gtk_window_set_title(GTK_WINDOW(window), "Linear gradients");

gtk_widget_set_app_paintable(window, TRUE);
gtk_widget_show_all(window);

gtk_main();

return 0;
}
The example draws three rectangles filled with linear gradients.
pat3 = cairo_pattern_create_linear(20.0, 260.0, 20.0, 360.0);
Here we create a linear gradient pattern. The parameters specify the line, along which we draw the gradient. In our case it is a vertical line.
cairo_pattern_add_color_stop_rgb(pat3, 0.1, 0, 0, 0);
cairo_pattern_add_color_stop_rgb(pat3, 0.5, 1, 1, 0);
cairo_pattern_add_color_stop_rgb(pat3, 0.9, 0, 0, 0);
We define colour stops to produce our gradient pattern. In this case, the gradient is a blending of black and yellow colours. By adding two black and one yellow stops, we create a horizontal gradient pattern. What these stops actually mean? In our case, we begin with black colour, which will stop at 1/10 of the size. Then we begin to gradually paint in yellow, which will culminate at the centre of the shape. The yellow colour stops at 9/10 of the size, where we begin painting in black again, until the end.
Linear gradients
Figure: Linear gradients

Radial gradients

Radial gradients are blendings of colours or shades of colours between two circles. The cairo_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);
cairo_pattern_add_color_stop_rgb(r2, 0.8, 0, 0, 0);
cairo_set_source(cr, r2);
cairo_arc(cr, 0, 0, 40, 0, M_PI * 2);
cairo_fill(cr);
}


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

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

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

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Radial gradients");

gtk_widget_set_app_paintable(window, TRUE);
gtk_widget_show_all(window);

gtk_main();

return 0;
}
In the example, we draw two radial gradients.
r1 = cairo_pattern_create_radial(30, 30, 10, 30, 30, 90);
cairo_pattern_add_color_stop_rgba(r1, 0, 1, 1, 1, 1);
cairo_pattern_add_color_stop_rgba(r1, 1, 0.6, 0.6, 0.6, 1);
cairo_set_source(cr, r1);
cairo_arc(cr, 0, 0, 40, 0, M_PI * 2);
cairo_fill(cr);
We draw a circle and fill its inside with a radial gradient. The radial gradient is defined by two circles. The cairo_pattern_add_color_stop_rgba()function defines the colours. We can experiment with the position of the circles or the length of their radius. In the first gradient example, we have created an object which resembles a 3D shape.
r2 = cairo_pattern_create_radial(0, 0, 10, 0, 0, 40);  
cairo_pattern_add_color_stop_rgb(r2, 0, 1, 1, 0);
cairo_pattern_add_color_stop_rgb(r2, 0.8, 0, 0, 0);
cairo_set_source(cr, r2);
cairo_arc(cr, 0, 0, 40, 0, M_PI * 2);
cairo_fill(cr);
In this example, the circles that define the radial gradient and the custom drawn circle have a common center point.
Radial gradients
Figure: Radial gradients
In this chapter of the Cairo graphics tutorial we have covered gradients.

Shapes and fills in Cairo

Shapes and fills in Cairo

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

Basic shapes

The Cairo API has some basic functions to create simple shapes.
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
cairo_set_line_width(cr, 1);

cairo_rectangle(cr, 20, 20, 120, 80);
cairo_rectangle(cr, 180, 20, 80, 80);
cairo_stroke_preserve(cr);
cairo_fill(cr);

cairo_arc(cr, 330, 60, 40, 0, 2*M_PI);
cairo_stroke_preserve(cr);
cairo_fill(cr);

cairo_arc(cr, 90, 160, 40, M_PI/4, M_PI);
cairo_close_path(cr);
cairo_stroke_preserve(cr);
cairo_fill(cr);

cairo_translate(cr, 220, 180);
cairo_scale(cr, 1, 0.7);
cairo_arc(cr, 0, 0, 50, 0, 2*M_PI);
cairo_stroke_preserve(cr);
cairo_fill(cr);
}
In this example, we will create a rectangle, a square, a circle, an arc and an ellipse.
cairo_rectangle(cr, 20, 20, 120, 80);
cairo_rectangle(cr, 180, 20, 80, 80);
The cairo_rectangle() is used to create both squares and rectangles. A square is just a specific type of a rectangle.
cairo_arc(cr, 330, 60, 40, 0, 2*M_PI);
This line creates a circle.
cairo_scale(cr, 1, 0.7);
cairo_arc(cr, 0, 0, 50, 0, 2*M_PI);
We use the cairo_scale() function call to create an ellipse.
Basic shapes
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);

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 460, 240);
gtk_window_set_title(GTK_WINDOW(window), "Other shapes");

gtk_widget_show_all(window);

gtk_main();

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

cairo_close_path(cr);
The star is drawn by joining all the points that are in the points array. The star is finished by calling the cairo_close_path() function, which joins the last two points of a star.
cairo_move_to(cr, 380, 40);
cairo_line_to(cr, 380, 160);
cairo_line_to(cr, 450, 160);
cairo_curve_to(cr, 440, 155, 380, 145, 380, 40);
The modified triangle is a simple combination of two lines and one curve.
Other shapes
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);

cairo_set_source_rgb(cr, 1, 0, 0.5);
cairo_rectangle(cr, 150, 140, 100, 100);
cairo_fill(cr);
}
In the example we draw four coloured rectangles.
cairo_set_source_rgb(cr, 0.5, 0.5, 1);
cairo_rectangle(cr, 20, 20, 100, 100);
cairo_fill(cr);
The cairo_set_source_rgb() function call sets the source to an opaque colour. The parameters are the Red, Green, Blue intensity values. The source is used to fill the interior of a rectangle by calling the cairo_fill() function.
Solid colours
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;
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 use four pattern images from the Gimp image manipulation program. We must retain the original size of those patterns, because we are going to tile them.
We create image surfaces outside the on_draw_event() function. It would not be efficient to read from harddisk each time, the window needs to be redrawn.
pattern1 = cairo_pattern_create_for_surface(surface1);
We create a pattern from the surface by calling the cairo_pattern_create_for_surface() function.
cairo_set_source(cr, pattern1);
cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
cairo_rectangle(cr, 20, 20, 100, 100);
cairo_fill(cr);
Here we draw our first rectangle. The cairo_set_source() tells the Cairo context to use a pattern as a source for drawing. The image patterns may not fit exactly the shape. We set the mode to CAIRO_EXTEND_REPEAT, which causes the pattern to be tiled by repeating. The cairo_rectangle() creates a rectangular path. Finally, cairo_fill() fills the path with the source.
This chapter covered Cairo shapes and fills.

Basic drawing in Cairo

Basic drawing in Cairo

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

Lines

Lines are very basic vector objects. To draw a line, we use two function calls. The starting point is specified with the cairo_move_to() call. The ending point of a line is specified with the cairo_line_to() call.
#include <cairo.h>
#include <gtk/gtk.h>

static void do_drawing(cairo_t *);

struct {
int count;
double coordx[100];
double coordy[100];
} glob;

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

return FALSE;
}

static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width(cr, 0.5);

int i, j;
for (i = 0; i <= glob.count - 1; i++ ) {
for (j = 0; j <= glob.count - 1; j++ ) {
cairo_move_to(cr, glob.coordx[i], glob.coordy[i]);
cairo_line_to(cr, glob.coordx[j], glob.coordy[j]);
}
}

glob.count = 0;
cairo_stroke(cr);
}

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

if (event->button == 3) {
gtk_widget_queue_draw(widget);
}

return TRUE;
}


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

glob.count = 0;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

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

gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);

g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);

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

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_window_set_title(GTK_WINDOW(window), "Lines");

gtk_widget_show_all(window);

gtk_main();

return 0;
}
In our example, we click randomly on a window with a left mouse button. Each click is stored in an array. When we right click on the window, all points are connected with every point in the array. This way, we can create some interesting objects. Right clicking on the drawn object clears the window, and we can click another object.
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width (cr, 0.5);
The lines will be drawn in black ink and will be 0.5 points wide.
int i, j;
for (i = 0; i <= glob.count - 1; i++ ) {
for (j = 0; j <= glob.count - 1; j++ ) {
cairo_move_to(cr, glob.coordx[i], glob.coordy[i]);
cairo_line_to(cr, glob.coordx[j], glob.coordy[j]);
}
}
We connect every point from the array to every other point.
cairo_stroke(cr);
The cairo_stroke() call draws the lines.
g_signal_connect(window, "button-press-event", 
G_CALLBACK(clicked), NULL);
We connect the button-press-event to the clicked callback.
if (event->button == 1) {
glob.coordx[glob.count] = event->x;
glob.coordy[glob.count++] = event->y;
}
Inside the clicked callback, we determine, if we there was a left or right click. If we click with a left mouse button, we store the x, y coordinates into the arrays.
if (event->button == 3) {
gtk_widget_queue_draw(widget);
}
By right clicking, we redraw the window.
Lines
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);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Fill & stroke");

gtk_widget_show_all(window);

gtk_main();

return 0;
}
In our example, we will draw a circle and fill it with a solid color.
#include <math.h>
This header file is needed for the M_PI constant.
GtkWidget *win = gtk_widget_get_toplevel(widget);

int width, height;
gtk_window_get_size(GTK_WINDOW(win), &width, &height);
Here we get the width and height of the window. We will need these values, when we draw the circle. The circle will be resized, when we resize the window.
cairo_set_line_width(cr, 9);  
cairo_set_source_rgb(cr, 0.69, 0.19, 0);
We set a line width with the set_line_width() method. We set the source to some dark red colour using the set_source_rgb() method.
cairo_translate(cr, width/2, height/2);
cairo_arc(cr, 0, 0, 50, 0, 2 * M_PI);
cairo_stroke_preserve(cr);
With the cairo_translate() method, we move the drawing origin to the center of the window. We want our circle to be centered. The arc() method adds a new circular path to the cairo drawing context. Finally, the stroke_preserve() method draws the outline of the circle. Unlike the stroke() method, it also preserves the shape for later drawing.
cairo_set_source_rgb(cr, 0.3, 0.4, 0.6); 
cairo_fill(cr);
Here we fill the cirle with blue color.
Fill and stroke
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 used by the cairo_stroke() function call. The dash pattern is specified by the cairo_set_dash() function. The pattern is set by the dash array, which is an array of positive floating point values. They set the on and off parts of the dash pattern. We also specify the length of the array and the offset value. If the length is 0, the dashing is disabled. If it is 1, a symmetric pattern is asumed with alternating on and off portions of the size specified by the single value in dashes.
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgba(cr, 0, 0, 0, 1);

static const double dashed1[] = {4.0, 21.0, 2.0};
static int len1 = sizeof(dashed1) / sizeof(dashed1[0]);

static const double dashed2[] = {14.0, 6.0};
static int len2 = sizeof(dashed2) / sizeof(dashed2[0]);

static const double dashed3[] = {1.0};

cairo_set_line_width(cr, 1.5);

cairo_set_dash(cr, dashed1, len1, 0);

cairo_move_to(cr, 40, 30);
cairo_line_to(cr, 200, 30);
cairo_stroke(cr);

cairo_set_dash(cr, dashed2, len2, 1);

cairo_move_to(cr, 40, 50);
cairo_line_to(cr, 200, 50);
cairo_stroke(cr);

cairo_set_dash(cr, dashed3, 1, 0);

cairo_move_to(cr, 40, 70);
cairo_line_to(cr, 200, 70);
cairo_stroke(cr);
}
In this example, we will draw three lines with different dash patterns.
static const double dashed1[] = {4.0, 21.0, 2.0};
We have a pattern of three numbers. We have 4 points drawn, 21 not drawn and 2 drawn. Then 4 points not drawn, 21 points drawn and 2 not drawn. This pattern takes turns until the end of the line.
static int len1  = sizeof(dashed1) / sizeof(dashed1[0]);
We get the size of the array.
cairo_set_dash(cr, dashed1, len1, 0);
We set the dash.
static const double dashed3[] = {1.0};
...
cairo_set_dash(cr, dashed3, 1, 0);

cairo_move_to(cr, 40, 70);
cairo_line_to(cr, 200, 70);
cairo_stroke(cr);
These lines create a line with a pen dash of a symmetric pattern of alternating single on and off points.
Dashes
Figure: Dashes

Line caps

The line caps are endpoints of lines.
  • CAIRO_LINE_CAP_SQUARE
  • CAIRO_LINE_CAP_ROUND
  • CAIRO_LINE_CAP_BUTT
There are three different line cap styles in Cairo.
Line caps
Figure: Square, round and butt caps
A line with a CAIRO_LINE_CAP_SQUARE cap will have a different size, than a line with a CAIRO_LINE_CAP_BUTT cap. If a line is width px wide, the line with a CAIRO_LINE_CAP_SQUARE cap will be exactly width px greater in size. width/2 px at the beginning and width/2 px at the end.
static void do_drawing(cairo_t *cr)
{
cairo_set_line_width(cr, 10);

cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
cairo_move_to(cr, 30, 50);
cairo_line_to(cr, 150, 50);
cairo_stroke(cr);

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

cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
cairo_move_to(cr, 30, 130);
cairo_line_to(cr, 150, 130);
cairo_stroke(cr);

cairo_set_line_width(cr, 1.5);

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

cairo_move_to(cr, 150, 40);
cairo_line_to(cr, 150, 140);
cairo_stroke(cr);

cairo_move_to(cr, 155, 40);
cairo_line_to(cr, 155, 140);
cairo_stroke(cr);
}
The example draws three lines with three different caps. It will also graphically demonstrate the differences is size of the lines.
cairo_set_line_width(cr, 10);
Our lines will be 10 px wide.
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); 
cairo_move_to(cr, 30, 90);
cairo_line_to(cr, 150, 90);
cairo_stroke(cr);
Here we draw a horizontal line with a CAIRO_LINE_CAP_ROUND cap.
cairo_set_line_width(cr, 1.5);

cairo_move_to(cr, 30, 40);
cairo_line_to(cr, 30, 140);
cairo_stroke(cr);
This is one of the three vertical lines used to demostrate the differences in size.
Line caps
Figure: Line caps

Line joins

The lines can be joined using three different join styles.
  • CAIRO_LINE_JOIN_MITER
  • CAIRO_LINE_JOIN_BEVEL
  • CAIRO_LINE_JOIN_ROUND
Bevel, Round, Miter line joins
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.
Line joins
Figure: Line joins
In this chapter, we did some basic drawing.