GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/colour.c Lines: 12 106 11.3 %
Date: 2017-11-07 Branches: 11 118 9.3 %

Line Branch Exec Source
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
108
	const char	*errstr;
169
	const char	*cp;
170
	int		 n;
171
54
	u_char		 r, g, b;
172
173

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

84
	if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0)
192
24
		return (0);
193

60
	if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0)
194
		return (1);
195

48
	if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0)
196
12
		return (2);
197

18
	if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0)
198
18
		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
54
}
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
}