Re: Problem in cairo + xcb where alpha blending is cumulative (becoming more opaque with each render)

Uli Schlachter <[email protected]> Thu, 27 Sep 2018 09:56:01 +0200
Newsgroups gmane.comp.lib.cairo
Message-ID <[email protected]>
Hi,

On 26.09.2018 07:51, Ryan Flannery wrote:
> The raw transparency piece works fine and is
> easy in cairo + xcb,
sorry to tell you this, but the problem is in the raw transparency part.
You only *think* that you have transparency. Also, "is easy" is the
total opposite of the truth. Again, sorry.

When a new window is mapped, its content is undefined. In practice this
means that the window contains whatever was visible at this position
before. Thus, you think you have transparency when you draw ontop of
this. However, this is wrong.

Try apply the attached patch move.patch. What this patch does is to wait
for the window to become visible and then it moves the window. You will
notice that together with the window, its content is moved. Thus, making
it obvious that your transparency does not work.

The black background that you see is the working case *if your window
had an alpha channel*. Because it does not have an alpha channel, you
only see black.

To create a transparent window in X11, first of all, the user has to be
using a compositing manager (compiz, compton, xcompmgr, ...). Without
this, there is no real transparency. The _NET_WM_CMs selection can be
used to figure out if a compositing manager is running.

Next, you need to create a window with a 32bit visual (and also tell
cairo that your window is using this visual). For this part, I refer you to:

https://stackoverflow.com/questions/3645632/how-to-create-a-window-with-a-bit-depth-of-32

The attached patch fix.patch gives me a window with either a black
background (if no compositing manager is running) or a transparent black
background (if a compositing manager is running; I used compton).

Cheers,
Uli
-- 
Sent from my Game Boy.

-- 
cairo mailing list
[email protected]
https://lists.cairographics.org/mailman/listinfo/cairo
move.patch (text/x-diff, 3 KB)
--- cairo_example.c.orig	2018-09-27 09:37:28.775992348 +0200
+++ cairo_example.c	2018-09-27 09:54:32.519404799 +0200
@@ -119,6 +119,8 @@ main()
    xcb_map_window(xcon, xwindow);
    cairo_clear_background();
    xcb_flush(xcon);
+   sleep(1);
+   xcb_configure_window(xcon, xwindow, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, (uint32_t[]) { 300, 300 });
 
    /* begin main draw loop */
    for (int i = 0; i < 11; i++) {
@@ -137,9 +139,9 @@ main()
 
       /* END: pop group/buffer (exposing it) and render */
       cairo_pop_group_to_source(cairo);
-      /*cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); XXX Makes the background black (?) */
+      cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
       cairo_paint(cairo);
-      /*cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); only done if th eprevious cairo_set_operator() is done */
+      cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
       xcb_flush(xcon);
       sleep(1);
    }
@@ -170,12 +172,12 @@ get_xvisual(xcb_screen_t *screen)
 {
    xcb_depth_iterator_t i = xcb_screen_allowed_depths_iterator(screen);
    for (; i.rem; xcb_depth_next(&i)) {
+     if (i.data->depth != 32)
+       continue;
       xcb_visualtype_iterator_t vi;
       vi = xcb_depth_visuals_iterator(i.data);
       for (; vi.rem; xcb_visualtype_next(&vi)) {
-         if (screen->root_visual == vi.data->visual_id) {
-            return vi.data;
-         }
+        return vi.data;
       }
    }
 
@@ -256,16 +258,13 @@ wm_hints()
          xatoms[NET_WM_STRUT_PARTIAL], XCB_ATOM_CARDINAL, 32, 12, struts);
 	xcb_change_property(xcon, XCB_PROP_MODE_REPLACE, xwindow,
          xatoms[NET_WM_STRUT], XCB_ATOM_CARDINAL, 32, 4, struts);
-
-   /* remove window from window manager tabbing */
-   const uint32_t val[] = { 1 };
-   xcb_change_window_attributes(xcon, xwindow,
-         XCB_CW_OVERRIDE_REDIRECT, val);
 }
 
 void
 setup_xcb()
 {
+   xcb_colormap_t colormap;
+
    xcon = xcb_connect(NULL, &default_screen);
    if (xcb_connection_has_error(xcon)) {
       xcb_disconnect(xcon);
@@ -278,23 +277,29 @@ setup_xcb()
    if (NULL == (xvisual = get_xvisual(xscreen)))
       errx(1, "Failed to retrieve X visual context");
 
-   static uint32_t valwin[2] = {
+   colormap = xcb_generate_id(xcon);
+   xcb_create_colormap(xcon, XCB_COLORMAP_ALLOC_NONE, colormap, xscreen->root, xvisual->visual_id);
+
+   uint32_t valwin[5] = {
       XCB_NONE,
-      XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS
+      0,
+      1,
+      XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS,
+      colormap
    };
 
    xwindow = xcb_generate_id(xcon);
    xcb_create_window(
          xcon,
-         XCB_COPY_FROM_PARENT,
+         32,
          xwindow,
          xscreen->root,
          x, y,
          y, h,
          1,    /* border width */
          XCB_WINDOW_CLASS_INPUT_OUTPUT,
-         xscreen->root_visual,
-         XCB_CW_EVENT_MASK | XCB_CW_BACK_PIXMAP,
+         xvisual->visual_id,
+         XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP,
          valwin);
 
    wm_hints();
fix.patch (text/x-diff, 3 KB)
--- cairo_example.c.orig	2018-09-27 09:37:28.775992348 +0200
+++ cairo_example.c	2018-09-27 09:54:32.519404799 +0200
@@ -119,6 +119,8 @@ main()
    xcb_map_window(xcon, xwindow);
    cairo_clear_background();
    xcb_flush(xcon);
+   sleep(1);
+   xcb_configure_window(xcon, xwindow, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, (uint32_t[]) { 300, 300 });
 
    /* begin main draw loop */
    for (int i = 0; i < 11; i++) {
@@ -137,9 +139,9 @@ main()
 
       /* END: pop group/buffer (exposing it) and render */
       cairo_pop_group_to_source(cairo);
-      /*cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); XXX Makes the background black (?) */
+      cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
       cairo_paint(cairo);
-      /*cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); only done if th eprevious cairo_set_operator() is done */
+      cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
       xcb_flush(xcon);
       sleep(1);
    }
@@ -170,12 +172,12 @@ get_xvisual(xcb_screen_t *screen)
 {
    xcb_depth_iterator_t i = xcb_screen_allowed_depths_iterator(screen);
    for (; i.rem; xcb_depth_next(&i)) {
+     if (i.data->depth != 32)
+       continue;
       xcb_visualtype_iterator_t vi;
       vi = xcb_depth_visuals_iterator(i.data);
       for (; vi.rem; xcb_visualtype_next(&vi)) {
-         if (screen->root_visual == vi.data->visual_id) {
-            return vi.data;
-         }
+        return vi.data;
       }
    }
 
@@ -256,16 +258,13 @@ wm_hints()
          xatoms[NET_WM_STRUT_PARTIAL], XCB_ATOM_CARDINAL, 32, 12, struts);
 	xcb_change_property(xcon, XCB_PROP_MODE_REPLACE, xwindow,
          xatoms[NET_WM_STRUT], XCB_ATOM_CARDINAL, 32, 4, struts);
-
-   /* remove window from window manager tabbing */
-   const uint32_t val[] = { 1 };
-   xcb_change_window_attributes(xcon, xwindow,
-         XCB_CW_OVERRIDE_REDIRECT, val);
 }
 
 void
 setup_xcb()
 {
+   xcb_colormap_t colormap;
+
    xcon = xcb_connect(NULL, &default_screen);
    if (xcb_connection_has_error(xcon)) {
       xcb_disconnect(xcon);
@@ -278,23 +277,29 @@ setup_xcb()
    if (NULL == (xvisual = get_xvisual(xscreen)))
       errx(1, "Failed to retrieve X visual context");
 
-   static uint32_t valwin[2] = {
+   colormap = xcb_generate_id(xcon);
+   xcb_create_colormap(xcon, XCB_COLORMAP_ALLOC_NONE, colormap, xscreen->root, xvisual->visual_id);
+
+   uint32_t valwin[5] = {
       XCB_NONE,
-      XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS
+      0,
+      1,
+      XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS,
+      colormap
    };
 
    xwindow = xcb_generate_id(xcon);
    xcb_create_window(
          xcon,
-         XCB_COPY_FROM_PARENT,
+         32,
          xwindow,
          xscreen->root,
          x, y,
          y, h,
          1,    /* border width */
          XCB_WINDOW_CLASS_INPUT_OUTPUT,
-         xscreen->root_visual,
-         XCB_CW_EVENT_MASK | XCB_CW_BACK_PIXMAP,
+         xvisual->visual_id,
+         XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP,
          valwin);
 
    wm_hints();