Home | History | Annotate | Line # | Download | only in dist
colour.c revision 1.8
      1  1.5  christos /* $OpenBSD$ */
      2  1.1      jmmv 
      3  1.1      jmmv /*
      4  1.6  christos  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott (at) gmail.com>
      5  1.7  christos  * Copyright (c) 2016 Avi Halachmi <avihpit (at) yahoo.com>
      6  1.1      jmmv  *
      7  1.1      jmmv  * Permission to use, copy, modify, and distribute this software for any
      8  1.1      jmmv  * purpose with or without fee is hereby granted, provided that the above
      9  1.1      jmmv  * copyright notice and this permission notice appear in all copies.
     10  1.1      jmmv  *
     11  1.1      jmmv  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  1.1      jmmv  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  1.1      jmmv  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  1.1      jmmv  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  1.1      jmmv  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     16  1.1      jmmv  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     17  1.1      jmmv  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  1.1      jmmv  */
     19  1.1      jmmv 
     20  1.1      jmmv #include <sys/types.h>
     21  1.1      jmmv 
     22  1.2        he #include <ctype.h>
     23  1.1      jmmv #include <stdlib.h>
     24  1.1      jmmv #include <string.h>
     25  1.1      jmmv 
     26  1.1      jmmv #include "tmux.h"
     27  1.1      jmmv 
     28  1.7  christos static int
     29  1.7  christos colour_dist_sq(int R, int G, int B, int r, int g, int b)
     30  1.7  christos {
     31  1.7  christos 	return ((R - r) * (R - r) + (G - g) * (G - g) + (B - b) * (B - b));
     32  1.7  christos }
     33  1.2        he 
     34  1.7  christos static int
     35  1.7  christos colour_to_6cube(int v)
     36  1.2        he {
     37  1.7  christos 	if (v < 48)
     38  1.7  christos 		return (0);
     39  1.7  christos 	if (v < 114)
     40  1.5  christos 		return (1);
     41  1.7  christos 	return ((v - 35) / 40);
     42  1.2        he }
     43  1.2        he 
     44  1.7  christos /*
     45  1.7  christos  * Convert an RGB triplet to the xterm(1) 256 colour palette.
     46  1.7  christos  *
     47  1.7  christos  * xterm provides a 6x6x6 colour cube (16 - 231) and 24 greys (232 - 255). We
     48  1.7  christos  * map our RGB colour to the closest in the cube, also work out the closest
     49  1.7  christos  * grey, and use the nearest of the two.
     50  1.7  christos  *
     51  1.7  christos  * Note that the xterm has much lower resolution for darker colours (they are
     52  1.7  christos  * not evenly spread out), so our 6 levels are not evenly spread: 0x0, 0x5f
     53  1.7  christos  * (95), 0x87 (135), 0xaf (175), 0xd7 (215) and 0xff (255). Greys are more
     54  1.7  christos  * evenly spread (8, 18, 28 ... 238).
     55  1.7  christos  */
     56  1.2        he int
     57  1.5  christos colour_find_rgb(u_char r, u_char g, u_char b)
     58  1.2        he {
     59  1.7  christos 	static const int	q2c[6] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff };
     60  1.7  christos 	int			qr, qg, qb, cr, cg, cb, d, idx;
     61  1.7  christos 	int			grey_avg, grey_idx, grey;
     62  1.7  christos 
     63  1.7  christos 	/* Map RGB to 6x6x6 cube. */
     64  1.7  christos 	qr = colour_to_6cube(r); cr = q2c[qr];
     65  1.7  christos 	qg = colour_to_6cube(g); cg = q2c[qg];
     66  1.7  christos 	qb = colour_to_6cube(b); cb = q2c[qb];
     67  1.7  christos 
     68  1.7  christos 	/* If we have hit the colour exactly, return early. */
     69  1.7  christos 	if (cr == r && cg == g && cb == b)
     70  1.7  christos 		return ((16 + (36 * qr) + (6 * qg) + qb) | COLOUR_FLAG_256);
     71  1.7  christos 
     72  1.7  christos 	/* Work out the closest grey (average of RGB). */
     73  1.7  christos 	grey_avg = (r + g + b) / 3;
     74  1.7  christos 	if (grey_avg > 238)
     75  1.7  christos 		grey_idx = 23;
     76  1.7  christos 	else
     77  1.7  christos 		grey_idx = (grey_avg - 3) / 10;
     78  1.7  christos 	grey = 8 + (10 * grey_idx);
     79  1.7  christos 
     80  1.7  christos 	/* Is grey or 6x6x6 colour closest? */
     81  1.7  christos 	d = colour_dist_sq(cr, cg, cb, r, g, b);
     82  1.7  christos 	if (colour_dist_sq(grey, grey, grey, r, g, b) < d)
     83  1.7  christos 		idx = 232 + grey_idx;
     84  1.7  christos 	else
     85  1.7  christos 		idx = 16 + (36 * qr) + (6 * qg) + qb;
     86  1.7  christos 	return (idx | COLOUR_FLAG_256);
     87  1.2        he }
     88  1.2        he 
     89  1.7  christos /* Join RGB into a colour. */
     90  1.7  christos int
     91  1.7  christos colour_join_rgb(u_char r, u_char g, u_char b)
     92  1.1      jmmv {
     93  1.7  christos 	return ((((int)((r) & 0xff)) << 16) |
     94  1.7  christos 	    (((int)((g) & 0xff)) << 8) |
     95  1.7  christos 	    (((int)((b) & 0xff))) | COLOUR_FLAG_RGB);
     96  1.1      jmmv }
     97  1.1      jmmv 
     98  1.7  christos /* Split colour into RGB. */
     99  1.1      jmmv void
    100  1.7  christos colour_split_rgb(int c, u_char *r, u_char *g, u_char *b)
    101  1.1      jmmv {
    102  1.7  christos 	*r = (c >> 16) & 0xff;
    103  1.7  christos 	*g = (c >> 8) & 0xff;
    104  1.7  christos 	*b = c & 0xff;
    105  1.1      jmmv }
    106  1.1      jmmv 
    107  1.2        he /* Convert colour to a string. */
    108  1.1      jmmv const char *
    109  1.1      jmmv colour_tostring(int c)
    110  1.1      jmmv {
    111  1.1      jmmv 	static char	s[32];
    112  1.7  christos 	u_char		r, g, b;
    113  1.7  christos 
    114  1.7  christos 	if (c & COLOUR_FLAG_RGB) {
    115  1.7  christos 		colour_split_rgb(c, &r, &g, &b);
    116  1.7  christos 		xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b);
    117  1.7  christos 		return (s);
    118  1.7  christos 	}
    119  1.1      jmmv 
    120  1.7  christos 	if (c & COLOUR_FLAG_256) {
    121  1.7  christos 		xsnprintf(s, sizeof s, "colour%u", c & 0xff);
    122  1.1      jmmv 		return (s);
    123  1.1      jmmv 	}
    124  1.1      jmmv 
    125  1.1      jmmv 	switch (c) {
    126  1.1      jmmv 	case 0:
    127  1.1      jmmv 		return ("black");
    128  1.1      jmmv 	case 1:
    129  1.1      jmmv 		return ("red");
    130  1.1      jmmv 	case 2:
    131  1.1      jmmv 		return ("green");
    132  1.1      jmmv 	case 3:
    133  1.1      jmmv 		return ("yellow");
    134  1.1      jmmv 	case 4:
    135  1.1      jmmv 		return ("blue");
    136  1.1      jmmv 	case 5:
    137  1.1      jmmv 		return ("magenta");
    138  1.1      jmmv 	case 6:
    139  1.1      jmmv 		return ("cyan");
    140  1.1      jmmv 	case 7:
    141  1.1      jmmv 		return ("white");
    142  1.1      jmmv 	case 8:
    143  1.1      jmmv 		return ("default");
    144  1.8  christos 	case 9:
    145  1.8  christos 		return ("terminal");
    146  1.4  christos 	case 90:
    147  1.4  christos 		return ("brightblack");
    148  1.4  christos 	case 91:
    149  1.4  christos 		return ("brightred");
    150  1.4  christos 	case 92:
    151  1.4  christos 		return ("brightgreen");
    152  1.4  christos 	case 93:
    153  1.4  christos 		return ("brightyellow");
    154  1.4  christos 	case 94:
    155  1.4  christos 		return ("brightblue");
    156  1.4  christos 	case 95:
    157  1.4  christos 		return ("brightmagenta");
    158  1.4  christos 	case 96:
    159  1.4  christos 		return ("brightcyan");
    160  1.4  christos 	case 97:
    161  1.4  christos 		return ("brightwhite");
    162  1.1      jmmv 	}
    163  1.8  christos 	return ("invalid");
    164  1.1      jmmv }
    165  1.1      jmmv 
    166  1.2        he /* Convert colour from string. */
    167  1.1      jmmv int
    168  1.1      jmmv colour_fromstring(const char *s)
    169  1.1      jmmv {
    170  1.5  christos 	const char	*errstr;
    171  1.5  christos 	const char	*cp;
    172  1.5  christos 	int		 n;
    173  1.5  christos 	u_char		 r, g, b;
    174  1.2        he 
    175  1.2        he 	if (*s == '#' && strlen(s) == 7) {
    176  1.2        he 		for (cp = s + 1; isxdigit((u_char) *cp); cp++)
    177  1.2        he 			;
    178  1.2        he 		if (*cp != '\0')
    179  1.2        he 			return (-1);
    180  1.5  christos 		n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b);
    181  1.2        he 		if (n != 3)
    182  1.2        he 			return (-1);
    183  1.7  christos 		return (colour_join_rgb(r, g, b));
    184  1.2        he 	}
    185  1.1      jmmv 
    186  1.1      jmmv 	if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) {
    187  1.1      jmmv 		n = strtonum(s + (sizeof "colour") - 1, 0, 255, &errstr);
    188  1.1      jmmv 		if (errstr != NULL)
    189  1.1      jmmv 			return (-1);
    190  1.7  christos 		return (n | COLOUR_FLAG_256);
    191  1.1      jmmv 	}
    192  1.1      jmmv 
    193  1.8  christos 	if (strcasecmp(s, "default") == 0)
    194  1.8  christos 		return (8);
    195  1.8  christos 	if (strcasecmp(s, "terminal") == 0)
    196  1.8  christos 		return (9);
    197  1.8  christos 
    198  1.5  christos 	if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0)
    199  1.1      jmmv 		return (0);
    200  1.5  christos 	if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0)
    201  1.1      jmmv 		return (1);
    202  1.5  christos 	if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0)
    203  1.1      jmmv 		return (2);
    204  1.5  christos 	if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0)
    205  1.1      jmmv 		return (3);
    206  1.5  christos 	if (strcasecmp(s, "blue") == 0 || strcmp(s, "4") == 0)
    207  1.1      jmmv 		return (4);
    208  1.5  christos 	if (strcasecmp(s, "magenta") == 0 || strcmp(s, "5") == 0)
    209  1.1      jmmv 		return (5);
    210  1.5  christos 	if (strcasecmp(s, "cyan") == 0 || strcmp(s, "6") == 0)
    211  1.1      jmmv 		return (6);
    212  1.5  christos 	if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0)
    213  1.1      jmmv 		return (7);
    214  1.5  christos 	if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0)
    215  1.4  christos 		return (90);
    216  1.5  christos 	if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0)
    217  1.4  christos 		return (91);
    218  1.5  christos 	if (strcasecmp(s, "brightgreen") == 0 || strcmp(s, "92") == 0)
    219  1.4  christos 		return (92);
    220  1.5  christos 	if (strcasecmp(s, "brightyellow") == 0 || strcmp(s, "93") == 0)
    221  1.4  christos 		return (93);
    222  1.5  christos 	if (strcasecmp(s, "brightblue") == 0 || strcmp(s, "94") == 0)
    223  1.4  christos 		return (94);
    224  1.5  christos 	if (strcasecmp(s, "brightmagenta") == 0 || strcmp(s, "95") == 0)
    225  1.4  christos 		return (95);
    226  1.5  christos 	if (strcasecmp(s, "brightcyan") == 0 || strcmp(s, "96") == 0)
    227  1.4  christos 		return (96);
    228  1.5  christos 	if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0)
    229  1.4  christos 		return (97);
    230  1.1      jmmv 	return (-1);
    231  1.1      jmmv }
    232  1.1      jmmv 
    233  1.2        he /* Convert 256 colour palette to 16. */
    234  1.1      jmmv u_char
    235  1.1      jmmv colour_256to16(u_char c)
    236  1.1      jmmv {
    237  1.1      jmmv 	static const u_char table[256] = {
    238  1.1      jmmv 		 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
    239  1.1      jmmv 		 0,  4,  4,  4, 12, 12,  2,  6,  4,  4, 12, 12,  2,  2,  6,  4,
    240  1.1      jmmv 		12, 12,  2,  2,  2,  6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
    241  1.1      jmmv 		10, 10, 10, 14,  1,  5,  4,  4, 12, 12,  3,  8,  4,  4, 12, 12,
    242  1.1      jmmv 		 2,  2,  6,  4, 12, 12,  2,  2,  2,  6, 12, 12, 10, 10, 10, 10,
    243  1.1      jmmv 		14, 12, 10, 10, 10, 10, 10, 14,  1,  1,  5,  4, 12, 12,  1,  1,
    244  1.1      jmmv 		 5,  4, 12, 12,  3,  3,  8,  4, 12, 12,  2,  2,  2,  6, 12, 12,
    245  1.1      jmmv 		10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,  1,  1,  1,  5,
    246  1.1      jmmv 		12, 12,  1,  1,  1,  5, 12, 12,  1,  1,  1,  5, 12, 12,  3,  3,
    247  1.1      jmmv 		 3,  7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
    248  1.1      jmmv 		 9,  9,  9,  9, 13, 12,  9,  9,  9,  9, 13, 12,  9,  9,  9,  9,
    249  1.1      jmmv 		13, 12,  9,  9,  9,  9, 13, 12, 11, 11, 11, 11,  7, 12, 10, 10,
    250  1.1      jmmv 		10, 10, 10, 14,  9,  9,  9,  9,  9, 13,  9,  9,  9,  9,  9, 13,
    251  1.1      jmmv 		 9,  9,  9,  9,  9, 13,  9,  9,  9,  9,  9, 13,  9,  9,  9,  9,
    252  1.1      jmmv 		 9, 13, 11, 11, 11, 11, 11, 15,  0,  0,  0,  0,  0,  0,  8,  8,
    253  1.1      jmmv 		 8,  8,  8,  8,  7,  7,  7,  7,  7,  7, 15, 15, 15, 15, 15, 15
    254  1.1      jmmv 	};
    255  1.1      jmmv 
    256  1.1      jmmv 	return (table[c]);
    257  1.1      jmmv }
    258