colour.c revision 1.8
1/* $OpenBSD$ */
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
28static int
29colour_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
34static int
35colour_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 */
56int
57colour_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. */
90int
91colour_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. */
99void
100colour_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. */
108const char *
109colour_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. */
167int
168colour_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
193	if (strcasecmp(s, "default") == 0)
194		return (8);
195	if (strcasecmp(s, "terminal") == 0)
196		return (9);
197
198	if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0)
199		return (0);
200	if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0)
201		return (1);
202	if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0)
203		return (2);
204	if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0)
205		return (3);
206	if (strcasecmp(s, "blue") == 0 || strcmp(s, "4") == 0)
207		return (4);
208	if (strcasecmp(s, "magenta") == 0 || strcmp(s, "5") == 0)
209		return (5);
210	if (strcasecmp(s, "cyan") == 0 || strcmp(s, "6") == 0)
211		return (6);
212	if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0)
213		return (7);
214	if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0)
215		return (90);
216	if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0)
217		return (91);
218	if (strcasecmp(s, "brightgreen") == 0 || strcmp(s, "92") == 0)
219		return (92);
220	if (strcasecmp(s, "brightyellow") == 0 || strcmp(s, "93") == 0)
221		return (93);
222	if (strcasecmp(s, "brightblue") == 0 || strcmp(s, "94") == 0)
223		return (94);
224	if (strcasecmp(s, "brightmagenta") == 0 || strcmp(s, "95") == 0)
225		return (95);
226	if (strcasecmp(s, "brightcyan") == 0 || strcmp(s, "96") == 0)
227		return (96);
228	if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0)
229		return (97);
230	return (-1);
231}
232
233/* Convert 256 colour palette to 16. */
234u_char
235colour_256to16(u_char c)
236{
237	static const u_char table[256] = {
238		 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
239		 0,  4,  4,  4, 12, 12,  2,  6,  4,  4, 12, 12,  2,  2,  6,  4,
240		12, 12,  2,  2,  2,  6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
241		10, 10, 10, 14,  1,  5,  4,  4, 12, 12,  3,  8,  4,  4, 12, 12,
242		 2,  2,  6,  4, 12, 12,  2,  2,  2,  6, 12, 12, 10, 10, 10, 10,
243		14, 12, 10, 10, 10, 10, 10, 14,  1,  1,  5,  4, 12, 12,  1,  1,
244		 5,  4, 12, 12,  3,  3,  8,  4, 12, 12,  2,  2,  2,  6, 12, 12,
245		10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,  1,  1,  1,  5,
246		12, 12,  1,  1,  1,  5, 12, 12,  1,  1,  1,  5, 12, 12,  3,  3,
247		 3,  7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
248		 9,  9,  9,  9, 13, 12,  9,  9,  9,  9, 13, 12,  9,  9,  9,  9,
249		13, 12,  9,  9,  9,  9, 13, 12, 11, 11, 11, 11,  7, 12, 10, 10,
250		10, 10, 10, 14,  9,  9,  9,  9,  9, 13,  9,  9,  9,  9,  9, 13,
251		 9,  9,  9,  9,  9, 13,  9,  9,  9,  9,  9, 13,  9,  9,  9,  9,
252		 9, 13, 11, 11, 11, 11, 11, 15,  0,  0,  0,  0,  0,  0,  8,  8,
253		 8,  8,  8,  8,  7,  7,  7,  7,  7,  7, 15, 15, 15, 15, 15, 15
254	};
255
256	return (table[c]);
257}
258