1 /* $OpenBSD: colour.c,v 1.19 2020/08/25 11:35:32 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * Copyright (c) 2016 Avi Halachmi <avihpit@yahoo.com> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 16 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 17 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 22 #include <ctype.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include "tmux.h" 27 28 static int 29 colour_dist_sq(int R, int G, int B, int r, int g, int b) 30 { 31 return ((R - r) * (R - r) + (G - g) * (G - g) + (B - b) * (B - b)); 32 } 33 34 static int 35 colour_to_6cube(int v) 36 { 37 if (v < 48) 38 return (0); 39 if (v < 114) 40 return (1); 41 return ((v - 35) / 40); 42 } 43 44 /* 45 * Convert an RGB triplet to the xterm(1) 256 colour palette. 46 * 47 * xterm provides a 6x6x6 colour cube (16 - 231) and 24 greys (232 - 255). We 48 * map our RGB colour to the closest in the cube, also work out the closest 49 * grey, and use the nearest of the two. 50 * 51 * Note that the xterm has much lower resolution for darker colours (they are 52 * not evenly spread out), so our 6 levels are not evenly spread: 0x0, 0x5f 53 * (95), 0x87 (135), 0xaf (175), 0xd7 (215) and 0xff (255). Greys are more 54 * evenly spread (8, 18, 28 ... 238). 55 */ 56 int 57 colour_find_rgb(u_char r, u_char g, u_char b) 58 { 59 static const int q2c[6] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff }; 60 int qr, qg, qb, cr, cg, cb, d, idx; 61 int grey_avg, grey_idx, grey; 62 63 /* Map RGB to 6x6x6 cube. */ 64 qr = colour_to_6cube(r); cr = q2c[qr]; 65 qg = colour_to_6cube(g); cg = q2c[qg]; 66 qb = colour_to_6cube(b); cb = q2c[qb]; 67 68 /* If we have hit the colour exactly, return early. */ 69 if (cr == r && cg == g && cb == b) 70 return ((16 + (36 * qr) + (6 * qg) + qb) | COLOUR_FLAG_256); 71 72 /* Work out the closest grey (average of RGB). */ 73 grey_avg = (r + g + b) / 3; 74 if (grey_avg > 238) 75 grey_idx = 23; 76 else 77 grey_idx = (grey_avg - 3) / 10; 78 grey = 8 + (10 * grey_idx); 79 80 /* Is grey or 6x6x6 colour closest? */ 81 d = colour_dist_sq(cr, cg, cb, r, g, b); 82 if (colour_dist_sq(grey, grey, grey, r, g, b) < d) 83 idx = 232 + grey_idx; 84 else 85 idx = 16 + (36 * qr) + (6 * qg) + qb; 86 return (idx | COLOUR_FLAG_256); 87 } 88 89 /* Join RGB into a colour. */ 90 int 91 colour_join_rgb(u_char r, u_char g, u_char b) 92 { 93 return ((((int)((r) & 0xff)) << 16) | 94 (((int)((g) & 0xff)) << 8) | 95 (((int)((b) & 0xff))) | COLOUR_FLAG_RGB); 96 } 97 98 /* Split colour into RGB. */ 99 void 100 colour_split_rgb(int c, u_char *r, u_char *g, u_char *b) 101 { 102 *r = (c >> 16) & 0xff; 103 *g = (c >> 8) & 0xff; 104 *b = c & 0xff; 105 } 106 107 /* Convert colour to a string. */ 108 const char * 109 colour_tostring(int c) 110 { 111 static char s[32]; 112 u_char r, g, b; 113 114 if (c & COLOUR_FLAG_RGB) { 115 colour_split_rgb(c, &r, &g, &b); 116 xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b); 117 return (s); 118 } 119 120 if (c & COLOUR_FLAG_256) { 121 xsnprintf(s, sizeof s, "colour%u", c & 0xff); 122 return (s); 123 } 124 125 switch (c) { 126 case 0: 127 return ("black"); 128 case 1: 129 return ("red"); 130 case 2: 131 return ("green"); 132 case 3: 133 return ("yellow"); 134 case 4: 135 return ("blue"); 136 case 5: 137 return ("magenta"); 138 case 6: 139 return ("cyan"); 140 case 7: 141 return ("white"); 142 case 8: 143 return ("default"); 144 case 9: 145 return ("terminal"); 146 case 90: 147 return ("brightblack"); 148 case 91: 149 return ("brightred"); 150 case 92: 151 return ("brightgreen"); 152 case 93: 153 return ("brightyellow"); 154 case 94: 155 return ("brightblue"); 156 case 95: 157 return ("brightmagenta"); 158 case 96: 159 return ("brightcyan"); 160 case 97: 161 return ("brightwhite"); 162 } 163 return ("invalid"); 164 } 165 166 /* Convert colour from string. */ 167 int 168 colour_fromstring(const char *s) 169 { 170 const char *errstr; 171 const char *cp; 172 int n; 173 u_char r, g, b; 174 175 if (*s == '#' && strlen(s) == 7) { 176 for (cp = s + 1; isxdigit((u_char) *cp); cp++) 177 ; 178 if (*cp != '\0') 179 return (-1); 180 n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b); 181 if (n != 3) 182 return (-1); 183 return (colour_join_rgb(r, g, b)); 184 } 185 186 if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) { 187 n = strtonum(s + (sizeof "colour") - 1, 0, 255, &errstr); 188 if (errstr != NULL) 189 return (-1); 190 return (n | COLOUR_FLAG_256); 191 } 192 if (strncasecmp(s, "color", (sizeof "color") - 1) == 0) { 193 n = strtonum(s + (sizeof "color") - 1, 0, 255, &errstr); 194 if (errstr != NULL) 195 return (-1); 196 return (n | COLOUR_FLAG_256); 197 } 198 199 if (strcasecmp(s, "default") == 0) 200 return (8); 201 if (strcasecmp(s, "terminal") == 0) 202 return (9); 203 204 if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0) 205 return (0); 206 if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0) 207 return (1); 208 if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0) 209 return (2); 210 if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0) 211 return (3); 212 if (strcasecmp(s, "blue") == 0 || strcmp(s, "4") == 0) 213 return (4); 214 if (strcasecmp(s, "magenta") == 0 || strcmp(s, "5") == 0) 215 return (5); 216 if (strcasecmp(s, "cyan") == 0 || strcmp(s, "6") == 0) 217 return (6); 218 if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0) 219 return (7); 220 if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0) 221 return (90); 222 if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0) 223 return (91); 224 if (strcasecmp(s, "brightgreen") == 0 || strcmp(s, "92") == 0) 225 return (92); 226 if (strcasecmp(s, "brightyellow") == 0 || strcmp(s, "93") == 0) 227 return (93); 228 if (strcasecmp(s, "brightblue") == 0 || strcmp(s, "94") == 0) 229 return (94); 230 if (strcasecmp(s, "brightmagenta") == 0 || strcmp(s, "95") == 0) 231 return (95); 232 if (strcasecmp(s, "brightcyan") == 0 || strcmp(s, "96") == 0) 233 return (96); 234 if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0) 235 return (97); 236 return (-1); 237 } 238 239 /* Convert 256 colour to RGB colour. */ 240 int 241 colour_256toRGB(int c) 242 { 243 static const int table[256] = { 244 0x000000, 0x800000, 0x008000, 0x808000, 245 0x000080, 0x800080, 0x008080, 0xc0c0c0, 246 0x808080, 0xff0000, 0x00ff00, 0xffff00, 247 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, 248 0x000000, 0x00005f, 0x000087, 0x0000af, 249 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 250 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 251 0x008700, 0x00875f, 0x008787, 0x0087af, 252 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 253 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, 254 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 255 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 256 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 257 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 258 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 259 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 260 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 261 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, 262 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 263 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 264 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 265 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 266 0x870000, 0x87005f, 0x870087, 0x8700af, 267 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 268 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 269 0x878700, 0x87875f, 0x878787, 0x8787af, 270 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 271 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 272 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 273 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, 274 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 275 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 276 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 277 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 278 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 279 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 280 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 281 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, 282 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 283 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, 284 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 285 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 286 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 287 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 288 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 289 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 290 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 291 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, 292 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 293 0xff0000, 0xff005f, 0xff0087, 0xff00af, 294 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 295 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 296 0xff8700, 0xff875f, 0xff8787, 0xff87af, 297 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, 298 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 299 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 300 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 301 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, 302 0x080808, 0x121212, 0x1c1c1c, 0x262626, 303 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 304 0x585858, 0x626262, 0x6c6c6c, 0x767676, 305 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 306 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 307 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee 308 }; 309 310 return (table[c & 0xff] | COLOUR_FLAG_RGB); 311 } 312 313 /* Convert 256 colour to 16 colour. */ 314 int 315 colour_256to16(int c) 316 { 317 static const char table[256] = { 318 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 319 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, 320 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 321 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12, 322 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 323 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1, 324 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12, 325 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5, 326 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3, 327 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 328 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 329 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10, 330 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 331 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 332 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8, 333 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15 334 }; 335 336 return (table[c & 0xff]); 337 } 338