[cairo] Problems with transparent background

Martin Fischer martinfischer8 at t-online.de
Wed Feb 22 18:30:13 UTC 2017


Hi,

I'm working on an CAD application that needs to be ported to GTK3. For 
that purpose I need to find out the best way to handle dragging drawing 
elements from one position to another.

My idea is to create a surface that has all the static elemants. Another 
surface should have a transparent background and the element that is 
currently moved.

To try out that concept I created a small application:

----
/*
  * gcc raster2.c -o raster2 `pkg-config --cflags --libs gtk+-2.0`
  */

#include <gtk/gtk.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>

struct {
   cairo_surface_t *image;
   cairo_surface_t *ball;
} glob;


double px = 10;
double py = 100;
double vx = 2;
double vy = 2;


/* create a bunch of green lines on white as background */

void CreateBackground(cairo_surface_t *destination )
{
	cairo_t *cr;
	int i;
	
	glob.image = cairo_surface_create_similar_image (destination,
                                     CAIRO_FORMAT_ARGB32,
                                     200,
                                     200);


     cr = cairo_create( glob.image );

     cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
     cairo_paint(cr);

     cairo_set_source_rgba(cr,0,1.0,0, 1.0);

     for( i = 0; i <= 200; i+=10 ) {
		cairo_move_to( cr, i, 0 );
		cairo_line_to( cr, i, 200 );
		cairo_move_to( cr, 0, i );
		cairo_line_to(cr, 200, i );
	}
     cairo_stroke(cr);
	cairo_destroy(cr);
}

/* create a transparent background and a red dot */

void CreateForeground()
{
	cairo_t *cr;

     cr = cairo_create( glob.ball );

     /* why has alpha to be greater than 0.0? */
	cairo_set_source_rgba (cr, 0, 0, 0, 0.01);
	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
	cairo_paint (cr);
	
     cairo_set_source_rgba(cr,1.0,0,0, 1);

     cairo_arc(cr, px, py, 6, 0, 2*M_PI);
     cairo_fill(cr);

     cairo_destroy(cr);

}

/* when called, create a new foreground surface and invalidate old
  * position. Finally calculate the next position */

gboolean timeout(gpointer data)
{
     GtkWidget *widget = GTK_WIDGET(data);
     GdkRectangle rect;
     if (!widget->window) return TRUE;

	CreateForeground();

	rect.x = px - 3;
     rect.y = py - 3;
     rect.width = 6;
     rect.height = 6;		
     gdk_window_invalidate_rect( widget->window, &rect, FALSE );
     gtk_widget_queue_draw(widget);

     if (px <= 3 || px >= 200-3) vx = -vx ;
     if(py <= 3 || py >= 200-3 ) vy = -vy;
     px += vx;
     py += vy;

     return TRUE;
}

/* draw the animation:
  * 1. clip to the changed area
  * 2. draw the background
  * 3. draw the surface containing the red dot on top
  */

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

     if( glob.image == NULL )
		CreateBackground( cairo_get_target (cr));
		
     cairo_rectangle(cr, event->area.x, event->area.y, 
event->area.width, event->area.height);
     cairo_clip(cr);
     cairo_set_source_surface(cr, glob.image, 0, 0);
     cairo_paint(cr);

	cairo_set_operator( cr, CAIRO_OPERATOR_OVER );
	cairo_set_source_surface( cr, glob.ball, 0, 0 );
	cairo_paint(cr);
	
	cairo_destroy( cr );

     return FALSE;
}

int main(int argc, char *argv[])
{
     char *title = "Test";
     int sx = 200;
     int sy = 200;

	gtk_init(NULL,NULL);
		
	glob.ball = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA,
                                 NULL);

     GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
     gtk_window_set_title(GTK_WINDOW(window),title);
     gtk_container_set_border_width(GTK_CONTAINER (window), 2);

     g_signal_connect(window, "destroy",G_CALLBACK(gtk_main_quit),&window);
     GtkWidget *drawing_area = gtk_drawing_area_new();

	if( drawing_area ) {
	    GdkScreen *screen = gtk_widget_get_screen(drawing_area);
		GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);

		if( colormap == NULL ) {
			printf("Screen does not support alpha!\n");
		}
	}	

     gtk_widget_set_size_request(drawing_area, sx, sy);

     g_signal_connect(drawing_area,"expose_event",G_CALLBACK(expose),NULL);

     gtk_container_add(GTK_CONTAINER(window), drawing_area);
     gtk_widget_show(drawing_area);

     g_timeout_add(10, timeout, window);

     if (!GTK_WIDGET_VISIBLE (window))
         gtk_widget_show_all(window);
     else {
         gtk_widget_destroy (window);
         window = NULL;
     }

     gtk_main();

     cairo_surface_destroy(glob.image);
	cairo_surface_destroy(glob.ball);
}
----

The expected result is a small window with e few green lines and a red 
circle moving across it.

This mostly works with one exception. The alpha value for the 
transparent background has to be set to a value slightly larger than the 
expected 0.0! (See CreateForeground()). If I set it to 0.0 I get some 
strange flickering but no correct rendering.

What am I doing wrong? Any hints and tips welcome.

Regards,
Martin


More information about the cairo mailing list