Re: Problem displaying image with alpha channel in a transparent window

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

 



Le mercredi 23 juillet 2008 à 10:38 +0200, Yann Droneaud a écrit :
> 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).
> 

Using Cairo correctly (e.g. using operator CAIRO_OPERATOR_SOURCE) fixes
my problems, see below.

> 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()
> 

Problem:

- gdk_draw_pixbuf() seems to use Cairo with operator CAIRO_OPERATOR_OVER
  so it composes the GdkPixbuf with the background of the window (or 
  the content of the root window, if it has no background).

Two way to fix:

Fill the window with a black, full transparency content before 
calling gdk_draw_pixbuf(): in expose_event():

    ...  
    cairo_t *cr = gdk_cairo_create(widget->window);
    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
    cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
    cairo_paint(cr);
    cairo_destroy(cr);
 
    gdk_draw_pixbuf(widget->window,
    ...

Or, better, use gdk_cairo_set_source_pixbuf() instead of 
gdk_draw_pixbuf():

    ...
    cairo_t *cr = gdk_cairo_create(widget->window);
    gdk_cairo_region(cr, event->region);    
    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
    
    gdk_cairo_set_source_pixbuf(cr, pixbuf, 0.0, 0.0);
    cairo_paint(cr);
    cairo_destroy(cr);
    ...

Here, I was using CAIRO_OPERATOR_OVER in my first tests, so the 
results were the same than with gdk_draw_pixbuf(). My mistake.

Fixed program is test-gdkpixbuf-cairo.c

> - 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()
> 

Problems:
- gdk_create_pixmap() returns a GdkPixmap with undefined content
- gdk_draw_pixbuf() composes the GdkPixbuf's content with the 
     undefined content of the GdkPixmap

  See my post[1] titled "There's something in my pixmap".

Same fixes two can be applied here: either initialize the content of the
drawable before calling gdk_draw_pixbuf(), either use only cairo to 
draw the GdkPixbuf with the correct operator: CAIRO_OPERATOR_SOURCE.

Remark: I'm still interested in a version using only GDK drawing
functions and no Cairo functions.

Regards.

[1] http://mail.gnome.org/archives/gtk-list/2008-July/msg00107.html

-- 
Yann Droneaud <ydroneaud@xxxxxxxxxxxx>

/* test-gdkpixbuf-cairo.c - draw a RGBA image in a window using Cairo
 *
 * Yann Droneaud <ydroneaud@xxxxxxxxxxxx>
 * 
 */

#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;

  cr = gdk_cairo_create(widget->window);

  gdk_cairo_region(cr, event->region);    

  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);

  gdk_cairo_set_source_pixbuf(cr, pixbuf, 0.0, 0.0);

  cairo_paint(cr);

  cairo_destroy(cr);

  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;
}
_______________________________________________
gtk-list mailing list
gtk-list@xxxxxxxxx
http://mail.gnome.org/mailman/listinfo/gtk-list

[Index of Archives]     [Touch Screen Library]     [GIMP Users]     [Gnome]     [KDE]     [Yosemite News]     [Steve's Art]

  Powered by Linux