[Spice-devel] [PATCH v2 spice-gtk 2/2] channel-cursor: mono cursors edge highlighting

Alon Levy alevy at redhat.com
Fri Oct 18 13:55:13 CEST 2013


Fix 998529, mono (invert) cursors not visible on a black background, by doing
simple edge detection on the cursor (this is done once when the cursor
is changed and then cached, cursors are 32x32 generally) and thus having
a cursor with contrast on both dark and light backgrounds.

When (if) GDK gets invert cursor support (wayland?) then we can just use
the cursor as is. Until then X doesn't provide any way I see of solving
this otherwise. The end result was tested with the I beam cursor that
the original bug was referring to (run putty on a windows 7 vm) and
looks ok to me.
---
 gtk/channel-cursor.c | 86 ++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 66 insertions(+), 20 deletions(-)

diff --git a/gtk/channel-cursor.c b/gtk/channel-cursor.c
index 66e91df..2c48d37 100644
--- a/gtk/channel-cursor.c
+++ b/gtk/channel-cursor.c
@@ -275,15 +275,67 @@ static void print_cursor(display_cursor *cursor, const guint8 *data)
 }
 #endif
 
+static bool cursor_xor_pixel(const guint8 *xor, int bpl, int x, int y)
+{
+    return (xor[bpl * y + (x / 8)] & (0x80 >> (x % 8))) > 0;
+}
+
+static guint8 cursor_is_xor_edge(display_cursor *cursor, const guint8 *xor, int bpl, int x, int y)
+{
+    if (x == 0 || x == cursor->hdr.width -1 || y == 0 || y == cursor->hdr.height - 1) {
+        return 0;
+    }
+#define P(x, y) cursor_xor_pixel(xor, bpl, x, y)
+    return !P(x, y) && (P(x - 1, y + 1) || P(x, y + 1) || P(x + 1, y + 1) ||
+                        P(x - 1, y)     ||                P(x + 1, y)     ||
+                        P(x - 1, y - 1) || P(x, y - 1) || P(x + 1, y - 1));
+#undef P
+}
+
+static bool cursor_is_and_ones(display_cursor *cursor, const guint8 *data)
+{
+    int x, y, bpl;
+
+    bpl = (cursor->hdr.width + 7) / 8;
+    for (y = 0 ; y < cursor->hdr.height; ++y) {
+        for (x = 0 ; x < cursor->hdr.width / 8; ++x) {
+            if (data[x] != 0xff) {
+                return false;
+            }
+        }
+        data += bpl;
+    }
+    return true;
+}
+
+/* Mono cursors have two places, "and" and "xor". If a bit is 1 in both, it
+ * means invertion of the corresponding pixel in the display. Since X11 (and
+ * gdk) doesn't do invertion, instead we do edge detection and turn the
+ * sorrounding edge pixels black, and the invert-me pixels white. To
+ * illustrate:
+ *
+ *  and   xor      dest RGB (1=0xffffff, 0=0x000000)
+ *
+ *                        dest alpha (1=0xff, 0=0x00)
+ *
+ * 11111 00000     00000  00000
+ * 11111 00000     00000  01110
+ * 11111 00100 =>  00100  01110
+ * 11111 00100     00100  01110
+ * 11111 00000     00000  01110
+ * 11111 00000     00000  00000
+ *
+ */
 static void mono_cursor(display_cursor *cursor, const guint8 *data)
 {
-    const guint8 *xor, *and;
+    const guint8 *xor, *xor_base, *and;
     guint8 *dest;
     int bpl, x, y, bit;
+    bool and_ones = cursor_is_and_ones(cursor, data);
 
     bpl = (cursor->hdr.width + 7) / 8;
     and = data;
-    xor = and + bpl * cursor->hdr.height;
+    xor_base = xor = and + bpl * cursor->hdr.height;
     dest  = (uint8_t *)cursor->data;
 #ifdef DEBUG_CURSOR
     print_cursor(cursor, data);
@@ -291,26 +343,19 @@ static void mono_cursor(display_cursor *cursor, const guint8 *data)
     for (y = 0; y < cursor->hdr.height; y++) {
         bit = 0x80;
         for (x = 0; x < cursor->hdr.width; x++, dest += 4) {
+            if (cursor_is_xor_edge(cursor, xor_base, bpl, x, y) && and_ones) {
+                dest[0] = 0x00;
+                dest[1] = 0x00;
+                dest[2] = 0x00;
+                dest[3] = 0xff;
+                goto next_bit;
+            }
             if (and[x/8] & bit) {
                 if (xor[x/8] & bit) {
-                    /*
-                     * flip -> unsupported by x11, since XCreatePixmapCursor has
-                     * no invert functionality, only a mask, shape, background and
-                     * foreground colors. Use this checkerboard hack to get some
-                     * contrast for cursors in the guest that relied on invert for
-                     * the same contrast.
-                     */
-                    if ((x ^ y) & 1) {
-                        dest[0] = 0xff;
-                        dest[1] = 0xff;
-                        dest[2] = 0xff;
-                        dest[3] = 0xff;
-                    } else {
-                        dest[0] = 0x00;
-                        dest[1] = 0x00;
-                        dest[2] = 0x00;
-                        dest[3] = 0x00;
-                    }
+                    dest[0] = 0xff;
+                    dest[1] = 0xff;
+                    dest[2] = 0xff;
+                    dest[3] = 0xff;
                 } else {
                     /* unchanged -> transparent */
                     dest[0] = 0x00;
@@ -333,6 +378,7 @@ static void mono_cursor(display_cursor *cursor, const guint8 *data)
                     dest[3] = 0xff;
                 }
             }
+        next_bit:
             bit >>= 1;
             if (bit == 0) {
                 bit = 0x80;
-- 
1.8.3.1



More information about the Spice-devel mailing list