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