1 /* $OpenBSD: colour.c,v 1.13 2016/06/06 10:12:58 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 colors (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); 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); 87 } 88 89 /* Set grid cell foreground colour. */ 90 void 91 colour_set_fg(struct grid_cell *gc, int c) 92 { 93 if (c & 0x100) 94 gc->flags |= GRID_FLAG_FG256; 95 gc->fg = c; 96 } 97 98 /* Set grid cell background colour. */ 99 void 100 colour_set_bg(struct grid_cell *gc, int c) 101 { 102 if (c & 0x100) 103 gc->flags |= GRID_FLAG_BG256; 104 gc->bg = c; 105 } 106 107 /* Convert colour to a string. */ 108 const char * 109 colour_tostring(int c) 110 { 111 static char s[32]; 112 113 if (c & 0x100) { 114 xsnprintf(s, sizeof s, "colour%d", c & ~0x100); 115 return (s); 116 } 117 118 switch (c) { 119 case 0: 120 return ("black"); 121 case 1: 122 return ("red"); 123 case 2: 124 return ("green"); 125 case 3: 126 return ("yellow"); 127 case 4: 128 return ("blue"); 129 case 5: 130 return ("magenta"); 131 case 6: 132 return ("cyan"); 133 case 7: 134 return ("white"); 135 case 8: 136 return ("default"); 137 case 90: 138 return ("brightblack"); 139 case 91: 140 return ("brightred"); 141 case 92: 142 return ("brightgreen"); 143 case 93: 144 return ("brightyellow"); 145 case 94: 146 return ("brightblue"); 147 case 95: 148 return ("brightmagenta"); 149 case 96: 150 return ("brightcyan"); 151 case 97: 152 return ("brightwhite"); 153 } 154 return (NULL); 155 } 156 157 /* Convert colour from string. */ 158 int 159 colour_fromstring(const char *s) 160 { 161 const char *errstr; 162 const char *cp; 163 int n; 164 u_char r, g, b; 165 166 if (*s == '#' && strlen(s) == 7) { 167 for (cp = s + 1; isxdigit((u_char) *cp); cp++) 168 ; 169 if (*cp != '\0') 170 return (-1); 171 n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b); 172 if (n != 3) 173 return (-1); 174 return (colour_find_rgb(r, g, b) | 0x100); 175 } 176 177 if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) { 178 n = strtonum(s + (sizeof "colour") - 1, 0, 255, &errstr); 179 if (errstr != NULL) 180 return (-1); 181 return (n | 0x100); 182 } 183 184 if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0) 185 return (0); 186 if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0) 187 return (1); 188 if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0) 189 return (2); 190 if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0) 191 return (3); 192 if (strcasecmp(s, "blue") == 0 || strcmp(s, "4") == 0) 193 return (4); 194 if (strcasecmp(s, "magenta") == 0 || strcmp(s, "5") == 0) 195 return (5); 196 if (strcasecmp(s, "cyan") == 0 || strcmp(s, "6") == 0) 197 return (6); 198 if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0) 199 return (7); 200 if (strcasecmp(s, "default") == 0 || strcmp(s, "8") == 0) 201 return (8); 202 if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0) 203 return (90); 204 if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0) 205 return (91); 206 if (strcasecmp(s, "brightgreen") == 0 || strcmp(s, "92") == 0) 207 return (92); 208 if (strcasecmp(s, "brightyellow") == 0 || strcmp(s, "93") == 0) 209 return (93); 210 if (strcasecmp(s, "brightblue") == 0 || strcmp(s, "94") == 0) 211 return (94); 212 if (strcasecmp(s, "brightmagenta") == 0 || strcmp(s, "95") == 0) 213 return (95); 214 if (strcasecmp(s, "brightcyan") == 0 || strcmp(s, "96") == 0) 215 return (96); 216 if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0) 217 return (97); 218 return (-1); 219 } 220 221 /* Convert 256 colour palette to 16. */ 222 u_char 223 colour_256to16(u_char c) 224 { 225 static const u_char table[256] = { 226 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 227 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, 228 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 229 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12, 230 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 231 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1, 232 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12, 233 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5, 234 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3, 235 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 236 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 237 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10, 238 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 239 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 240 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8, 241 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15 242 }; 243 244 return (table[c]); 245 } 246