xref: /openbsd/usr.bin/tmux/colour.c (revision a6445c1d)
1 /* $OpenBSD: colour.c,v 1.6 2013/03/27 11:17:12 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "tmux.h"
26 
27 /*
28  * Colour to string conversion functions. Bit 8 of the colour means it is one
29  * of the 256 colour palette.
30  */
31 
32 /* An RGB colour. */
33 struct colour_rgb {
34 	u_char	r;
35 	u_char	g;
36 	u_char	b;
37 };
38 
39 /* 256 colour RGB table, generated on first use. */
40 struct colour_rgb *colour_rgb_256;
41 
42 void	colour_rgb_generate256(void);
43 u_int	colour_rgb_distance(struct colour_rgb *, struct colour_rgb *);
44 int	colour_rgb_find(struct colour_rgb *);
45 
46 /* Generate 256 colour RGB table. */
47 void
48 colour_rgb_generate256(void)
49 {
50 	struct colour_rgb	*rgb;
51 	u_int			 i, r, g, b;
52 
53 	/*
54 	 * Allocate the table. The first 16 colours are often changed by users
55 	 * and terminals so don't include them.
56 	 */
57 	colour_rgb_256 = xcalloc(240, sizeof *colour_rgb_256);
58 
59 	/* Add the colours first. */
60 	r = g = b = 0;
61 	for (i = 240; i > 24; i--) {
62 		rgb = &colour_rgb_256[240 - i];
63 
64 		if (r != 0)
65 			rgb->r = (r * 40) + 55;
66 		if (g != 0)
67 			rgb->g = (g * 40) + 55;
68 		if (b != 0)
69 			rgb->b = (b * 40) + 55;
70 
71 		b++;
72 		if (b > 5) {
73 			b = 0;
74 			g++;
75 		}
76 		if (g > 5) {
77 			g = 0;
78 			r++;
79 		}
80 	}
81 
82 	/* Then add the greys. */
83 	for (i = 24; i > 0; i--) {
84 		rgb = &colour_rgb_256[240 - i];
85 
86 		rgb->r = 8 + (24 - i) * 10;
87 		rgb->g = 8 + (24 - i) * 10;
88 		rgb->b = 8 + (24 - i) * 10;
89 	}
90 }
91 
92 /* Get colour RGB distance. */
93 u_int
94 colour_rgb_distance(struct colour_rgb *rgb1, struct colour_rgb *rgb2)
95 {
96 	int	r, g, b;
97 
98 	r = rgb1->r - rgb2->r;
99 	g = rgb1->g - rgb2->g;
100 	b = rgb1->b - rgb2->b;
101 	return (r * r + g * g + b * b);
102 }
103 
104 /* Work out the nearest colour from the 256 colour set. */
105 int
106 colour_rgb_find(struct colour_rgb *rgb)
107 {
108 	u_int	distance, lowest, colour, i;
109 
110 	if (colour_rgb_256 == NULL)
111 		colour_rgb_generate256();
112 
113 	colour = 16;
114 	lowest = UINT_MAX;
115 	for (i = 0; i < 240; i++) {
116 		distance = colour_rgb_distance(&colour_rgb_256[i], rgb);
117 		if (distance < lowest) {
118 			lowest = distance;
119 			colour = 16 + i;
120 		}
121 	}
122 	return (colour);
123 }
124 
125 /* Set grid cell foreground colour. */
126 void
127 colour_set_fg(struct grid_cell *gc, int c)
128 {
129 	if (c & 0x100)
130 		gc->flags |= GRID_FLAG_FG256;
131 	gc->fg = c;
132 }
133 
134 /* Set grid cell background colour. */
135 void
136 colour_set_bg(struct grid_cell *gc, int c)
137 {
138 	if (c & 0x100)
139 		gc->flags |= GRID_FLAG_BG256;
140 	gc->bg = c;
141 }
142 
143 /* Convert colour to a string. */
144 const char *
145 colour_tostring(int c)
146 {
147 	static char	s[32];
148 
149 	if (c & 0x100) {
150 		xsnprintf(s, sizeof s, "colour%u", c & ~0x100);
151 		return (s);
152 	}
153 
154 	switch (c) {
155 	case 0:
156 		return ("black");
157 	case 1:
158 		return ("red");
159 	case 2:
160 		return ("green");
161 	case 3:
162 		return ("yellow");
163 	case 4:
164 		return ("blue");
165 	case 5:
166 		return ("magenta");
167 	case 6:
168 		return ("cyan");
169 	case 7:
170 		return ("white");
171 	case 8:
172 		return ("default");
173 	case 90:
174 		return ("brightblack");
175 	case 91:
176 		return ("brightred");
177 	case 92:
178 		return ("brightgreen");
179 	case 93:
180 		return ("brightyellow");
181 	case 94:
182 		return ("brightblue");
183 	case 95:
184 		return ("brightmagenta");
185 	case 96:
186 		return ("brightcyan");
187 	case 97:
188 		return ("brightwhite");
189 	}
190 	return (NULL);
191 }
192 
193 /* Convert colour from string. */
194 int
195 colour_fromstring(const char *s)
196 {
197 	const char		*errstr;
198 	const char		*cp;
199 	struct colour_rgb	 rgb;
200 	int			 n;
201 
202 	if (*s == '#' && strlen(s) == 7) {
203 		for (cp = s + 1; isxdigit((u_char) *cp); cp++)
204 			;
205 		if (*cp != '\0')
206 			return (-1);
207 		n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &rgb.r, &rgb.g, &rgb.b);
208 		if (n != 3)
209 			return (-1);
210 		return (colour_rgb_find(&rgb) | 0x100);
211 	}
212 
213 	if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) {
214 		n = strtonum(s + (sizeof "colour") - 1, 0, 255, &errstr);
215 		if (errstr != NULL)
216 			return (-1);
217 		return (n | 0x100);
218 	}
219 
220 	if (strcasecmp(s, "black") == 0 || (s[0] == '0' && s[1] == '\0'))
221 		return (0);
222 	if (strcasecmp(s, "red") == 0 || (s[0] == '1' && s[1] == '\0'))
223 		return (1);
224 	if (strcasecmp(s, "green") == 0 || (s[0] == '2' && s[1] == '\0'))
225 		return (2);
226 	if (strcasecmp(s, "yellow") == 0 || (s[0] == '3' && s[1] == '\0'))
227 		return (3);
228 	if (strcasecmp(s, "blue") == 0 || (s[0] == '4' && s[1] == '\0'))
229 		return (4);
230 	if (strcasecmp(s, "magenta") == 0 || (s[0] == '5' && s[1] == '\0'))
231 		return (5);
232 	if (strcasecmp(s, "cyan") == 0 || (s[0] == '6' && s[1] == '\0'))
233 		return (6);
234 	if (strcasecmp(s, "white") == 0 || (s[0] == '7' && s[1] == '\0'))
235 		return (7);
236 	if (strcasecmp(s, "default") == 0 || (s[0] == '8' && s[1] == '\0'))
237 		return (8);
238 	if (strcasecmp(s, "brightblack") == 0 ||
239 	    (s[0] == '9' && s[1] == '0' && s[1] == '\0'))
240 		return (90);
241 	if (strcasecmp(s, "brightred") == 0 ||
242 	    (s[0] == '9' && s[1] == '1' && s[1] == '\0'))
243 		return (91);
244 	if (strcasecmp(s, "brightgreen") == 0 ||
245 	    (s[0] == '9' && s[1] == '2' && s[1] == '\0'))
246 		return (92);
247 	if (strcasecmp(s, "brightyellow") == 0 ||
248 	    (s[0] == '9' && s[1] == '3' && s[1] == '\0'))
249 		return (93);
250 	if (strcasecmp(s, "brightblue") == 0 ||
251 	    (s[0] == '9' && s[1] == '4' && s[1] == '\0'))
252 		return (94);
253 	if (strcasecmp(s, "brightmagenta") == 0 ||
254 	    (s[0] == '9' && s[1] == '5' && s[1] == '\0'))
255 		return (95);
256 	if (strcasecmp(s, "brightcyan") == 0 ||
257 	    (s[0] == '9' && s[1] == '6' && s[1] == '\0'))
258 		return (96);
259 	if (strcasecmp(s, "brightwhite") == 0 ||
260 	    (s[0] == '9' && s[1] == '7' && s[1] == '\0'))
261 		return (97);
262 	return (-1);
263 }
264 
265 /* Convert 256 colour palette to 16. */
266 u_char
267 colour_256to16(u_char c)
268 {
269 	static const u_char table[256] = {
270 		 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
271 		 0,  4,  4,  4, 12, 12,  2,  6,  4,  4, 12, 12,  2,  2,  6,  4,
272 		12, 12,  2,  2,  2,  6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
273 		10, 10, 10, 14,  1,  5,  4,  4, 12, 12,  3,  8,  4,  4, 12, 12,
274 		 2,  2,  6,  4, 12, 12,  2,  2,  2,  6, 12, 12, 10, 10, 10, 10,
275 		14, 12, 10, 10, 10, 10, 10, 14,  1,  1,  5,  4, 12, 12,  1,  1,
276 		 5,  4, 12, 12,  3,  3,  8,  4, 12, 12,  2,  2,  2,  6, 12, 12,
277 		10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,  1,  1,  1,  5,
278 		12, 12,  1,  1,  1,  5, 12, 12,  1,  1,  1,  5, 12, 12,  3,  3,
279 		 3,  7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
280 		 9,  9,  9,  9, 13, 12,  9,  9,  9,  9, 13, 12,  9,  9,  9,  9,
281 		13, 12,  9,  9,  9,  9, 13, 12, 11, 11, 11, 11,  7, 12, 10, 10,
282 		10, 10, 10, 14,  9,  9,  9,  9,  9, 13,  9,  9,  9,  9,  9, 13,
283 		 9,  9,  9,  9,  9, 13,  9,  9,  9,  9,  9, 13,  9,  9,  9,  9,
284 		 9, 13, 11, 11, 11, 11, 11, 15,  0,  0,  0,  0,  0,  0,  8,  8,
285 		 8,  8,  8,  8,  7,  7,  7,  7,  7,  7, 15, 15, 15, 15, 15, 15
286 	};
287 
288 	return (table[c]);
289 }
290