>From 109b5b51de798993deaa53426cdbdc6adf0cc7ee Mon Sep 17 00:00:00 2001 From: Brand Huntsman Date: Thu, 15 Feb 2018 19:49:42 -0700 Subject: [PATCH 4/6] RGB colors Signed-off-by: Brand Huntsman --- src/color.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/proto.h | 1 + src/rcfile.c | 39 ++++++++++++ syntax/nanorc.nanorc | 4 +- 4 files changed, 214 insertions(+), 3 deletions(-) diff --git a/src/color.c b/src/color.c index 426692a3..4eac6f34 100644 --- a/src/color.c +++ b/src/color.c @@ -432,13 +432,88 @@ void precalc_multicolorinfo(void) } } +static int color_cube_size_squared; + /* Number of colors per cube face. */ +static int color_cube_size; + /* Number of colors per cube row. */ +static unsigned char gray_lut[256]; + /* Grayscale lookup table. */ +static unsigned char color_lut[256]; + /* Color lookup table. + * Channel intensity = 0 to (color_cube_size-1). */ +static unsigned char *low_color_cube; + /* Color cube to map extended colors to 8/16-color terminals. */ +static unsigned char _low_color_cube[125] = { + /* black0, red1, green2, yellow3, blue4, magenta5, cyan6, white7 + * black8, red9, green10, yellow11, blue12, magenta13, cyan14, white15 */ + + /*000*/ 0, /*001*/ 0, /*002*/ 4, /*003*/ 4, /*004*/12, + /*010*/ 0, /*011*/ 8, /*012*/ 4, /*013*/ 4, /*014*/12, + /*020*/ 2, /*021*/ 2, /*022*/ 6, /*023*/ 6, /*024*/12, + /*030*/ 2, /*031*/ 2, /*032*/ 6, /*033*/ 6, /*034*/14, + /*040*/10, /*041*/10, /*042*/10, /*043*/14, /*044*/14, + + /*100*/ 0, /*101*/ 8, /*102*/ 4, /*103*/ 4, /*104*/12, + /*110*/ 8, /*111*/ 8, /*112*/ 4, /*113*/ 4, /*114*/12, + /*120*/ 2, /*121*/ 2, /*122*/ 6, /*123*/ 6, /*124*/12, + /*130*/ 2, /*131*/ 2, /*132*/ 6, /*133*/ 6, /*134*/14, + /*140*/10, /*141*/10, /*142*/10, /*143*/14, /*144*/14, + + /*200*/ 1, /*201*/ 1, /*202*/ 5, /*203*/ 5, /*204*/12, + /*210*/ 1, /*211*/ 1, /*212*/ 5, /*213*/ 5, /*214*/12, + /*220*/ 3, /*221*/ 3, /*222*/ 7, /*223*/ 7, /*224*/12, + /*230*/ 3, /*231*/ 3, /*232*/ 7, /*233*/ 7, /*234*/14, + /*240*/10, /*241*/10, /*242*/10, /*243*/14, /*244*/14, + + /*300*/ 1, /*301*/ 1, /*302*/ 5, /*303*/ 5, /*304*/13, + /*310*/ 1, /*311*/ 1, /*312*/ 5, /*313*/ 5, /*314*/13, + /*320*/ 3, /*321*/ 3, /*322*/ 7, /*323*/ 7, /*324*/13, + /*330*/ 3, /*331*/ 3, /*332*/ 7, /*333*/ 7, /*334*/15, + /*340*/11, /*341*/11, /*342*/11, /*343*/15, /*344*/15, + + /*400*/ 9, /*401*/ 9, /*402*/ 9, /*403*/13, /*404*/13, + /*410*/ 9, /*411*/ 9, /*412*/ 9, /*413*/13, /*414*/13, + /*420*/ 9, /*421*/ 9, /*422*/ 9, /*423*/13, /*424*/13, + /*430*/11, /*431*/11, /*432*/11, /*433*/15, /*434*/15, + /*440*/11, /*441*/11, /*442*/11, /*443*/15, /*444*/15, +}; + +static float nano_round(float value) +{ + return (float)(value > 0 ? (int)(value + 0.5) : (int)(value - 0.4999999)); +} + +/* Initialize the gray or color lookup table. */ +static void init_lut(int size, unsigned char lut[256], + unsigned char shades[], unsigned char g_codes[]) +{ + if(g_codes == NULL) + color_cube_size = size; + + /* Get index of each mid-point. */ + int prev_index = 0; + for (int i = 0; i < (size-1); i++) { + float s = nano_round(1000 * shades[i+0] / 255) / 1000; + float e = nano_round(1000 * shades[i+1] / 255) / 1000; + int index = 255 * ((e - s)/2 + s); + /* Fill lookup table with g_codes or channel intensity. */ + for (int ii = prev_index; ii <= index; ii++) + lut[ii] = (g_codes != NULL ? g_codes[i] : i); + prev_index = index+1; + } + /* Fill lookup table with g_codes or channel intensity. + * From last mid-point to 255. */ + for (int ii = prev_index; ii <= 255; ii++) + lut[ii] = (g_codes != NULL ? g_codes[size-1] : size-1); +} + #if defined(NCURSES_VERSION_MAJOR) && (NCURSES_VERSION_MAJOR >= 6) #define HAS_EXTENDED_COLORS 1 #else #define HAS_EXTENDED_COLORS 0 #endif -/* Get number of colors supported by terminal. */ +/* Get number of colors supported by terminal and initialize lookup tables. */ void extended_color_init(void) { if (tgetent(NULL, getenv("TERM")) != 1) { @@ -461,6 +536,102 @@ void extended_color_init(void) nr_term_colors = 8; } else if (nr_term_colors != 8) nr_term_colors = 0; + + /* Initialize lookup tables. */ + switch (nr_term_colors) { + case 256: + { + unsigned char g_codes[30] = { + 16, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 59, 241, 242, 243, 244, 102, 245, 246, 247, 248, + 145, 249, 250, 251, 252, 188, 253, 254, 255, 231 + }; + unsigned char g_shades[30] = { + 0, 8, 18, 28, 38, 48, 58, 68, 78, 88, + 95, 98, 108, 118, 128, 135, 138, 148, 158, 168, + 175, 178, 188, 198, 208, 215, 218, 228, 238, 255 + }; + init_lut(30, gray_lut, g_shades, g_codes); + + unsigned char c_shades[6] = {0, 94, 135, 176, 214, 255}; + init_lut(6, color_lut, c_shades, NULL); + low_color_cube = NULL; + + break; + } + case 88: + { + /* Code 37 is identical to code 83, only use 83. */ + unsigned char g_codes[11] = { + 16, 80, 81, 82, 83, 84, 85, 58, 86, 87, 79 + }; + unsigned char g_shades[11] = { + 0, 46, 92, 115, 139, 162, 185, 205, 208, 231, 255 + }; + init_lut(11, gray_lut, g_shades, g_codes); + + unsigned char c_shades[4] = {0, 139, 205, 255}; + init_lut(4, color_lut, c_shades, NULL); + low_color_cube = NULL; + + break; + } + case 16: + { + unsigned char g_codes[4] = {0, 8, 7, 15}; + unsigned char g_shades[4] = {0, 68, 176, 255}; + /* black - 13% / 35% / 36% / 16% - white */ + init_lut(4, gray_lut, g_shades, g_codes); + + unsigned char c_shades[5] = {0, 51, 127, 204, 255}; + /* dark - 10% / 25% / 30% / 25% / 10% - bright */ + init_lut(5, color_lut, c_shades, NULL); + low_color_cube = _low_color_cube; + + break; + } + case 8: + { + unsigned char g_codes[2] = {0, 7}; + unsigned char g_shades[2] = {0, 68}; + /* black - 13% / 87% - white (black background biased) */ + init_lut(2, gray_lut, g_shades, g_codes); + + unsigned char c_shades[5] = {0, 51, 127, 204, 255}; + /* dark - 10% / 25% / 30% / 25% / 10% - bright */ + init_lut(5, color_lut, c_shades, NULL); + low_color_cube = _low_color_cube; + + /* Convert low color cube from 16 to 8 colors. */ + for (int i = 0; i < 125; i++) + if (low_color_cube[i] > 7) + low_color_cube[i] -= 8; + + break; + }} + color_cube_size_squared = color_cube_size * color_cube_size; +} + +/* Convert RGB channel indices to a base or extended color code. */ +short map_extended_color(int ri, int gi, int bi) +{ + if (ri == gi && gi == bi) { + /* Use gray lookup table if all channels are identical. + * Map to 8/16/88/256-color. */ + return gray_lut[gi]; + } + + int r = color_lut[ri]; + int g = color_lut[gi]; + int b = color_lut[bi]; + if (low_color_cube != NULL) { + /* Map to 8/16-color. */ + return low_color_cube[(r * color_cube_size_squared) + + (g * color_cube_size) + b]; + } else { + /* Map to 88/256-color. */ + return 16 + (r * color_cube_size_squared) + (g * color_cube_size) + b; + } } #endif /* ENABLE_COLOR */ diff --git a/src/proto.h b/src/proto.h index 887a1bc8..c959822d 100644 --- a/src/proto.h +++ b/src/proto.h @@ -250,6 +250,7 @@ void check_the_multis(filestruct *line); void alloc_multidata_if_needed(filestruct *fileptr); void precalc_multicolorinfo(void); void extended_color_init(void); +short map_extended_color(int ri, int gi, int bi); #endif /* Most functions in cut.c. */ diff --git a/src/rcfile.c b/src/rcfile.c index cab54694..4a11e113 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -582,12 +582,51 @@ void parse_includes(char *ptr) lineno = lineno_save; } +static int hex2int(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + + /* To lowercase. */ + c |= 32; + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + + return -1; +} + /* Return the short value corresponding to the color named in colorname, * and set bright to TRUE if that color has bright prefix. */ short color_to_short(const char *colorname, bool *bright) { *bright = FALSE; + /* RGB colors are #RRGGBB or #RGB hex formatted values. */ + if (*colorname == '#') { + int ri, gi, bi; + + switch(strlen(colorname)){ + case 4: + ri = (hex2int(colorname[1])<<4) | hex2int(colorname[1]); + gi = (hex2int(colorname[2])<<4) | hex2int(colorname[2]); + bi = (hex2int(colorname[3])<<4) | hex2int(colorname[3]); + break; + case 7: + ri = (hex2int(colorname[1])<<4) | hex2int(colorname[2]); + gi = (hex2int(colorname[3])<<4) | hex2int(colorname[4]); + bi = (hex2int(colorname[5])<<4) | hex2int(colorname[6]); + break; + default: + /* Invalid. */ + ri = gi = bi = -1; + } + + if (ri != -1 && gi != -1 && bi != -1) + return map_extended_color(ri, gi, bi); + rcfile_error(N_("Color \"%s\" not understood"), colorname); + return -1; + } + /* Index colors are numeric values from 0 to nr_term_colors-1. * These are are not portable between terminals with different * color counts. */ diff --git a/syntax/nanorc.nanorc b/syntax/nanorc.nanorc index bfa6eca9..ac028095 100644 --- a/syntax/nanorc.nanorc +++ b/syntax/nanorc.nanorc @@ -8,7 +8,7 @@ icolor brightred "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|comm # Keywords icolor brightgreen "^[[:space:]]*(set|unset)[[:space:]]+(allow_insecure_backup|atblanks|autoindent|backup|backwards|boldtext|casesensitive|constantshow|cutfromcursor|fill[[:space:]]+-?[[:digit:]]+|historylog|linenumbers|locking|morespace|mouse|multibuffer|noconvert|nohelp|nopauses|nonewlines|nowrap|positionlog|preserve|quickblank|quiet|rebinddelete|rebindkeypad|regexp|showcursor|smarthome|smooth|softwrap|suspend|tabsize[[:space:]]+[1-9][0-9]*|tabstospaces|tempfile|trimblanks|unix|view|wordbounds)\>" -icolor yellow "^[[:space:]]*set[[:space:]]+((function|key|number|selected|status|title)color)[[:space:]]+(bold|(bold,)?((bright)?(white|black|red|blue|green|yellow|magenta|cyan)|[0-9]+)?(,(white|black|red|blue|green|yellow|magenta|cyan|[0-9]+))?)\>" +icolor yellow "^[[:space:]]*set[[:space:]]+((function|key|number|selected|status|title)color)[[:space:]]+(bold|(bold,)?((bright)?(white|black|red|blue|green|yellow|magenta|cyan)|([0-9]+|#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3}))?(,(white|black|red|blue|green|yellow|magenta|cyan|[0-9]+|#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3}))?)\>" icolor brightgreen "^[[:space:]]*set[[:space:]]+(backupdir|brackets|functioncolor|keycolor|matchbrackets|numbercolor|operatingdir|punct|quotestr|selectedcolor|speller|statuscolor|titlecolor|whitespace|wordchars)[[:space:]]+" icolor brightgreen "^[[:space:]]*bind[[:space:]]+((\^([[:alpha:]]|[]0-9\^_]|Space)|M-([[:alpha:]]|[]!"#$%&'()*+,./0-9:;<=>address@hidden|}~-]|Space))|F([1-9]|1[0-6])|Ins|Del)[[:space:]]+[[:alpha:]]+[[:space:]]+(all|main|search|replace(with)?|gotoline|writeout|insert|ext(ernal)?cmd|help|spell|linter|browser|whereisfile|gotodir)([[:space:]]+#|[[:space:]]*$)" icolor brightgreen "^[[:space:]]*unbind[[:space:]]+((\^([[:alpha:]]|[]0-9\^_]|Space)|M-([[:alpha:]]|[]!"#$%&'()*+,./0-9:;<=>address@hidden|}~-]|Space))|F([1-9]|1[0-6])|Ins|Del)[[:space:]]+(all|main|search|replace(with)?|gotoline|writeout|insert|ext(ernal)?cmd|help|spell|linter|browser|whereisfile|gotodir)([[:space:]]+#|[[:space:]]*$)" @@ -20,7 +20,7 @@ icolor green "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|comment| color brightmagenta "".+"([[:space:]]|$)" # Colors -icolor yellow "^[[:space:]]*i?color[[:space:]]+(bold|(bold,)?((bright)?(white|black|red|blue|green|yellow|magenta|cyan)|[0-9]+)?(,(white|black|red|blue|green|yellow|magenta|cyan|[0-9]+))?)\>" +icolor yellow "^[[:space:]]*i?color[[:space:]]+(bold|(bold,)?((bright)?(white|black|red|blue|green|yellow|magenta|cyan)|([0-9]+|#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3}))?(,(white|black|red|blue|green|yellow|magenta|cyan|[0-9]+|#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3}))?)\>" icolor magenta "^[[:space:]]*i?color\>" "\<(start|end)=" # Comments -- 2.13.6