1 /* radare - LGPL - Copyright 2013-2020 - pancake, xarkes */
2 /* ansi 256 color extension for r_cons */
3 /* https://en.wikipedia.org/wiki/ANSI_color */
4 
5 #include <r_cons.h>
6 
7 int color_table[256] = { 0 };
8 int value_range[6] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
9 
init_color_table(void)10 static void init_color_table(void) {
11 	int i, r, g, b;
12 	// ansi colors
13 	color_table[0] = 0x000000;
14 	color_table[1] = 0x800000;
15 	color_table[2] = 0x008000;
16 	color_table[3] = 0x808000;
17 	color_table[4] = 0x000080;
18 	color_table[5] = 0x800080;
19 	color_table[6] = 0x008080;
20 	color_table[7] = 0xc0c0c0;
21 	color_table[8] = 0x808080;
22 	color_table[9] = 0xff0000;
23 	color_table[10] = 0x00ff00;
24 	color_table[11] = 0xffff00;
25 	color_table[12] = 0x0000ff;
26 	color_table[13] = 0xff00ff;
27 	color_table[14] = 0x00ffff;
28 	color_table[15] = 0xffffff;
29 	// color palette
30 	for (i = 0; i < 216; i++) {
31 		r = value_range[(i / 36) % 6];
32 		g = value_range[(i / 6) % 6];
33 		b = value_range[i % 6];
34 		color_table[i + 16] = ((r << 16) & 0xffffff) +
35 			((g << 8) & 0xffff) + (b & 0xff);
36 	}
37 	// grayscale
38 	for (i = 0; i < 24; i++) {
39 		r = 8 + (i * 10);
40 		color_table[i + 232] = ((r << 16) & 0xffffff) +
41 			((r << 8) & 0xffff) + (r & 0xff);
42 	}
43 }
44 
__lookup_rgb(int r,int g,int b)45 static int __lookup_rgb(int r, int g, int b) {
46 	int i, color = (r << 16) + (g << 8) + b;
47 	// lookup extended colors only, coz non-extended can be changed by users.
48 	for (i = 16; i < 256; i++) {
49 		if (color_table[i] == color) {
50 			return i;
51 		}
52 	}
53 	return -1;
54 }
55 
__approximate_rgb(int r,int g,int b)56 static ut32 __approximate_rgb(int r, int g, int b) {
57 	bool grey = (r > 0 && r < 255 && r == g && r == b);
58 	if (grey) {
59 		return 232 + (int)((double)r / (255 / 24.1));
60 	}
61 #if 0
62 	const double M = 16;
63 	double R = r;
64 	double G = g;
65 	double B = b;
66 	R = R /256 * 216;
67 	R /= 256 * 216;
68 	R /= 256 * 216;
69 	r = R = R_DIM (R / 16, 0, 16);
70 	g = G = R_DIM (G / 16, 0, 16);
71 	b = B = R_DIM (B / 16, 0, 16);
72 	r &= 0xff;
73 	g &= 0xff;
74 	b &= 0xff;
75 	return (ut32)((G * M * M)  + (g * M) + b) + 16;
76 #else
77 	const int k = (256.0 / 6);
78 	r = R_DIM (r / k, 0, 6);
79 	g = R_DIM (g / k, 0, 6);
80 	b = R_DIM (b / k, 0, 6);
81 	return 16 + (r * 36) + (g * 6) + b;
82 #endif
83 }
84 
rgb(int r,int g,int b)85 static int rgb(int r, int g, int b) {
86 	int c = __lookup_rgb (r, g, b);
87 	if (c == -1) {
88 		return __approximate_rgb (r, g, b);
89 	}
90 	return c;
91 }
92 
__unrgb(int color,int * r,int * g,int * b)93 static void __unrgb(int color, int *r, int *g, int *b) {
94 	if (color < 0 || color > 255) {
95 		*r = *g = *b = 0;
96 	} else {
97 		int rgb = color_table[color];
98 		*r = (rgb >> 16) & 0xff;
99 		*g = (rgb >> 8) & 0xff;
100 		*b = rgb & 0xff;
101 	}
102 }
103 
r_cons_rgb_init(void)104 R_API void r_cons_rgb_init(void) {
105 	if (color_table[255] == 0) {
106 		init_color_table ();
107 	}
108 }
109 
110 /* Parse an ANSI code string into RGB values -- Used by HTML filter only */
r_cons_rgb_parse(const char * p,ut8 * r,ut8 * g,ut8 * b,ut8 * a)111 R_API int r_cons_rgb_parse(const char *p, ut8 *r, ut8 *g, ut8 *b, ut8 *a) {
112 	const char *q = 0;
113 	ut8 isbg = 0, bold = 127;
114 	if (!p) {
115 		return 0;
116 	}
117 	if (*p == 0x1b) {
118 		p++;
119 		if (!*p) {
120 			return 0;
121 		}
122 	}
123 	if (*p == '[') {
124 		p++;
125 		if (!*p) {
126 			return 0;
127 		}
128 	}
129 	// here, p should be just after the '['
130 	switch (*p) {
131 	case '1':
132 		bold = 255;
133 		if (!p[1] || !p[2]) {
134 			return 0;
135 		}
136 		p += 2;
137 		break;
138 	case '3': isbg = 0; break;
139 	case '4': isbg = 1; break;
140 	}
141 #define SETRGB(x,y,z) if (r) *r = (x); if (g) *g = (y); if (b) *b = (z)
142 	if (bold != 255 && strchr (p, ';')) {
143 		if (!p[0] || !p[1] || !p[2]) {
144 			return 0;
145 		}
146 		if (p[3] == '5')  { // \x1b[%d;5;%dm is 256 colors
147 			int x, y, z;
148 			if (!p[3] || !p[4]) {
149 				return 0;
150 			}
151 			int n = atoi (p + 5);
152 			__unrgb (n, &x, &y, &z);
153 			SETRGB (x, y, z);
154 		} else { // 16M colors (truecolor)
155 			/* complex rgb */
156 			if (!p[3] || !p[4]) {
157 				return 0;
158 			}
159 			p += 5;
160 			if (r) {
161 				*r = atoi (p);
162 			}
163 			q = strchr (p, ';');
164 			if (!q) {
165 				return 0;
166 			}
167 			if (g) {
168 				*g = atoi (q + 1);
169 			}
170 			q = strchr (q + 1, ';');
171 			if (!q) {
172 				return 0;
173 			}
174 			if (b) {
175 				*b = atoi (q + 1);
176 			}
177 		}
178 		return 1;
179 	} else {
180 		/* plain ansi escape codes */
181 		if (a) {
182 			*a = isbg;
183 		}
184 		if (!*p) {
185 			return 0;
186 		}
187 		switch (p[1]) {
188 		case '0': SETRGB (0, 0, 0); break;
189 		case '1': SETRGB (bold, 0, 0); break;
190 		case '2': SETRGB (0, bold, 0); break;
191 		case '3': SETRGB (bold, bold, 0); break;
192 		case '4': SETRGB (0, 0, bold); break;
193 		case '5': SETRGB (bold, 0, bold); break;
194 		case '6': SETRGB (0, bold, bold); break;
195 		case '7': SETRGB (bold, bold, bold); break;
196 		}
197 	}
198 	return 1;
199 }
200 
r_cons_rgb_str_off(char * outstr,size_t sz,ut64 off)201 R_API char *r_cons_rgb_str_off(char *outstr, size_t sz, ut64 off) {
202 	RColor rc = RColor_BLACK;
203 	rc.id16 = -1;
204 	rc.r = (off >> 2) & 0xff;
205 	rc.g = (off >> 6) & 0xff;
206 	rc.b = (off >> 12) & 0xff;
207 	return r_cons_rgb_str (outstr, sz, &rc);
208 }
209 
210 /* Compute color string depending on cons->color */
r_cons_rgb_gen(RConsColorMode mode,char * outstr,size_t sz,ut8 attr,ut8 a,ut8 r,ut8 g,ut8 b,st8 id16)211 static void r_cons_rgb_gen(RConsColorMode mode, char *outstr, size_t sz, ut8 attr, ut8 a, ut8 r, ut8 g, ut8 b,
212                            st8 id16) {
213 	ut8 fgbg = (a == ALPHA_BG)? 48: 38; // ANSI codes for Background/Foreground
214 
215 	if (sz < 4) { // must have at least room for "<esc>[m\0"
216 		if (sz > 0) {
217 			outstr[0] = '\0';
218 		}
219 		return;
220 	}
221 
222 	size_t i = 2;
223 	outstr[0] = '\x1b';
224 	outstr[1] = '[';
225 	for (; attr; attr &= attr - 1) {
226 		if (sz < i + 4) { // must have at least room for e.g. "1;m\0"
227 			outstr[0] = '\0';
228 			return;
229 		}
230 		switch (attr & -attr) {
231 		case R_CONS_ATTR_BOLD: outstr[i] = '1'; break;
232 		case R_CONS_ATTR_DIM: outstr[i] = '2'; break;
233 		case R_CONS_ATTR_ITALIC: outstr[i] = '3'; break;
234 		case R_CONS_ATTR_UNDERLINE: outstr[i] = '4'; break;
235 		case R_CONS_ATTR_BLINK: outstr[i] = '5'; break;
236 		}
237 		outstr[i + 1] = ';';
238 		i += 2;
239 	}
240 
241 	int written = -1;
242 	switch (mode) {
243 	case COLOR_MODE_256: // 256 color palette
244 		written = snprintf (outstr + i, sz - i, "%d;5;%dm", fgbg, rgb (r, g, b));
245 		break;
246 	case COLOR_MODE_16M: // 16M (truecolor)
247 		written = snprintf (outstr + i, sz - i, "%d;2;%d;%d;%dm", fgbg, r, g, b);
248 		break;
249 	case COLOR_MODE_16: { // ansi 16 colors
250 		ut8 bright, c;
251 		fgbg -= 8;
252 		if (id16 >= 0 && id16 <= 15) {
253 			c = id16 % 8;
254 			bright = id16 >= 8 ? 60 : 0;
255 		} else {
256 			bright = (r == 0x80 && g == 0x80 && b == 0x80) ? 53
257 			         : (r == 0xff || g == 0xff || b == 0xff) ? 60 : 0;  // eco bright-specific
258 			if (r == g && g == b) {
259 				r = (r > 0x7f) ? 1 : 0;
260 				g = (g > 0x7f) ? 1 : 0;
261 				b = (b > 0x7f) ? 1 : 0;
262 			} else {
263 				ut8 k = (r + g + b) / 3;
264 				r = (r >= k) ? 1 : 0;
265 				g = (g >= k) ? 1 : 0;
266 				b = (b >= k) ? 1 : 0;
267 			}
268 			c = (r ? 1 : 0) + (g ? (b ? 6 : 2) : (b ? 4 : 0));
269 		}
270 		written = snprintf (outstr + i, sz - i, "%dm", fgbg + bright + c);
271 		break;
272 	}
273 	default:
274 		break;
275 	}
276 
277 	if (written < 0 || written >= sz - i) {
278 		outstr[0] = '\0';
279 	}
280 }
281 
282 /* Return the computed color string for the specified color in the specified mode */
r_cons_rgb_str_mode(RConsColorMode mode,char * outstr,size_t sz,RColor * rcolor)283 R_API char *r_cons_rgb_str_mode(RConsColorMode mode, char *outstr, size_t sz, RColor *rcolor) {
284 	if (!rcolor) {
285 		return NULL;
286 	}
287 	if (!outstr) {
288 		sz = 64;
289 		outstr = calloc (sz, 1);
290 	}
291 	*outstr = 0;
292 	if (rcolor->a == ALPHA_RESET) {
293 		strcpy (outstr, Color_RESET);
294 		return outstr;
295 	}
296 	// If the color handles both foreground and background, also add background
297 	if (rcolor->a == ALPHA_FGBG) {
298 		r_cons_rgb_gen (mode, outstr, sz, 0, ALPHA_BG, rcolor->r2, rcolor->g2, rcolor->b2, rcolor->id16);
299 	}
300 	// APPEND
301 	size_t len = strlen (outstr);
302 	r_cons_rgb_gen (mode, outstr + len, sz - len, rcolor->attr, rcolor->a, rcolor->r, rcolor->g, rcolor->b,
303 	                rcolor->id16);
304 
305 	return outstr;
306 }
307 
308 /* Return the computed color string for the specified color */
r_cons_rgb_str(char * outstr,size_t sz,RColor * rcolor)309 R_API char *r_cons_rgb_str(char *outstr, size_t sz, RColor *rcolor) {
310 	return r_cons_rgb_str_mode (r_cons_singleton ()->context->color_mode, outstr, sz, rcolor);
311 }
312 
r_cons_rgb_tostring(ut8 r,ut8 g,ut8 b)313 R_API char *r_cons_rgb_tostring(ut8 r, ut8 g, ut8 b) {
314 	const char *str = NULL;
315 	if (r == 0x00 && g == b && g == 0) {
316 		str = "black";
317 	}
318 	if (r == 0xff && g == b && g == 0xff) {
319 		str = "white";
320 	}
321 	if (r == 0xff && g == b && g == 0) {
322 		str = "red";
323 	}
324 	if (g == 0xff && r == b && r == 0) {
325 		str = "green";
326 	}
327 	if (b == 0xff && r == g && r == 0) {
328 		str = "blue";
329 	}
330 	if (r == 0xff && g == 0xff && b == 0x00) {
331 		str = "yellow";
332 	}
333 	if (r == 0x00 && g == 0xff && b == 0xff) {
334 		str = "cyan";
335 	}
336 	if (r == 0xff && g == 0x00 && b == 0xff) {
337 		str = "magenta";
338 	}
339 	return str? strdup (str) : r_str_newf ("#%02x%02x%02x", r, g, b);
340 }
341