/* tput reset;clear;gcc -O2 -Wall -Werror -W -Wno-unused-parameter map-colors.c -lncurses && ./a.out ; rm a.out - first argument can be 8,16,88,256 and overrides number of colors received from terminal - 88 shouldn't be used on 256-color terminals, but 8/16 will show mapping tput reset;clear;gcc -O2 -Wall -Werror -W -Wno-unused-parameter map-colors.c -lncurses && ./a.out 8; rm a.out tput reset;clear;gcc -O2 -Wall -Werror -W -Wno-unused-parameter map-colors.c -lncurses && ./a.out 16; rm a.out tput reset;clear;gcc -O2 -Wall -Werror -W -Wno-unused-parameter map-colors.c -lncurses && ./a.out 88; rm a.out tput reset;clear;gcc -O2 -Wall -Werror -W -Wno-unused-parameter map-colors.c -lncurses && ./a.out 256; rm a.out - black, red, green, yellow, blue, magenta, cyan, white (0-7) - bright prefix or bright/bold attributes (8-15) - add gray0 - gray100 names for grayscale colors int code = gray_lut[PCT2IDX(dec2int(pct_first, pct_last))]; - add #RRGGBB syntax for rgb based colors -- #ff7700 - add %RRGGBB syntax for percent based colors -- %994900 - add 100-100-100 syntax for percent based colors -- 100-50-0 - add bold attribute to bold extended colors - ISSUE: bold on an extended color that maps to 0-7 on a 16-color terminal will use the 8-15 colors */ #include #include #include #include #include //////////////////////////////////////////////////////////////////////////////// int nr_cube_colors, nr_row_colors; int gray_lut[256]; // grayscale color codes int cube_lut[256]; // cube offsets if 88/256 -- channel 0-2 intensity if 8/16 int *base_color_lut; // maps extended colors to 8/16-color terminals //////////////////////////////////////////////////////////////////////////////// void init_gray_lut( int n, int codes[], int shades[], float percents[] ){ printf("[GRAYS]\n"); // convert shade to percentage printf("CODE SHADE PERCENT\n"); for(int i = 0; i < n; i++){ percents[i] = round(1000 * 100 * shades[i]/255)/1000; printf("% 4d % 5d %7.3f\n", codes[i], shades[i], percents[i]); } // get percentage of each mid-point printf("FROM MID TO FROM-INDEX TO-INDEX CODE\n"); int prev_index = 0; for(int i = 0; i < (n-1); i++){ float s = percents[i]; float e = percents[i+1]; float pct = (e - s)/2 + s; int index = 255 * pct / 100; printf("%7.3f %7.3f %7.3f % 4d % 4d % 4d\n", s, pct, e, prev_index, index, codes[i]); for(int ii = prev_index; ii <= index; ii++) gray_lut[ii] = codes[i]; prev_index = index+1; } printf(" % 4d 255 % 4d\n", prev_index, codes[n-1]); for(int ii = prev_index; ii <= 255; ii++) gray_lut[ii] = codes[n-1]; } void init_cube_lut( int cube_size, int n, int shades[], float percents[], int base_colors[27] ){ nr_cube_colors = cube_size; nr_row_colors = n; base_color_lut = base_colors; printf("[COLORS]\n"); // convert shade to percentage printf("CODE SHADE PERCENT\n"); for(int i = 0; i < n; i++){ percents[i] = round(1000 * 100 * shades[i]/255)/1000; printf("% 4d % 5d %7.3f\n", i, shades[i], percents[i]); } // get percentage of each mid-point printf("FROM MID TO FROM-INDEX TO-INDEX CODE\n"); int prev_index = 0; for(int i = 0; i < (n-1); i++){ float s = percents[i]; float e = percents[i+1]; float pct = (e - s)/2 + s; int index = 255 * pct / 100; printf("%7.3f %7.3f %7.3f % 4d % 4d % 4d\n", s, pct, e, prev_index, index, i); for(int ii = prev_index; ii <= index; ii++) cube_lut[ii] = i; prev_index = index+1; } printf(" % 4d 255 % 4d\n", prev_index, n-1); for(int ii = prev_index; ii <= 255; ii++) cube_lut[ii] = n-1; } #define PCT2IDX(pct) ((255 * pct + 50) / 100) #define PCT2IDX_99(pct) ((255 * pct + 49) / 99) int hex2int( char c ){ if(c >= '0' && c <= '9') return c - '0'; c |= 32; // to lowercase if(c >= 'a' && c <= 'f') return 10 + c - 'a'; return -1; } int dec2int( char *s, char *e ){ int n = 0, x = 1; while(e >= s){ n += (*e - '0') * x; x *= 10; e--; } return n; } // #RRGGBB -- 00-FF int color_from_hex( char *s, int *ri, int *gi, int *bi ){ switch(strlen(s)){ case 3: *ri = (hex2int(s[0])<<4) | hex2int(s[0]); *gi = (hex2int(s[1])<<4) | hex2int(s[1]); *bi = (hex2int(s[2])<<4) | hex2int(s[2]); break; case 6: *ri = (hex2int(s[0])<<4) | hex2int(s[1]); *gi = (hex2int(s[2])<<4) | hex2int(s[3]); *bi = (hex2int(s[4])<<4) | hex2int(s[5]); break; default: *ri = *gi = *bi = -1; // invalid } printf("#RRGGBB = %s = (%d, %d, %d)\n", s, *ri, *gi, *bi); return (*ri==-1 || *gi==-1 || *bi==-1 ? 0 : 1); } // %RRGGBB -- 0-99 int color_from_pct( char *s, int *ri, int *gi, int *bi ){ switch(strlen(s)){ case 6: { char *p = s; while(*p >= '0' && *p <= '9') p++; if(*p == '\0'){ *ri = PCT2IDX_99(dec2int(&s[0], &s[1])); *gi = PCT2IDX_99(dec2int(&s[2], &s[3])); *bi = PCT2IDX_99(dec2int(&s[4], &s[5])); } else *ri = *gi = *bi = -1; // invalid } break; default: *ri = *gi = *bi = -1; // invalid } printf("%%RRGGBB = %s = (%d, %d, %d)\n", s, *ri, *gi, *bi); return (*ri==-1 || *gi==-1 || *bi==-1 ? 0 : 1); } // RR-GG-BB -- 0-100 int color_from_pct2( char *s, int *ri, int *gi, int *bi ){ *ri = *gi = *bi = -1; char *p = s, *r1, *r2, *g1, *g2, *b1, *b2; r1 = p; while(*p >= '0' && *p <= '9') p++; if(*p == '-'){ r2 = p-1; p++; g1 = p; while(*p >= '0' && *p <= '9') p++; if(*p == '-'){ g2 = p-1; p++; b1 = p; while(*p >= '0' && *p <= '9') p++; if(*p == '\0'){ b2 = p-1; *ri = PCT2IDX(dec2int(r1, r2)); *gi = PCT2IDX(dec2int(g1, g2)); *bi = PCT2IDX(dec2int(b1, b2)); } } } printf("R-G-B = %s = (%d, %d, %d)\n", s, *ri, *gi, *bi); return (*ri==-1 || *gi==-1 || *bi==-1 ? 0 : 1); } // convert RGB channel indices to a base or extended ANSI color code void map_extended_color( int is_bold, int ri, int gi, int bi ){ if(ri == gi && gi == bi){ // use gray colormap if all channels are identical int c = gray_lut[gi]; if(nr_cube_colors == 0){ // map to 8/16-color int code = c; int bright = (is_bold || code < 8 ? 30 : 90); c = (code > 7 ? code - 8 : code); printf(" \033[%s%dm#%02X%02X%02X_%d_%d\033[0m", (is_bold?"1;":""), bright+c, ri,gi,bi, code, c); } else { // map to 88/256-color printf(" \033[%s38;5;%dm#%02X%02X%02X_%d\033[0m", (is_bold?"1;":""), c, ri,gi,bi, c); } } else { int r = cube_lut[ri]; int g = cube_lut[gi]; int b = cube_lut[bi]; if(nr_cube_colors == 0){ // map to 8/16-color int code = base_color_lut[(r * 9) + (g * 3) + b]; int bright = (is_bold || code < 8 ? 30 : 90); int c = (code > 7 ? code - 8 : code); printf(" \033[%s%dm#%02X%02X%02X_%d_%d\033[0m", (is_bold?"1;":""), bright+c, ri,gi,bi, code, c); } else { // map to 88/256-color int c = 16 + (r * nr_cube_colors) + (g * nr_row_colors) + b; printf(" \033[%s38;5;%dm#%02X%02X%02X_%d\033[0m", (is_bold?"1;":""), c, ri,gi,bi, c); } } } //////////////////////////////////////////////////////////////////////////////// int main( int argc, char *argv[] ){ printf("\n"); // skip lookup table initialization if ncurses doesn't support color // or nano color is disabled char *tc_buffer = (char*)malloc(2048); char *termtype = getenv("TERM"); printf("terminal = %s\n", termtype); tgetent(tc_buffer, termtype); int nr_colors = tgetnum("Co"); printf("nr colors = %d\n", nr_colors); if(argc == 2){ nr_colors = atoi(argv[1]); printf("use colors = %d\n", nr_colors); } printf("\n"); ////////// // initialize tables if(nr_colors == 256){ int 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}; int 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}; float percents[30]; init_gray_lut(30, codes, g_shades, percents); int c_shades[6] = {0, 94, 135, 176, 214, 255}; init_cube_lut(36, 6, c_shades, percents, NULL); } else if(nr_colors == 88){ int codes[11] = {16, 80, 81, 82, 83, 84, 85, 58, 86, 87, 79}; // 37 is identical to 83, only use 83 int g_shades[11] = {0, 46, 92, 115, 139, 162, 185, 205, 208, 231, 255}; float percents[11]; init_gray_lut(11, codes, g_shades, percents); int c_shades[4] = {0, 139, 205, 255}; init_cube_lut(16, 4, c_shades, percents, NULL); } else if(nr_colors == 16){ int codes[4] = {0, 8, 7, 15}; int g_shades[4] = {0, 127, 229, 255}; float percents[4]; init_gray_lut(4, codes, g_shades, percents); // no extended colors int c_shades[3] = {0, 205, 255}; int colors_16[27] = { // black0, red1, green2, yellow3, blue4, magenta5, cyan6, white7 // black8, red9, green10, yellow11, blue12, magenta13, cyan14, white15 /*000*/0, /*001*/4, /*002*/12, /*010*/2, /*011*/6, /*012*/14, /*020*/10, /*021*/14, /*022*/14, /*100*/1, /*101*/5, /*102*/13, /*110*/3, /*111*/7, /*112*/15, /*120*/11, /*121*/15, /*122*/15, /*200*/9, /*201*/13, /*202*/13, /*210*/11, /*211*/15, /*212*/15, /*220*/11, /*221*/15, /*222*/15 }; init_cube_lut(0, 3, c_shades, percents, colors_16); } else if(nr_colors == 8){ int codes[2] = {0, 7}; int g_shades[2] = {0, 229}; float percents[2]; init_gray_lut(2, codes, g_shades, percents); // no extended colors int c_shades[2] = {0, 205}; int colors_8[27] = { // black0, red1, green2, yellow3, blue4, magenta5, cyan6, white7 /*000*/0, /*001*/4, /*002*/4, /*010*/2, /*011*/6, /*012*/6, /*020*/2, /*021*/6, /*022*/6, /*100*/1, /*101*/5, /*102*/5, /*110*/3, /*111*/7, /*112*/7, /*120*/3, /*121*/7, /*122*/7, /*200*/1, /*201*/5, /*202*/5, /*210*/3, /*211*/7, /*212*/7, /*220*/3, /*221*/7, /*222*/7 }; init_cube_lut(0, 2, c_shades, percents, colors_8); } else { printf("no support for %d-colors\n", nr_colors); exit(EXIT_FAILURE); } printf("\n"); ////////// // dump tables // printf("int grays_N[256] = {"); for(int i =0; i < 256; i++) printf("%d%s", gray_lut[i], (i==255?"":", ")); printf("};\n\n"); // printf("int colors_N[256] = {"); for(int i =0; i < 256; i++) printf("%d%s", cube_lut[i], (i==255?"":", ")); printf("};\n\n"); ////////// // gray printf("GRAY"); for(int gp = 0; gp <= 100; gp++){ int c = gray_lut[PCT2IDX(gp)]; printf(" \033[38;5;%dm%d%%_%d\033[0m", c, gp, c); } printf("\n"); printf("\n"); // color printf("COLOR\n"); #define STEP 10 for(int rp = 0; rp <= 100; rp+=STEP){ printf("R %d%%\n", rp); for(int gp = 0; gp <= 100; gp+=STEP){ printf(">"); for(int bp = 0; bp <= 100; bp+=STEP) map_extended_color(0, PCT2IDX(rp), PCT2IDX(gp), PCT2IDX(bp)); printf("\n"); } printf("\n"); } printf("\n"); // gray colors printf("GRAY COLORS"); for(int p = 0; p <= 100; p+=10){ int i = PCT2IDX(p); map_extended_color(0, i, i, i); } printf("\n"); printf("GRAY COLORS"); for(int p = 0; p <= 100; p+=10){ int i = PCT2IDX(p); map_extended_color(1, i, i, i); } printf("\n"); printf("\n"); // parsing int ri, gi, bi; if(!color_from_hex(&"#-f7700"[1], &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_hex(&"#f-7700"[1], &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_hex(&"#ff7700"[1], &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_hex(&"#FF7700"[1], &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_hex(&"#-70"[1], &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_hex(&"#f70"[1], &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_hex(&"#F70"[1], &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_pct(&"%-95500"[1], &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_pct(&"%9-5500"[1], &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_pct(&"%995500"[1], &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_pct2("-00-55-0", &ri, &gi, &bi)) printf("^- FAIL\n"); if(!color_from_pct2("100-55-0", &ri, &gi, &bi)) printf("^- FAIL\n"); printf("\n"); ////////// exit(EXIT_SUCCESS); }