When the color cube slot is found to be already occupied by a similar
24-bit color, search through the -1, 0, +1 R/G/B indices trying to find
an empty slot, or the oldest used one (which hopefully is no longer in
active use).

This effectively reduces random collisions, hence make it pretty hard to
hit a vim GUI color scheme that cannot be correctly showed in urxvt.

Signed-off-by: Fengguang Wu <[email protected]>
---
 src/command.C | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/rxvt.h    |  20 ++++++++++-
 2 files changed, 123 insertions(+), 7 deletions(-)

diff --git a/src/command.C b/src/command.C
index c5a3d6d..a1e0757 100644
--- a/src/command.C
+++ b/src/command.C
@@ -3336,6 +3336,20 @@ rxvt_term::process_osc_seq ()
     }
 }
 
+static unsigned int
+colorcube_index(unsigned int idx_r,
+                unsigned int idx_g,
+                unsigned int idx_b)
+{
+  assert (idx_r < Red_levels);
+  assert (idx_g < Green_levels);
+  assert (idx_b < Blue_levels);
+
+  return idx_r * Blue_levels * Green_levels +
+         idx_g * Blue_levels +
+         idx_b;
+}
+
 /*
  * Find the nearest color slot in the hidden color cube,
  * adapt its value to the 24bit RGB color.
@@ -3343,15 +3357,99 @@ rxvt_term::process_osc_seq ()
 unsigned int
 rxvt_term::map_rgb24_color (unsigned int r, unsigned int g, unsigned int b)
 {
-  unsigned int idx_r = (r & 0xff) / (0xff / (Red_levels - 1));
-  unsigned int idx_g = (g & 0xff) / (0xff / (Green_levels - 1));
-  unsigned int idx_b = (b & 0xff) / (0xff / (Blue_levels - 1));
+  unsigned int idx_r;
+  unsigned int idx_g;
+  unsigned int idx_b;
   unsigned int idx;
+  unsigned int key;
+  unsigned int color;
+  unsigned short seqno;
+  unsigned int index;
+
+  r &= 0xff;
+  g &= 0xff;
+  b &= 0xff;
+
+  color = (r << 16) | (g << 8) | b;
+
+  key = (((r >> (8 - RGB24_HASH_BITS)) << (RGB24_HASH_BITS * 2)) |
+         ((g >> (8 - RGB24_HASH_BITS)) << RGB24_HASH_BITS) |
+         (b >> (8 - RGB24_HASH_BITS))) & (RGB24_HASH_SIZE - 1);
+  assert (key < RGB24_HASH_SIZE);
+
+  idx = rgb24_index[key];
+  if (idx && rgb24_color[idx] == color)
+    return idx + minTermCOLOR24;
+
+  idx_r = r / (0xff / (Red_levels - 1));
+  idx_g = g / (0xff / (Green_levels - 1));
+  idx_b = b / (0xff / (Blue_levels - 1));
+  idx = colorcube_index (idx_r, idx_g, idx_b);
+
+  // minor issue: could update idx 0 few more times
+  if (rgb24_seqno[idx] == 0 &&
+      rgb24_color[idx] == 0)
+    goto update;
+
+  if (rgb24_color[idx] == color)
+    return idx + minTermCOLOR24;
+
+  seqno = rgb24_seqno[idx];
+
+  int i, j, k;
+  for (i = idx_r - 1; i <= (signed) idx_r + 1; i++)
+    {
+      // do unsigned comparison, so that -1 will wrap around to a number
+      // larger than Red/Green/Blue_levels and skipped by below if's.
+      if ((unsigned) i >= (unsigned) Red_levels)
+       continue;
+      for (j = idx_g - 1; j <= (signed) idx_g + 1; j++)
+       {
+         if ((unsigned) j >= (unsigned) Green_levels)
+           continue;
+         for (k = idx_b - 1; k <= (signed) idx_b + 1; k++)
+           {
+             if ((unsigned) k >= (unsigned) Blue_levels)
+               continue;
+
+              index = colorcube_index (i, j, k);
+
+              // minor issue: could update index 0 few more times
+              if (rgb24_seqno[index] == 0 &&
+                  rgb24_color[index] == 0)
+                {
+                  idx = index;
+                  goto update;
+                }
+
+              if (rgb24_color[index] == color)
+                return index + minTermCOLOR24;
+
+              // like (seqno > rgb24_seqno[index])
+              // but also handles wrap around values good enough
+              if ((unsigned short) (seqno - rgb24_seqno[index]) <
+                  (unsigned short) (USHRT_MAX / 2))
+                {
+                  idx = index;
+                 seqno = rgb24_seqno[index];
+                }
+           }
+       }
+    }
+
+#if defined(DEBUG_RGB24)
+    rxvt_warn ("24-bit color collision %hu: [%d%d%d] [%03x] [%3u] #%06x => 
#%06x\n",
+               rgb24_sequence,
+               idx_r, idx_g, idx_b,
+               key, idx, rgb24_color[idx], color);
+#endif
 
-  idx = minTermCOLOR24 + idx_r * Blue_levels * Green_levels +
-                         idx_g * Blue_levels +
-                         idx_b;
+update:
+  rgb24_index[key] = idx;
+  rgb24_color[idx] = color;
+  rgb24_seqno[idx] = ++rgb24_sequence;
 
+  idx += minTermCOLOR24;
   pix_colors_focused [idx].free (this);
   pix_colors_focused [idx].set (this, rgba (r * 0x0101,
                                             g * 0x0101,
diff --git a/src/rxvt.h b/src/rxvt.h
index 8c19025..444c88e 100644
--- a/src/rxvt.h
+++ b/src/rxvt.h
@@ -372,6 +372,19 @@ struct mouse_event
 # define Blue_levels     4
 #endif
 
+#define RGB24_CUBE_SIZE  (Red_levels * Green_levels * Blue_levels)
+#define RGB24_HASH_BITS  4  // for each R/G/B component
+#define RGB24_HASH_SIZE  (1 << (RGB24_HASH_BITS * 3))
+
+#if (RGB24_CUBE_SIZE > UCHAR_MAX)
+# error please increase storage size of rgb24_index[]
+#endif
+
+#if (RGB24_HASH_SIZE <  2 * RGB24_CUBE_SIZE) || \
+    (RGB24_HASH_SIZE > 32 * RGB24_CUBE_SIZE)
+# error consider update RGB24_HASH_BITS to reasonable value
+#endif
+
 #if defined (NO_MOUSE_REPORT) && !defined (NO_MOUSE_REPORT_SCROLLBAR)
 # define NO_MOUSE_REPORT_SCROLLBAR 1
 #endif
@@ -577,7 +590,7 @@ enum colour_list {
 #endif
   minTermCOLOR24,
   maxTermCOLOR24 = minTermCOLOR24 +
-                   (Red_levels * Green_levels * Blue_levels) - 1,
+                   RGB24_CUBE_SIZE - 1,
 #ifndef NO_CURSORCOLOR
   Color_cursor,
   Color_cursor2,
@@ -1272,6 +1285,11 @@ struct rxvt_term : zero_initialized, rxvt_vars, 
rxvt_screen
   void           *chunk;
   size_t          chunk_size;
 
+  unsigned char   rgb24_index[RGB24_HASH_SIZE];   // index into rgb24_color[]
+  unsigned int    rgb24_color[RGB24_CUBE_SIZE];   // the 24-bit color value
+  unsigned short  rgb24_seqno[RGB24_CUBE_SIZE];   // which one is older?
+  unsigned short  rgb24_sequence;
+
   static vector<rxvt_term *> termlist; // a vector of all running rxvt_term's
 
 #if ENABLE_FRILLS || ISO_14755
-- 
2.8.1


_______________________________________________
rxvt-unicode mailing list
[email protected]
http://lists.schmorp.de/mailman/listinfo/rxvt-unicode

Reply via email to