Is there an API to do per-pixel Alpha Blend on GTK

2019-04-15 09:06发布

问题:

I want to draw transparent windows (with alpha channel) on Linux (GTK) and OSX. Is there an API to do that ? Note that I don't want to set a global transparency, the alpha level should be set per pixel.

I'm looking for the same kind of API than the UpdateLayeredWindow function on Windows, as in this example: Per Pixel Alpha Blend.

回答1:

For Mac OS X, see the RoundTransparentWindow sample code. It works by using a custom completely transparent window and drawing shapes in it. Although the example only uses shapes with hard edges + overall alpha, arbitrary alpha can be used.

Although the example uses a custom window, you can use the same technique to punch holes in normal windows by calling setOpaque:NO. Hacky example:

@implementation ClearView

- (void)drawRect:(NSRect)rect
{
    if (mask == nil)  mask = [[NSImage imageNamed:@"mask"] retain];
    [self.window setOpaque:NO];
    [mask drawInRect:self.bounds
            fromRect:(NSRect){{0, 0},mask.size}
           operation:NSCompositeCopy
            fraction:1.0];
}

@end

The primary limitation of this technique is that the standard drop shadow doesn’t interact very well with alpha-blended edges.



回答2:

I found this code in my experiments folder from earlier this year. I can't remember how much of this I wrote and how much is based on examples from elsewhere in the Internet.

This example will display a partially-transparent blue window with a fully opaque GTK+ button in the centre. Drawing, for example, an alpha-blended PNG somewhere inside the window should result in it being composited correctly. Hopefully this will put you on the right track.

Compile it with the following:

$ gcc `pkg-config --cflags --libs gtk+-2.0` -o per-pixel-opacity per-pixel-opacity.c

Now for the code:

#include <gtk/gtk.h>

static gboolean on_window_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
    cairo_t *cr;
    cr = gdk_cairo_create(widget->window); // create cairo context

    cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.2);
    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // set drawing compositing operator
                                                   // SOURCE -> replace destination
    cairo_paint(cr); // paint source

    cairo_destroy(cr);

    return FALSE;
}

gint main(gint argc, gchar **argv)
{
    GtkWidget *window, *button, *vbox;
    GdkScreen *screen;
    GdkColormap *colormap;

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    g_signal_connect(G_OBJECT(window), "delete-event", gtk_main_quit, NULL);
    g_signal_connect(G_OBJECT(window), "expose-event", G_CALLBACK(on_window_expose_event), NULL);

    gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
    gtk_container_set_border_width(GTK_CONTAINER(window), 20);

    gtk_widget_set_app_paintable(window, TRUE);

    screen = gtk_widget_get_screen(window);
    colormap = gdk_screen_get_rgba_colormap(screen);
    gtk_widget_set_colormap(window, colormap);

    button = gtk_button_new();
    gtk_button_set_label(GTK_BUTTON(button), "Don't Press!");

    gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(button));

    gtk_widget_show_all(window);

    gtk_main();

    return 0;
}