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
