>From ffc83e8eb1743af6ec470e9bcb4581fa146c1c73 Mon Sep 17 00:00:00 2001 From: Brand Huntsman Date: Mon, 26 Feb 2018 21:21:56 -0700 Subject: [PATCH 3/5] RGB colors Signed-off-by: Brand Huntsman --- src/color.c | 92 +++++++++++++++++++++++++++++++++++++++++- src/proto.h | 1 + src/rcfile.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++--- syntax/nanorc.nanorc | 11 +++-- 4 files changed, 206 insertions(+), 9 deletions(-) diff --git a/src/color.c b/src/color.c index 3c68c777..7d7c3e82 100644 --- a/src/color.c +++ b/src/color.c @@ -435,13 +435,49 @@ void precalc_multicolorinfo(void) } } +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 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) { @@ -464,6 +500,60 @@ 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); + + 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); + + break; + }} +} + +/* Convert RGB channel indices to an extended color code. */ +short map_extended_color(int ri, int gi, int bi) +{ + /* Use gray lookup table if all channels are identical. */ + if (ri == gi && gi == bi) + return gray_lut[gi]; + + int r = color_lut[ri]; + int g = color_lut[gi]; + int b = color_lut[bi]; + return 16 + + (r * color_cube_size * color_cube_size) + + (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 a86c9d47..18e4d2e4 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -585,7 +585,7 @@ void parse_includes(char *ptr) /* 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) +static short namedcolor_to_short(const char *colorname, bool *bright) { if (strncasecmp(colorname, "bright", 6) == 0) { *bright = TRUE; @@ -614,6 +614,103 @@ short color_to_short(const char *colorname, bool *bright) return -1; } +/* Convert single [0-9a-fA-F] digit to 0-15 value. */ +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 RGB color in rgbcolor. */ +static short rgbcolor_to_short(const char *rgbcolor) +{ + int ri, gi, bi; + + /* RGB colors are #RRGGBB or #RGB hex formatted values. */ + switch(strlen(rgbcolor)){ + case 3: + ri = (hex2int(rgbcolor[0]) << 4) | hex2int(rgbcolor[0]); + gi = (hex2int(rgbcolor[1]) << 4) | hex2int(rgbcolor[1]); + bi = (hex2int(rgbcolor[2]) << 4) | hex2int(rgbcolor[2]); + break; + case 6: + ri = (hex2int(rgbcolor[0]) << 4) | hex2int(rgbcolor[1]); + gi = (hex2int(rgbcolor[2]) << 4) | hex2int(rgbcolor[3]); + bi = (hex2int(rgbcolor[4]) << 4) | hex2int(rgbcolor[5]); + break; + default: + /* Invalid. */ + ri = gi = bi = -1; + } + + if (ri == -1 || gi == -1 || bi == -1) { + rgbcolor--; + rcfile_error(N_("Color \"%s\" not understood"), rgbcolor); + return -1; + } + + return map_extended_color(ri, gi, bi); +} + +/* Return the short value corresponding to the color in colorname, + * and set bright to TRUE if that color has bright prefix. */ +static short color_to_short(const char *colorname, bool *bright, bool *invalid) +{ + *invalid = TRUE; + + if (*colorname == '#') { + char *rgbcolorname = (char *)colorname + 1; + char *namedcolorname = (char *)colorname + 1; + short rgbcolor, namedcolor; + + while (*namedcolorname != '\0' && *namedcolorname != ':') + namedcolorname++; + + /* A colon is required after RGB value. */ + if (*namedcolorname != ':') { + rcfile_error(N_("Color \"%s\" not understood"), colorname); + return -1; + } + *namedcolorname = '\0'; + namedcolorname++; + + /* Both values are parsed on all terminals to catch errors. */ + + rgbcolor = rgbcolor_to_short(rgbcolorname); + if (rgbcolor == -1) + return -1; + + /* Use default color if named color is not given. */ + if (*namedcolorname != '\0') { + namedcolor = namedcolor_to_short(namedcolorname, bright); + if (namedcolor == -1) + return -1; + } else { + *bright = FALSE; + namedcolor = -1; + } + + *invalid = FALSE; + if (nr_term_colors > 16) { + /* namedcolor_to_short() may have set bright, clear it. */ + *bright = FALSE; + return rgbcolor; + } else + return namedcolor; + } + + short namedcolor = namedcolor_to_short(colorname, bright); + *invalid = (namedcolor == -1); + return namedcolor; +} + /* Parse the color string in the line at ptr, and add it to the current * file's associated colors. rex_flags are the regex compilation flags * to use, excluding or including REG_ICASE for case (in)sensitivity. */ @@ -762,7 +859,7 @@ bool parse_color_names(char *combostr, short *fg, short *bg, int *attributes) { char *word = combostr; char *next_word = find_next_comma_word(combostr); - bool bright; + bool bright, invalid; *attributes = A_NORMAL; @@ -778,10 +875,10 @@ bool parse_color_names(char *combostr, short *fg, short *bg, int *attributes) /* If it's given and not empty, get the foreground color. */ if (word != NULL && *word != '\0') { - *fg = color_to_short(word, &bright); + *fg = color_to_short(word, &bright, &invalid); /* If the specified foreground color is bad, ignore the regexes. */ - if (*fg == -1) + if (invalid) return FALSE; if (bright) { @@ -799,7 +896,11 @@ bool parse_color_names(char *combostr, short *fg, short *bg, int *attributes) /* If it's given and not empty, get the background color. */ if (word != NULL && *word != '\0') { - *bg = color_to_short(word, &bright); + *bg = color_to_short(word, &bright, &invalid); + + /* If the specified background color is bad, ignore the regexes. */ + if (invalid) + return FALSE; if (bright) { rcfile_error(N_("A background color cannot be bright")); diff --git a/syntax/nanorc.nanorc b/syntax/nanorc.nanorc index dde60f8a..6c5a107c 100644 --- a/syntax/nanorc.nanorc +++ b/syntax/nanorc.nanorc @@ -6,9 +6,14 @@ comment "#" # Possible errors and parameters icolor brightred "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|comment|linter|formatter|i?color|extendsyntax).*$" +# EOL Comments +# - any errors before the # will be brightblue instead of brightred +# - must be here to prevent end-of-line comments from highlighting #rgb colors and strings containing "[[:space:]]#" +color brightblue "^[[:space:]]*(set|unset|bind|unbind).*[[:space:]]+#.*$" + # 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:]]+((error|function|key|number|selected|status|title)color)[[:space:]]+(bold|(bold,)?((bright)?(white|black|red|blue|green|yellow|magenta|cyan))?(,(white|black|red|blue|green|yellow|magenta|cyan))?)\>" +icolor yellow "^[[:space:]]*set[[:space:]]+((error|function|key|number|selected|status|title)color)[[:space:]]+(bold|(bold,)?(#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3}):((bright)?(white|black|red|blue|green|yellow|magenta|cyan))?|(bright)?(white|black|red|blue|green|yellow|magenta|cyan))?(,(#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3}):(white|black|red|blue|green|yellow|magenta|cyan)?|(white|black|red|blue|green|yellow|magenta|cyan)))?)(,|[[:space:]]|$)" icolor brightgreen "^[[:space:]]*set[[:space:]]+(backupdir|brackets|errorcolor|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,11 +25,11 @@ 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))?(,(white|black|red|blue|green|yellow|magenta|cyan))?)\>" +icolor yellow "^[[:space:]]*i?color[[:space:]]+(bold|(bold,)?(#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3}):((bright)?(white|black|red|blue|green|yellow|magenta|cyan))?|(bright)?(white|black|red|blue|green|yellow|magenta|cyan))?(,(#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3}):(white|black|red|blue|green|yellow|magenta|cyan)?|(white|black|red|blue|green|yellow|magenta|cyan)))?)(,|[[:space:]]|$)" icolor magenta "^[[:space:]]*i?color\>" "\<(start|end)=" # Comments -color brightblue "(^|[[:space:]]+)#.*$" +color brightblue "^[[:space:]]*#.*$" color cyan "^[[:space:]]*##.*$" # Trailing whitespace -- 2.16.1