Cairo FAQ Transparency

Материал из Wiki.crossplatform.ru

Версия от 08:57, 19 марта 2009; ViGOur (Обсуждение | вклад)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

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.

#include <cairo.h>
#include <gtk/gtk.h>
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  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);  
  }
 
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
 
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, 90); 
  gtk_window_set_title(GTK_WINDOW(window), "transparency");
 
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_show_all(window);
 
  gtk_main();
 
  return 0;
}

The cairo_set_source_rgba() has an optional alpha parameter to provide transparency.

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

This code creates ten rectangles with alpha values from 0.1 ... 1.

center

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.

#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)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  static double alpha = 1;
  double const delta = 0.01;
 
  cairo_set_source_surface(cr, image, 10, 10);
  cairo_paint_with_alpha(cr, alpha);
 
  alpha -= delta; 
 
  if (alpha <= 0) timer = FALSE;
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
static gboolean
time_handler (GtkWidget *widget)
{
  if (widget->window == NULL) return FALSE;
 
  if (!timer) return FALSE;
 
  gtk_widget_queue_draw(widget);
  return TRUE;
}
 
int
main (int argc, char *argv[])
{
 
  GtkWidget *window;
  GtkWidget *darea;
 
  gint width, height;
 
  image = cairo_image_surface_create_from_png("turnacastle.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), "fade out");
  g_timeout_add(50, (GSourceFunc) time_handler, (gpointer) window);
  gtk_widget_show_all(window);
 
  gtk_main();
 
  cairo_surface_destroy(image);
 
  return 0;
}

In our example we display an image that will gradually fade out. We use a photo of ruins of a Turna Castle, located in east part of Slovakia.

 image = cairo_image_surface_create_from_png("turnacastle.png");

For efficiency reasons, the creation of the image surface is placed inside the main function.

 cairo_set_source_surface(cr, image, 10, 10);

Here we set the source for painting.

 cairo_paint_with_alpha(cr, alpha);

The fade out effect is created using the cairo_paint_with_alpha() function call. This function uses transparency value as a mask.

 alpha -= delta;

We decrease the alpha value each time, the on_expose_event() function is executed.

 if (alpha <= 0) timer = FALSE;

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.

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

We create a timer function. This function will call time_handler every 50 ms.

 static gboolean
 time_handler (GtkWidget *widget)
 {
   if (widget->window == NULL) return FALSE;
 
   if (!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.

 if (widget->window == NULL) return FALSE;

You might wonder, what this code line is there for. It is a simple check. If the timeout value is too small, like 5 ms, it happens that while closing the application, the window is already destroyed but still we get the timeout function executed. This code line prevents from manipulating with a window in such cases.

center

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 gushort count = 0;
 
static gboolean
on_expose_event(GtkWidget *widget,
    GdkEventExpose *event,
    gpointer data)
{
  cairo_t *cr;
 
  cr = gdk_cairo_create(widget->window);
 
  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++) {
      cairo_set_line_width(cr, 3);
      cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
      cairo_set_source_rgba(cr, 0, 0, 0, trs[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);
  }
 
  cairo_destroy(cr);
 
  return FALSE;
}
 
static gboolean
time_handler (GtkWidget *widget)
{
  count += 1;
  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);
 
  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);
  g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) 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[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.

center