5. Shapes and fills

In this part of the Cairo tutorial, we will create some basic and more advanced shapes. We will fill them with solid colors, patterns and gradients.

Basic shapes

The Cairo API has several functions to create basic shpaes.

basicshapes.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
#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>

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

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

  GtkWidget *window;
  GtkWidget *darea;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  /*
    In this exmaple, we do not draw directly on the window but on the drawing area.
  */
  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER(window), darea);

  g_signal_connect(darea, "expose-event", G_CALLBACK(on_expose_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), 600, 480);

  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, 0, 0);
  cairo_set_line_width(cr, 2);

  /* create a rectangle */
  cairo_rectangle(cr, 20, 20, 120, 80);
  
  /* create a square */
  /*
    The cairo_rectangle() is used to create both squares and rectangles.
    A square is just a specific type of a rectangle.
  */
  cairo_rectangle(cr, 180, 20, 80, 80);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 0, 0);
  cairo_fill(cr);

  /* create a circle */
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_arc(cr, 330, 60, 40, 0, 2*M_PI);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 0, 1, 0);
  cairo_fill(cr);

  /* create an arc */
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_arc(cr, 90, 160, 40, M_PI/4, M_PI);
  cairo_close_path(cr);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 0, 0, 1);
  cairo_fill(cr);

  /* create an ellipse */
  cairo_set_source_rgb(cr, 1, 1, 1);
  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_set_source_rgb(cr, 0.2, 0.3, 0.4);
  cairo_fill(cr);

  cairo_destroy(cr);

  return FALSE;
}

basicshapes.c の実行結果は:

_images/basicshapes2.png

Other shapes can be created using a combination of basic primitives.

basicprimitives.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
91
92
93
94
95
96
97
#include <cairo.h>
#include <gtk/gtk.h>

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_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data);

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

  GtkWidget *window;
  GtkWidget *darea;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  /*
    In this exmaple, we do not draw directly on the window but on the drawing area.
  */
  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER(window), darea);

  g_signal_connect(darea, "expose-event", G_CALLBACK(on_expose_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), 600, 480);

  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, 0, 0);
  cairo_set_line_width(cr, 2);

  gint i;
  /* 10 points for star shape */
  for(i = 0; i < 10; i++){
    cairo_line_to(cr, points[i][0], points[i][1]);
  }

  /*
    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_close_path(cr);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 0, 0);
  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_set_source_rgb(cr, 0, 0, 0);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_fill(cr);

  /*
    The modified rectangle is a simple combination of two lines and one curve.
  */
  cairo_move_to(cr, 380, 40);
  cairo_line_to(cr, 380, 160);
  cairo_line_to(cr, 450, 160);
  cairo_curve_to(cr, 220, 210, 380, 145, 380, 40);

  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 0, 1, 0);
  cairo_fill(cr);
  
  cairo_destroy(cr);

  return FALSE;
}

basicprimitives.c の実行結果は:

_images/basicprimitives2.png

Fills

Fills fill the interiors of shapes.

Fills can be:

1. solid colors
2. patterns
3. gradients

Solid colors

A color 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.

fillsolid.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
#include <cairo.h>
#include <gtk/gtk.h>

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

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

  GtkWidget *window;
  GtkWidget *darea;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  /*
    In this exmaple, we do not draw directly on the window but on the drawing area.
  */
  darea = gtk_drawing_area_new();
  gtk_container_add(GTK_CONTAINER(window), darea);

  g_signal_connect(darea, "expose-event", G_CALLBACK(on_expose_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), 600, 480);

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

  int width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);

  /*
    The cairo_set_source_rgb() function call sets the source to an opaque color.
    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.
   */
  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, 0.3, 0.0);
  cairo_rectangle(cr, 20, 140, 100, 100);
  cairo_fill(cr);

  cairo_set_source_rgb(cr, 1.0, 0.0, 0.5);
  cairo_rectangle(cr, 150, 140, 100, 100);
  cairo_fill(cr);
  
  cairo_destroy(cr);

  return FALSE;
}

fillsolid.c の実行結果は:

_images/fillsolid2.png

Patterns

Patterns are complex graphical objects that can fill the shapes.

fillpatterns.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
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <cairo.h>
#include <gtk/gtk.h>

cairo_surface_t *surface1;
cairo_surface_t *surface2;
cairo_surface_t *surface3;
cairo_surface_t *surface4;

static void create_surfaces();
static void destroy_surfaces();
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);

  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_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);

  gtk_main();

  destroy_surfaces();

  return 0;
}

static void create_surfaces(){
  surface1 = cairo_image_surface_create_from_png("pt/pt1.png");
  surface2 = cairo_image_surface_create_from_png("pt/pt2.png");
  surface3 = cairo_image_surface_create_from_png("pt/pt3.png");
  surface4 = cairo_image_surface_create_from_png("pt/pt4.png");
}

static void destroy_surfaces(){
  g_print("destroying surfaces");
  cairo_surface_destroy(surface1);
  cairo_surface_destroy(surface2);
  cairo_surface_destroy(surface3);
  cairo_surface_destroy(surface4);

}

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

  cairo_pattern_t *pattern1;
  cairo_pattern_t *pattern2;
  cairo_pattern_t *pattern3;
  cairo_pattern_t *pattern4;

  cr = gdk_cairo_create(widget->window);

  int width, height;
  gtk_window_get_size(GTK_WINDOW(widget), &width, &height);

  /*
    we create a pattern from the surface by calling the cairo_pattern_create_for_surface() function
  */
  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);

  /* for pt1.png */
  /*
    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.
  */
  cairo_set_source(cr, pattern1);
  cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_NONE);
  cairo_rectangle(cr, 20, 20, 100, 100);
  cairo_fill(cr);

  /* for pt2.png */
  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);

  /* for pt3.png */
  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);

  /* for pt4.png */
  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);
  
  cairo_destroy(cr);

  return FALSE;
}

fillpatterns.c の実行結果は:

_images/fillpatterns2.png

Gradients

In computer graphics, gradient is a smooth blending of shapes from light to dark or from one color to another, In 2D drawing programs and paint programs, gradients are used to create colorful backgrounds and special effects as well as to simulate lights ans shadows.

fillgradients.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
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
#include <cairo.h>
#include <gtk/gtk.h>

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), 340, 390);
  gtk_window_set_title(GTK_WINDOW(window), "gradients");

  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;

  cairo_pattern_t *pat1;
  cairo_pattern_t *pat2;
  cairo_pattern_t *pat3;

  cr = gdk_cairo_create(widget->window);

  /*
    in our example, we draw three rectangles with three different gradients.
  */

  /* for 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);

  /*for pat2 */
  pat2 = cairo_pattern_create_linear(0.0, 0.0, 350.0, 0.0);

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

  /* for pat3 */
  /*
    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.
   */
  pat3 = cairo_pattern_create_linear(20.0, 260.0, 20.0, 360.0);

  /*
    we define color stops to produce our gradient pattern. in this case, the gradient is a blending of black and yellow colors.
    by adding two black and one yellow stops, we create a horizontal gradient pattern.
    what these stops actually mean? in our case, we beging with black color, 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 color shops at 9/10 of the size, where we begin painting in black again, until the end.
  */
  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(pat1);
  cairo_pattern_destroy(pat2);
  cairo_pattern_destroy(pat3);

  cairo_destroy(cr);
  
  return FALSE;
}

fillgradients.c の実行結果は:

_images/fillgradients2.png

Table Of Contents

Previous topic

4. Basic drawing

Next topic

6. Transparency