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