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 |
|
|
} |