Problem displaying image with alpha channel in a transparent window



Hi,

I'm trying to write a program to display a picture with full
transparency in its own window (without border), using Composite
extension (ARGB colormap and visual).

I've written a couple of programs doing this using Xlib alone or with
libXrender.

Using GTK+ to do the same seems easy since programs like cairo-clock [1]
are able to draw window with non rectangular shape and full
transparency.

But after many hours, and many version of my GTK+ demonstration program,
I'm still wandering how to display a GdkPixBuf with alpha layer in a
GtkWindow without any background or artefact.

So here are my little demonstration programs

- test-gdkpixbuf.c : 

  This program does the following:
  - looks up the RGBA colormap with gdk_screen_get_rgba_colormap()
  - installs the colormap with gtk_widget_set_default_colormap()
  - creates a GdkPixbuf with gdk_pixbuf_new_from_file()
  - creates a GtkWindow
  - sets the widget as GTK_APP_PAINTABLE with  
    gtk_widget_set_app_paintable()
  - disables double buffering with gtk_widget_set_double_buffered()
  - in the realize signal handler, it removes any background pixmap 
    using gdk_window_set_back_pixmap()
  - in the expose event handler, it draws the GdkPixBuf using
    gdk_draw_pixbuf()

  At first, it seems to work, but in fact, as soon as you move the 
  picture using CTRL+Button 1, you will realize the problem: the 
  window's background is a snapshot of the root window (see post from 
  Matt Hoosier [2]), and is fixed for the window's lifetime.
  At least, the content of the picture is well composited with the   
  background, but it's not the full transparency I'm looking for.

  See:
  - test-gdkpixbuf.png

- test-gdkpixbuf-bg.patch:
  
  Apply the patch on top of test-gdkpixbuf.c to create 
  test-gdkpixbuf-background.c
  This patch set a pixmap as a background, and draw nothing on the 
  window. Only the realize signal and expose event handlers are 
  modified:
  - realize signal handler creates a new pixmap with gdk_pixmap_new(),
    writes the content of the GdkPixBuf with gdk_draw_pixbuf() and then
    install the background with gdk_window_set_back_pixmap().
  - expose event handler only call to gdk_window_clear()

  With this version, the background is really transparent, but many 
  artefacts are visibles: the background is corrupted.
  On some configuration, I'm even able to see some other pixmap content 
  as background, strange !

  See:
  - test-gdkpixbuf-background.png

Note: those were tested with Gtk+ 2.10 and Gtk+ 2.12.

So I'm stuck. Either I'm always getting a unwanted background (filled
with solid color or a root window snapshot) or I'm getting a corrupted
image.

I've tried to: 
- use a GtkImage widget made from the GdkPixBuf
- use a GtkDrawingArea in the main window and draw into it
- use gdk_draw_rgb_32_image() instead of gdk_draw_pixbuf()
- paint with gdk_cairo_set_source_pixbuf()
I'm always getting an image composited with the window background.

Could someone provide me a link, a tips, or something to help me.
Thanks.

[1] http://macslow.thepimp.net/?page_id=23
[2] http://mail.gnome.org/archives/gtk-list/2006-October/msg00246.html

-- 
Yann Droneaud <ydroneaud mandriva com>

Attachment: sample.png
Description: PNG image

/* test-gdkpixbuf.c - draw a RGBA image in a window
 *
 * Yann Droneaud <ydroneaud mandriva com>
 * 
 */

#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>

#include <gdk-pixbuf/gdk-pixbuf.h>

static void
realize_signal(GtkWidget *widget,
	       gpointer   data)
{
  gdk_window_set_back_pixmap(widget->window, NULL, FALSE);

  return;
}

static void
destroy_signal(GtkWidget *widget,
	       gpointer   data)
{
  gtk_main_quit();
  
  return;
}

static gboolean
button_press_event(GtkWidget *widget,
		   GdkEventButton *event,
		   gpointer data)
{
  if (event->button == 1 && (event->state & GDK_CONTROL_MASK)) {

    gtk_window_begin_move_drag(GTK_WINDOW(widget),
			       (gint) event->button,
			       (gint) event->x_root, 
			       (gint) event->y_root,
			       event->time);

    return TRUE;
  }

  return FALSE;
}

static gboolean
key_press_event(GtkWidget *widget,
		GdkEventKey *event,
		gpointer data)
{
  /* handle move if mouse button is pressed and later ctrl is hit */
  if ((event->keyval == GDK_Control_L ||
       event->keyval == GDK_Control_R) && 
      (event->state & GDK_BUTTON1_MASK)) {

    gint x;
    gint y;
    
    gdk_display_get_pointer(gdk_display_get_default(), 
			    NULL, /* screen */
			    &x,
			    &y, 
			    NULL); /* state */

    gtk_window_begin_move_drag(GTK_WINDOW(widget),
			       (gint) 1, /* first button, see mask */
			       x, 
			       y,
			       event->time);

    return TRUE;
  }

  if (event->keyval == GDK_Escape) {
    gtk_main_quit();

    return TRUE;
  }

  return FALSE; /* event not handled */
}

static gboolean
expose_event(GtkWidget *widget, 
	     GdkEventExpose *event,
	     gpointer data)
{
  GdkPixbuf *pixbuf = (GdkPixbuf *) data;

  gdk_window_clear(widget->window);

  /* draw the whole pixbuf, regardless to the region to expose */
  gdk_draw_pixbuf(widget->window, 
		  widget->style->fg_gc[GTK_STATE_NORMAL], /* see demos/testpixbuf.c */
		  pixbuf,
		  0, 0,  /* src (x, y) */
		  0, 0,  /* dst (x, y) */
		  gdk_pixbuf_get_width(pixbuf),
		  gdk_pixbuf_get_height(pixbuf),
		  GDK_RGB_DITHER_NORMAL,
		  0, 0); /* dither offset (x, y) */

  return TRUE;
}

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

  GtkWidget *window;

  GdkColormap *colormap; 

  const char *color_name = NULL;
  GdkColor color;

  const char *pixbuf_name = NULL;
  GdkPixbuf *pixbuf;

  GError *error = NULL;

  gtk_init (&argc, &argv);

  if (argc > 1 && argv[1] != NULL && *argv[1] != '\0') {
    pixbuf_name = argv[1];
  }

  if (argc > 2 && argv[2] != NULL && *argv[2] != '\0') {
    color_name = argv[2];
  }

  if (pixbuf_name == NULL) {
    g_printerr("missing parameter\n");
    return 1;
  }

  /* get RGBA colormap */
  screen = gdk_screen_get_default();
  colormap = gdk_screen_get_rgba_colormap(screen);
  if (colormap == NULL) {
    g_printerr("no RGBA colormap\n");
    return 1;
  }

  /* use the RGBA colormap */
  gtk_widget_set_default_colormap(colormap);
  
  /* load the pixbuf */
  pixbuf = gdk_pixbuf_new_from_file(argv[1], &error);
  if (pixbuf == NULL) {
    g_printerr("can't load pixbuf\n");
    return 1;
  }
  
  /* check pixbuf format */
  if (gdk_pixbuf_get_has_alpha(pixbuf) != TRUE ||
      gdk_pixbuf_get_n_channels(pixbuf) != 4 ||
      gdk_pixbuf_get_bits_per_sample(pixbuf) != 8) {
    g_printerr("incorrect pixbuf format !\n");
    return 1;
  }

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_set_double_buffered(window, FALSE);

  /* unneeded, see gtk_widget_set_default_colormap() */  
  gtk_widget_set_colormap(window, colormap);

  gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
  gtk_window_set_resizable(GTK_WINDOW(window), FALSE);

  gtk_window_set_title(GTK_WINDOW(window), "simple");

  gtk_widget_set_size_request(window,
			      gdk_pixbuf_get_width(pixbuf),
			      gdk_pixbuf_get_height(pixbuf));

  if (color_name != NULL) {
    gdk_color_parse(color_name, &color);
    gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &color);
  }

  g_signal_connect_after(G_OBJECT(window), "realize",
			 G_CALLBACK(realize_signal), NULL);

  g_signal_connect(G_OBJECT(window), "destroy",
		   G_CALLBACK(destroy_signal), NULL);

  g_signal_connect(G_OBJECT(window), "key_press_event",
		   G_CALLBACK(key_press_event), NULL);

  g_signal_connect(G_OBJECT(window), "button_press_event",
		   G_CALLBACK(button_press_event), NULL);

  g_signal_connect(G_OBJECT(window), "expose_event",  
		   G_CALLBACK(expose_event), pixbuf);

  gtk_widget_add_events(window,
			GDK_EXPOSURE_MASK
			| GDK_BUTTON_PRESS_MASK
			| GDK_KEY_PRESS_MASK);

  gtk_widget_show(window);
  
  gtk_main ();
  
  return 0;
}

Attachment: test-gdkpixbuf.png
Description: PNG image

--- test-gdkpixbuf.c	2008-07-22 18:07:36.000000000 +0200
+++ test-gdkpixbuf-background.c	2008-07-22 18:09:07.000000000 +0200
@@ -1,4 +1,4 @@
-/* test-gdkpixbuf.c - draw a RGBA image in a window
+/* test-gdkpixbuf-background.c - draw a RGBA image in a window's background
  *
  * Yann Droneaud <ydroneaud mandriva com>
  * 
@@ -14,7 +14,25 @@ static void
 realize_signal(GtkWidget *widget,
 	       gpointer   data)
 {
-  gdk_window_set_back_pixmap(widget->window, NULL, FALSE);
+  GdkPixmap *pixmap;
+  GdkPixbuf *pixbuf = (GdkPixbuf *) data;
+
+  pixmap = gdk_pixmap_new(widget->window,
+			  gdk_pixbuf_get_width(pixbuf),
+			  gdk_pixbuf_get_height(pixbuf),
+			  -1); /* from drawable */
+  
+  gdk_draw_pixbuf(pixmap,
+		  widget->style->bg_gc[GTK_STATE_NORMAL],
+		  pixbuf,
+		  0, 0,  /* src (x, y) */
+		  0, 0,  /* dst (x, y) */
+		  gdk_pixbuf_get_width(pixbuf),
+		  gdk_pixbuf_get_height(pixbuf),
+		  GDK_RGB_DITHER_NORMAL,
+		  0, 0); /* dither offset (x, y) */
+
+  gdk_window_set_back_pixmap(widget->window, pixmap, FALSE);
 
   return;
 }
@@ -89,21 +107,8 @@ expose_event(GtkWidget *widget, 
 	     GdkEventExpose *event,
 	     gpointer data)
 {
-  GdkPixbuf *pixbuf = (GdkPixbuf *) data;
-
   gdk_window_clear(widget->window);
 
-  /* draw the whole pixbuf, regardless to the region to expose */
-  gdk_draw_pixbuf(widget->window, 
-		  widget->style->fg_gc[GTK_STATE_NORMAL], /* see demos/testpixbuf.c */
-		  pixbuf,
-		  0, 0,  /* src (x, y) */
-		  0, 0,  /* dst (x, y) */
-		  gdk_pixbuf_get_width(pixbuf),
-		  gdk_pixbuf_get_height(pixbuf),
-		  GDK_RGB_DITHER_NORMAL,
-		  0, 0); /* dither offset (x, y) */
-
   return TRUE;
 }
 
@@ -189,7 +194,7 @@ main(int   argc,
   }
 
   g_signal_connect_after(G_OBJECT(window), "realize",
-			 G_CALLBACK(realize_signal), NULL);
+			 G_CALLBACK(realize_signal), pixbuf);
 
   g_signal_connect(G_OBJECT(window), "destroy",
 		   G_CALLBACK(destroy_signal), NULL);
@@ -201,7 +206,7 @@ main(int   argc,
 		   G_CALLBACK(button_press_event), NULL);
 
   g_signal_connect(G_OBJECT(window), "expose_event",  
-		   G_CALLBACK(expose_event), pixbuf);
+		   G_CALLBACK(expose_event), NULL);
 
   gtk_widget_add_events(window,
 			GDK_EXPOSURE_MASK

Attachment: test-gdkpixbuf-background.png
Description: PNG image



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]