Bug 1082: mem_free_set in abort_preloading
[elinks.git] / src / terminal / color.c
blob9b13ac6d1817c61ba1735728232b0689631ee532
1 /** Terminal color composing.
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include "elinks.h"
10 #include "terminal/color.h"
11 #include "terminal/draw.h"
12 #include "util/color.h"
13 #include "util/error.h"
15 struct rgb {
16 unsigned char r, g, b;
17 unsigned char pad;
20 #include "terminal/palette.inc"
22 struct rgb_cache_entry {
23 int color;
24 int level;
25 color_T rgb;
28 static inline int
29 color_distance(const struct rgb *c1, const struct rgb *c2)
31 int r = c1->r - c2->r;
32 int g = c1->g - c2->g;
33 int b = c1->b - c2->b;
35 return (3 * r * r) + (4 * g * g) + (2 * b * b);
38 #define RED_COLOR_MASK 0x00FF0000
39 #define GREEN_COLOR_MASK 0x0000FF00
40 #define BLUE_COLOR_MASK 0x000000FF
42 #define RED_COLOR(color) (((color) & RED_COLOR_MASK) >> 16)
43 #define GREEN_COLOR(color) (((color) & GREEN_COLOR_MASK) >> 8)
44 #define BLUE_COLOR(color) (((color) & BLUE_COLOR_MASK) >> 0)
46 #define RED(color) (RED_COLOR(color) << 3)
47 #define GREEN(color) (GREEN_COLOR(color) << 2)
48 #define BLUE(color) (BLUE_COLOR(color) << 0)
50 #define RGBCOLOR(color) (RED(color) + GREEN(color) + BLUE(color))
52 #define RGB_HASH_SIZE 4096
53 #define HASH_RGB(color, l) ((RGBCOLOR(color) + (l)) & (RGB_HASH_SIZE - 1))
55 /** Initialize a rgb struct from a color_T
56 * @relates rgb */
57 #define INIT_RGB(color) \
58 { RED_COLOR(color), GREEN_COLOR(color), BLUE_COLOR(color) }
60 /** Locates the nearest terminal color. */
61 static inline unsigned char
62 get_color(color_T color, const struct rgb *palette, int level)
64 static struct rgb_cache_entry cache[RGB_HASH_SIZE];
65 struct rgb_cache_entry *rgb_cache = &cache[HASH_RGB(color, level)];
67 /* Uninitialized cache entries have level set to zero. */
68 if (rgb_cache->level == 0
69 || rgb_cache->level != level
70 || rgb_cache->rgb != color) {
71 struct rgb rgb = INIT_RGB(color);
72 unsigned char nearest_color = 0;
73 int min_dist = color_distance(&rgb, &palette[0]);
74 int i;
76 #ifdef CONFIG_DEBUG
77 if (level <= 0)
78 INTERNAL("get_color() called with @level <= 0");
79 #endif
81 for (i = 1; i < level; i++) {
82 int dist = color_distance(&rgb, &palette[i]);
84 if (dist < min_dist) {
85 min_dist = dist;
86 nearest_color = i;
90 rgb_cache->color = nearest_color;
91 rgb_cache->level = level;
92 rgb_cache->rgb = color;
95 return rgb_cache->color;
98 #undef INIT_RGB
99 #undef HASH_RGB
100 #undef RGB_HASH_SIZE
102 #undef RED
103 #undef GREEN
104 #undef BLUE
106 #undef RED_COLOR
107 #undef GREEN_COLOR
108 #undef BLUE_COLOR
110 #undef RED_COLOR_MASK
111 #undef GREEN_COLOR_MASK
112 #undef BLUE_COLOR_MASK
114 /** Controls what color ranges to use when setting the terminal color.
115 * @todo TODO: Part of the 256 color palette is gray scale, maybe we
116 * could experiment with a grayscale mode. ;) --jonas */
117 enum palette_range {
118 PALETTE_FULL = 0,
119 PALETTE_HALF,
121 PALETTE_RANGES, /* XXX: Keep last */
124 struct color_mode_info {
125 const struct rgb *palette;
127 struct {
128 int bg;
129 int fg;
130 } palette_range[PALETTE_RANGES];
133 static const struct color_mode_info color_mode_16 = {
134 palette16,
136 /* PALETTE_FULL */ { 8, 16 },
137 /* PALETTE_HALF */ { 8, 8 },
141 #ifdef CONFIG_88_COLORS
142 static const struct color_mode_info color_mode_88 = {
143 palette88,
145 /* PALETTE_FULL */ { 88, 88 },
146 /* PALETTE_HALF */ { 88, 44 },
149 #endif
151 #ifdef CONFIG_256_COLORS
152 static const struct color_mode_info color_mode_256 = {
153 palette256,
155 /* PALETTE_FULL */ { 256, 256 },
156 /* PALETTE_HALF */ { 256, 128 },
159 #endif
161 static const struct color_mode_info *const color_modes[] = {
162 /* COLOR_MODE_MONO */ &color_mode_16,
163 /* COLOR_MODE_16 */ &color_mode_16,
164 #ifdef CONFIG_88_COLORS
165 /* COLOR_MODE_88 */ &color_mode_88,
166 #else
167 /* COLOR_MODE_88 */ &color_mode_16,
168 #endif
169 #ifdef CONFIG_256_COLORS
170 /* COLOR_MODE_256 */ &color_mode_256,
171 #else
172 /* COLOR_MODE_256 */ &color_mode_16,
173 #endif
174 /* @set_term_color reads @color_modes[COLOR_MODE_TRUE_COLOR]
175 * only if CONFIG_TRUE_COLOR is not defined. */
176 /* COLOR_MODE_TRUE_COLOR */ &color_mode_16,
178 /** Get a compile-time error if the ::color_modes array has the wrong
179 * size. */
180 typedef int assert_enough_color_modes[
181 (sizeof(color_modes) / sizeof(color_modes[0]) == COLOR_MODES)
182 ? 1 : -1];
184 /* Colors values used in the foreground color table:
186 * 0 == black 8 == darkgrey (brightblack ;)
187 * 1 == red 9 == brightred
188 * 2 == green 10 == brightgreen
189 * 3 == brown 11 == brightyellow
190 * 4 == blue 12 == brightblue
191 * 5 == magenta 13 == brightmagenta
192 * 6 == cyan 14 == brightcyan
193 * 7 == white 15 == brightwhite
195 * Bright colors will be rendered bold. */
197 /** Map foreground colors to more visible ones on various backgrounds.
198 * Use like: fg = fg_color[fg][bg];
200 * This table is based mostly on wild guesses of mine. Feel free to
201 * correct it. --pasky */
202 static const unsigned char fg_color[16][8] = {
203 /* bk r gr br bl m c w */
205 /* 0 (black) */
206 { 7, 0, 0, 0, 7, 0, 0, 0 },
207 /* 1 (red) */
208 { 1, 9, 1, 9, 9, 9, 1, 1 },
209 /* 2 (green) */
210 { 2, 2, 10, 2, 2, 2, 10, 10 },
211 /* 3 (brown) */
212 { 3, 11, 3, 11, 3, 11, 3, 3 },
213 /* 4 (blue) */
214 { 12, 12, 6, 4, 12, 6, 4, 4 },
215 /* 5 (magenta) */
216 { 5, 13, 5, 13, 13, 13, 5, 5 },
217 /* 6 (cyan) */
218 { 6, 6, 14, 6, 6, 6, 14, 14 },
219 /* 7 (grey) */
220 { 7, 7, 0, 7, 7, 7, 0, 0 }, /* Don't s/0/8/, messy --pasky */
221 /* 8 (darkgrey) */
222 { 15, 15, 8, 15, 15, 15, 8, 8 },
223 /* 9 (brightred) */
224 { 9, 9, 1, 9, 9, 9, 1, 9 }, /* I insist on 7->9 --pasky */
225 /* 10 (brightgreen) */
226 { 10, 10, 10, 10, 10, 10, 10, 10 },
227 /* 11 (brightyellow) */
228 { 11, 11, 11, 11, 11, 11, 11, 11 },
229 /* 12 (brightblue) */
230 { 12, 12, 6, 4, 6, 6, 4, 12 },
231 /* 13 (brightmagenta) */
232 { 13, 13, 5, 13, 13, 13, 5, 5 },
233 /* 14 (brightcyan) */
234 { 14, 14, 14, 14, 14, 14, 14, 14 },
235 /* 15 (brightwhite) */
236 { 15, 15, 15, 15, 15, 15, 15, 15 },
239 /* When determining whether to use negative image we make the most significant
240 * be least significant. */
241 #define CMPCODE(c) (((c) << 1 | (c) >> 2) & TERM_COLOR_MASK)
242 #define use_inverse(bg, fg) CMPCODE(fg & TERM_COLOR_MASK) < CMPCODE(bg)
244 NONSTATIC_INLINE void
245 set_term_color16(struct screen_char *schar, enum color_flags flags,
246 unsigned char fg, unsigned char bg)
248 /* Adjusts the foreground color to be more visible. */
249 if (flags & COLOR_INCREASE_CONTRAST) {
250 fg = fg_color[fg][bg];
253 /* Add various color enhancement based on the attributes. */
254 if (schar->attr) {
255 if (schar->attr & SCREEN_ATTR_ITALIC)
256 fg ^= 0x01;
258 if (schar->attr & SCREEN_ATTR_BOLD)
259 fg |= SCREEN_ATTR_BOLD;
261 if ((schar->attr & SCREEN_ATTR_UNDERLINE)
262 && (flags & COLOR_ENHANCE_UNDERLINE)) {
263 fg |= SCREEN_ATTR_BOLD;
264 fg ^= 0x04;
268 /* Adjusts the foreground color to be more visible. */
269 if ((flags & COLOR_INCREASE_CONTRAST)
270 || (bg == fg && (flags & COLOR_ENSURE_CONTRAST))) {
271 if (flags & COLOR_ENSURE_INVERTED_CONTRAST) {
272 unsigned char contrastbg = fg_color[fg][bg];
274 fg = bg;
275 bg = contrastbg;
276 } else {
277 fg = fg_color[fg][bg];
281 if (fg & SCREEN_ATTR_BOLD) {
282 schar->attr |= SCREEN_ATTR_BOLD;
285 if (use_inverse(bg, fg)) {
286 schar->attr |= SCREEN_ATTR_STANDOUT;
288 schar->color[0] = (bg << 4 | fg);
291 void
292 set_term_color(struct screen_char *schar, struct color_pair *pair,
293 enum color_flags flags, enum color_mode color_mode)
295 const struct color_mode_info *mode;
296 enum palette_range palette_range = PALETTE_FULL;
297 unsigned char fg, bg;
299 assert(color_mode >= COLOR_MODE_DUMP && color_mode < COLOR_MODES);
301 /* Options for the various color modes. */
302 switch (color_mode) {
304 case COLOR_MODE_MONO:
305 /* TODO: A better way if possible to find out whether to
306 * inverse the fore- and backgroundcolor. Else figure out what:
308 * CMPCODE(c) (((c) << 1 | (c) >> 2) & TERM_COLOR_MASK)
310 * mean. :) --jonas */
312 /* Decrease the range of the 16 palette to not include
313 * bright colors. */
314 if (flags & COLOR_DECREASE_LIGHTNESS) {
315 palette_range = PALETTE_HALF;
316 schar->attr |= SCREEN_ATTR_STANDOUT;
318 break;
320 default:
321 /* If the desired color mode was not compiled in,
322 * use 16 colors. */
323 case COLOR_MODE_16:
324 /* Decrease the range of the 16 palette to not include
325 * bright colors. */
326 if (flags & COLOR_DECREASE_LIGHTNESS)
327 palette_range = PALETTE_HALF;
328 break;
329 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
330 #ifdef CONFIG_88_COLORS
331 case COLOR_MODE_88:
332 #endif
333 #ifdef CONFIG_256_COLORS
334 case COLOR_MODE_256:
335 #endif
336 /* TODO: Handle decrease lightness by converting to
337 * hue-lightness-saturation color model */
338 break;
339 #endif
340 #ifdef CONFIG_TRUE_COLOR
341 case COLOR_MODE_TRUE_COLOR:
342 /* TODO: make it better */
343 if (pair->foreground == pair->background && (flags & COLOR_ENSURE_CONTRAST)) {
344 if (flags & COLOR_ENSURE_INVERTED_CONTRAST) {
345 pair->background = (pair->foreground == 0) ? 0xffffff : 0;
346 } else {
347 pair->foreground = (pair->background == 0) ? 0xffffff : 0;
350 schar->color[0] = (pair->foreground >> 16) & 255; /* r */
351 schar->color[1] = (pair->foreground >> 8) & 255; /* g */
352 schar->color[2] = pair->foreground & 255; /* b */
353 schar->color[3] = (pair->background >> 16) & 255; /* r */
354 schar->color[4] = (pair->background >> 8) & 255; /* g */
355 schar->color[5] = pair->background & 255; /* b */
356 return;
357 #endif
358 case COLOR_MODE_DUMP:
359 return;
361 case COLOR_MODES:
362 /* This is caught by the assert() above. */
363 return;
366 assert(schar);
368 mode = color_modes[color_mode];
369 fg = get_color(pair->foreground, mode->palette, mode->palette_range[palette_range].fg);
370 bg = get_color(pair->background, mode->palette, mode->palette_range[palette_range].bg);
372 switch (color_mode) {
373 case COLOR_MODES:
374 case COLOR_MODE_DUMP:
375 INTERNAL("Bad color mode, it should _never_ occur here.");
376 break;
378 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
379 #ifdef CONFIG_88_COLORS
380 case COLOR_MODE_88:
381 #endif
382 #ifdef CONFIG_256_COLORS
383 case COLOR_MODE_256:
384 #endif
385 /* Adjusts the foreground color to be more visible. */
386 /* TODO: Be smarter! Here we just choose either black or white
387 * ANSI color to make sure the color is visible. Pasky
388 * mentioned maybe calculating a distance and choosing some
389 * intermediate color. --jonas */
390 /* TODO: Maybe also do something to honour the increase_contrast
391 * option. --jonas */
392 if (bg == fg && (flags & COLOR_ENSURE_CONTRAST)) {
393 if (flags & COLOR_ENSURE_INVERTED_CONTRAST) {
394 bg = (fg == 0) ? 15 : 0;
395 } else {
396 fg = (bg == 0) ? 15 : 0;
400 TERM_COLOR_FOREGROUND_256(schar->color) = fg;
401 TERM_COLOR_BACKGROUND_256(schar->color) = bg;
402 break;
403 #endif
404 #ifdef CONFIG_TRUE_COLOR
405 case COLOR_MODE_TRUE_COLOR:
406 return;
407 #endif
408 default:
409 /* If the desired color mode was not compiled in,
410 * use 16 colors. */
411 case COLOR_MODE_MONO:
412 case COLOR_MODE_16:
413 set_term_color16(schar, flags, fg, bg);
414 break;