6. Transparency

In this part of the Cairo C API tutorial, we will talk about transparency.

We will provide basic definitions and two interesting transparency effects.

In computer graphics, we can achieve transparency effects using alpha compositing.

Tranparent Rectangles

The first example will draw ten rectangles with different levels of transparency.

transrect.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
#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), 590, 180);
  gtk_window_set_title(GTK_WINDOW(window), "transparency");

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

  gint i;

  /* transparent blue */
  for(i = 1; i <= 10; i++){
    /*
      the cairo_set_source_rgbs() has an optional alpha parameter to provide
      transparency.
    */
    cairo_set_source_rgba(cr, 0, 0, 1, i*0.1);
    cairo_rectangle(cr, 50*i, 20, 40, 40);
    cairo_fill(cr);
  }

  /* transparent red */
  for(i = 1; i <= 10; i++){
    cairo_set_source_rgba(cr, 1, 0, 0, i*0.1);
    cairo_rectangle(cr, 50*i, 70, 40, 40);
    cairo_fill(cr);
  }

  /* transparent green */
  for(i = 1; i <= 10; i++){
    cairo_set_source_rgba(cr, 0, 1, 0, i*0.1);
    cairo_rectangle(cr, 50*i, 120, 40, 40);
    cairo_fill(cr);
  }

  cairo_destroy(cr);
  
  return FALSE;
}

transrect.c の実行結果は:

_images/transrect2.png

Fade out effect

In the next example, we will fade out an image.

The image will gradually get more transparent until it is completely invisible.

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

cairo_surface_t *image;
gboolean timer = TRUE;

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

static gboolean time_handler(GtkWidget *widget);

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

  gint width, height;

  /*
    for efficiency reasons, the creation of the image surface is placed inside the main funcion
   */
  image = cairo_image_surface_create_from_png("aki.png");
  width = cairo_image_surface_get_width(image);
  height = cairo_image_surface_get_height(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(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), width+20, height+20);
  gtk_window_set_title(GTK_WINDOW(window), "fadeout");


  /*
    we create a timer function.
    this function will call time_handler every 50ms.
  */
  g_timeout_add(50, (GSourceFunc) time_handler, (gpointer) window);
  gtk_widget_show_all(window);

  gtk_main();

  cairo_surface_destroy(image);

  return 0;
}

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

  cr = gdk_cairo_create(widget->window);

  static double alpha = 1;
  double const delta = 0.01;

  /*
    here we set the source for painting
  */
  cairo_set_source_surface(cr, image, 10, 10);

  /*
    the fade out effect is created using the cairo_paint_with_alpha() function call.
    this function uses transparency values as a mask.
  */
  cairo_paint_with_alpha(cr, alpha);

  /*
    we decrease the alpha value each time, the on_expose_event() function is executed.
  */
  alpha -= delta;

  /*
    if the alpha value is less or equal to zero, we finished our fade out effect.
    we set the timer value to FALSE.
    we do not need our timer function anymore.
  */
  if(alpha <= 0){
    timer = FALSE;
  }

  cairo_destroy(cr);
  
  return FALSE;
}

/*
  the main function of the time_handler call is to redraw the window regularly.
  when the function return FALSE, the timeout function will cease to work.
 */
static gboolean time_handler(GtkWidget *widget){
  if(widget->window == NULL){
    return FALSE;
  }

  if(!timer){
    return FALSE;
  }

  gtk_widget_queue_draw(widget);

  return TRUE;
}

fadeout.c の実行結果は:

_images/fadeout1.png

Waiting demo

in this example, 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.

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

static gushort count = 0;

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

static gboolean time_handler(GtkWidget *widget);

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), 250, 150);
  
  gtk_window_set_title(GTK_WINDOW(window), "waiting demo");

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

  /*
    we use a timer function to create animation
  */
  g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) 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 draw eight lines with eight different alpha values
  */
  /*
    this is a two dimensional array of transparency values used in this demo.
    there are 8 row, each for one state.
    each of the 8 lines will continously use these 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 }
  };

  gint width, height;

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

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

  gint i = 0;
  for(i = 0; i < 8; i++){
    /*
      we make the lines a bit thicker, so that they are better visible.
      we draw the lines with round caps.
     */
    cairo_set_line_width(cr, 6);
    cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
    /*
      here we define the transparency value for a line
    */
    cairo_set_source_rgba(cr, 0, 0, 0, trs[count%8][i]);

    /*
      these code will draw each of the eight lines.
    */
    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);
  }

  cairo_destroy(cr);
  
  return FALSE;
}

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

waitdemo.c の実行結果は:

_images/waitdemo.png

Table Of Contents

Previous topic

5. Shapes and fills

Next topic

7. Compositing