9. Transformations

In this part of the Cairo graphics programming tutorial, we will talk about transformations:

An affine transform
rotation
translation
shear

Translation

translation.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <cairo.h>
#include <gtk/gtk.h>

/*
  The following example describes a simple translation.
*/

/*
  The example draws a rectangle. Then we do a translation and draw the same rectangle again.
*/

static gboolean on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data);

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

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  g_signal_connect(G_OBJECT(window), "expose-event", G_CALLBACK(on_expose_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, 230);
  
  gtk_window_set_title(GTK_WINDOW(window), "translation");

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

  gtk_main();

  return 0;
}

static gboolean on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data){
  cairo_t *cr;
  
  cr = gdk_cairo_create(widget->window);

  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 20, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);

  /*
    The cairo_translate() function modifies the current transformation matrix by translating the use space origin.
    In our case we shift the origin by 100 units in both directions.
  */
  cairo_translate(cr, 100, 100);

  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 20, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);

  cairo_destroy(cr);
  
  return FALSE;
}

translation.c の実行結果は:

_images/translation1.png

Rotation

The next example demonstrates a rotation.

rotation.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>

/*
  The example draws a rectangle, performs a translation and a rotation and draws the same rectangle again.
 */

static gboolean on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data);

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

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  g_signal_connect(G_OBJECT(window), "expose-event", G_CALLBACK(on_expose_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, 230);
  
  gtk_window_set_title(GTK_WINDOW(window), "rotation");

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

  gtk_main();

  return 0;
}

static gboolean on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data){
  cairo_t *cr;
  
  cr = gdk_cairo_create(widget->window);

  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 20, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);

  /*
    First we shift the user space origin. Then we rotate it by 180°.
    Notice that we specify the angle in radians, not degrees.
    2*M_PI = 360°.
   */
  cairo_translate(cr, 150, 100);
  cairo_rotate(cr, M_PI/2);

  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 20, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);

  cairo_destroy(cr);
  
  return FALSE;
}

rotation.c の実行結果は:

_images/rotation1.png

Scale

The next exmaple demonstrates a scaling of an object.

scale.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <cairo.h>
#include <gtk/gtk.h>

/*
  This time the example makes the initial rectangle smaller and then bigger by a specific scale factor.
*/

static gboolean on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data);

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

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  g_signal_connect(G_OBJECT(window), "expose-event", G_CALLBACK(on_expose_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), 360, 140);
  
  gtk_window_set_title(GTK_WINDOW(window), "scale");

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

  gtk_main();

  return 0;
}

static gboolean on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data){
  cairo_t *cr;
  
  cr = gdk_cairo_create(widget->window);

  /*
    We want to perform two scaling operations on the initial rectangle.
    Therefore, we need to save the initial transformation matrix.
    This is done by a pair of cairo_save() and cairo_restore() functions.
  */
  cairo_save(cr);
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 30, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);

  cairo_save(cr);
  /*
    Here we shift the user space origin an scale it by a factor of 0.7.
  */
  cairo_translate(cr, 130, 30);
  cairo_scale(cr, 0.7, 0.7);

  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 0, 0, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);

  cairo_save(cr);
  cairo_translate(cr, 220, 30);
  cairo_scale(cr, 1.5, 1.5);

  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 0, 0, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);

  cairo_destroy(cr);
  
  return FALSE;
}

scale.c の実行結果は:

_images/scale1.png

Shear

In the following examples we perform shearing.

shear.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <cairo.h>
#include <gtk/gtk.h>

/*
  In this code example, we perform two shear transformations. For a shear transformation, we do not have a special function.
  We must use matrices.
*/

static gboolean on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data);

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

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  g_signal_connect(G_OBJECT(window), "expose-event", G_CALLBACK(on_expose_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), 360, 140);
  
  gtk_window_set_title(GTK_WINDOW(window), "shear");

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

  gtk_main();

  return 0;
}

static gboolean on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data){
  cairo_t *cr;
  /* The cairo_matrix_t is a structure that holds an affine transformation */
  cairo_matrix_t matrix;

  cr = gdk_cairo_create(widget->window);

  cairo_save(cr);
  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 20, 30, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);

  cairo_save(cr);
  cairo_translate(cr, 130, 30);

  /*
    This transformation shears y values by 0.5 of the x values.
  */
  cairo_matrix_init(&matrix,
		    1.0, 0.5,
		    0.0, 1.0,
		    0.0, 0.0);
  cairo_transform(cr, &matrix);

  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 0, 0, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);

  cairo_save(cr);
  cairo_translate(cr, 220, 30);

  /*
    This transformation multiplies the x value of each coordinate by 0.7 of the y.
  */
  cairo_matrix_init(&matrix,
		    1.0, 0.0,
		    0.7, 1.0,
		    0.0, 0.0);
  cairo_transform(cr, &matrix);

  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rectangle(cr, 0, 0, 80, 50);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);
  cairo_restore(cr);

  cairo_destroy(cr);
  
  return FALSE;
}

shear.c の実行結果は:

_images/shear1.png

Ellipses

In the following example we create an complex shape by

Table Of Contents

Previous topic

8. Clipping and Masking

Next topic

10. Text