1 /* $OpenBSD: colour.c,v 1.15 2017/03/24 07:14:27 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 90: 145 return ("brightblack"); 146 case 91: 147 return ("brightred"); 148 case 92: 149 return ("brightgreen"); 150 case 93: 151 return ("brightyellow"); 152 case 94: 153 return ("brightblue"); 154 case 95: 155 return ("brightmagenta"); 156 case 96: 157 return ("brightcyan"); 158 case 97: 159 return ("brightwhite"); 160 } 161 return (NULL); 162 } 163 164 /* Convert colour from string. */ 165 int 166 colour_fromstring(const char *s) 167 { 168 const char *errstr; 169 const char *cp; 170 int n; 171 u_char r, g, b; 172 173 if (*s == '#' && strlen(s) == 7) { 174 for (cp = s + 1; isxdigit((u_char) *cp); cp++) 175 ; 176 if (*cp != '\0') 177 return (-1); 178 n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b); 179 if (n != 3) 180 return (-1); 181 return (colour_join_rgb(r, g, b)); 182 } 183 184 if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) { 185 n = strtonum(s + (sizeof "colour") - 1, 0, 255, &errstr); 186 if (errstr != NULL) 187 return (-1); 188 return (n | COLOUR_FLAG_256); 189 } 190 191 if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0) 192 return (0); 193 if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0) 194 return (1); 195 if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0) 196 return (2); 197 if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0) 198 return (3); 199 if (strcasecmp(s, "blue") == 0 || strcmp(s, "4") == 0) 200 return (4); 201 if (strcasecmp(s, "magenta") == 0 || strcmp(s, "5") == 0) 202 return (5); 203 if (strcasecmp(s, "cyan") == 0 || strcmp(s, "6") == 0) 204 return (6); 205 if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0) 206 return (7); 207 if (strcasecmp(s, "default") == 0 || strcmp(s, "8") == 0) 208 return (8); 209 if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0) 210 return (90); 211 if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0) 212 return (91); 213 if (strcasecmp(s, "brightgreen") == 0 || strcmp(s, "92") == 0) 214 return (92); 215 if (strcasecmp(s, "brightyellow") == 0 || strcmp(s, "93") == 0) 216 return (93); 217 if (strcasecmp(s, "brightblue") == 0 || strcmp(s, "94") == 0) 218 return (94); 219 if (strcasecmp(s, "brightmagenta") == 0 || strcmp(s, "95") == 0) 220 return (95); 221 if (strcasecmp(s, "brightcyan") == 0 || strcmp(s, "96") == 0) 222 return (96); 223 if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0) 224 return (97); 225 return (-1); 226 } 227 228 /* Convert 256 colour palette to 16. */ 229 u_char 230 colour_256to16(u_char c) 231 { 232 static const u_char table[256] = { 233 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 234 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, 235 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 236 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12, 237 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 238 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1, 239 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12, 240 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5, 241 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3, 242 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 243 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 244 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10, 245 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 246 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 247 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8, 248 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15 249 }; 250 251 return (table[c]); 252 } 253