From 56dec05d29098b151421625c68525c2c3961e574 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Fri, 3 Oct 2014 14:44:41 +0200 Subject: [PATCH] terminal/screen: add color converter Terminals use pseudo color-codes mixed with 8bit and 24bit colors. Provide a color-converter so external renderers only have to deal with ARGB32 colors. This requires a color-palette as input as there's no fixed mapping. We provide a default, but maybe we wanna support external palettes in the future. --- src/libsystemd-terminal/term-parser.c | 116 ++++++++++++++++++++++++++++++++++ src/libsystemd-terminal/term-screen.c | 26 +------- src/libsystemd-terminal/term.h | 2 + 3 files changed, 120 insertions(+), 24 deletions(-) diff --git a/src/libsystemd-terminal/term-parser.c b/src/libsystemd-terminal/term-parser.c index f9326d563a..8ec6345d60 100644 --- a/src/libsystemd-terminal/term-parser.c +++ b/src/libsystemd-terminal/term-parser.c @@ -35,6 +35,122 @@ #include "term-internal.h" #include "util.h" +static const uint8_t default_palette[18][3] = { + { 0, 0, 0 }, /* black */ + { 205, 0, 0 }, /* red */ + { 0, 205, 0 }, /* green */ + { 205, 205, 0 }, /* yellow */ + { 0, 0, 238 }, /* blue */ + { 205, 0, 205 }, /* magenta */ + { 0, 205, 205 }, /* cyan */ + { 229, 229, 229 }, /* light grey */ + { 127, 127, 127 }, /* dark grey */ + { 255, 0, 0 }, /* light red */ + { 0, 255, 0 }, /* light green */ + { 255, 255, 0 }, /* light yellow */ + { 92, 92, 255 }, /* light blue */ + { 255, 0, 255 }, /* light magenta */ + { 0, 255, 255 }, /* light cyan */ + { 255, 255, 255 }, /* white */ + + { 229, 229, 229 }, /* light grey */ + { 0, 0, 0 }, /* black */ +}; + +static uint32_t term_color_to_argb32(const term_color *color, const term_attr *attr, const uint8_t *palette) { + static const uint8_t bval[] = { + 0x00, 0x5f, 0x87, + 0xaf, 0xd7, 0xff, + }; + uint8_t r, g, b, t; + + assert(color); + + if (!palette) + palette = (void*)default_palette; + + switch (color->ccode) { + case TERM_CCODE_RGB: + r = color->red; + g = color->green; + b = color->blue; + + break; + case TERM_CCODE_256: + t = color->c256; + if (t < 16) { + r = palette[t * 3 + 0]; + g = palette[t * 3 + 1]; + b = palette[t * 3 + 2]; + } else if (t < 232) { + t -= 16; + b = bval[t % 6]; + t /= 6; + g = bval[t % 6]; + t /= 6; + r = bval[t % 6]; + } else { + t = (t - 232) * 10 + 8; + r = t; + g = t; + b = t; + } + + break; + case TERM_CCODE_BLACK ... TERM_CCODE_LIGHT_WHITE: + t = color->ccode - TERM_CCODE_BLACK; + + /* bold causes light colors */ + if (t < 8 && attr->bold) + t += 8; + + r = palette[t * 3 + 0]; + g = palette[t * 3 + 1]; + b = palette[t * 3 + 2]; + break; + case TERM_CCODE_DEFAULT: + /* fallthrough */ + default: + t = 16 + !(color == &attr->fg); + r = palette[t * 3 + 0]; + g = palette[t * 3 + 1]; + b = palette[t * 3 + 2]; + break; + } + + return (0xff << 24) | (r << 16) | (g << 8) | b; +} + +/** + * term_attr_to_argb32() - Encode terminal colors as native ARGB32 value + * @color: Terminal attributes to work on + * @fg: Storage for foreground color (or NULL) + * @bg: Storage for background color (or NULL) + * @palette: The color palette to use (or NULL for default) + * + * This encodes the colors attr->fg and attr->bg as native-endian ARGB32 values + * and returns them. Any color conversions are automatically applied. + */ +void term_attr_to_argb32(const term_attr *attr, uint32_t *fg, uint32_t *bg, const uint8_t *palette) { + uint32_t f, b, t; + + assert(attr); + + f = term_color_to_argb32(&attr->fg, attr, palette); + b = term_color_to_argb32(&attr->bg, attr, palette); + + if (attr->inverse) { + t = f; + f = b; + b = t; + } + + if (fg) + *fg = f; + if (bg) + *bg = b; +} + /** * term_utf8_encode() - Encode single UCS-4 character as UTF-8 * @out_utf8: output buffer of at least 4 bytes or NULL diff --git a/src/libsystemd-terminal/term-screen.c b/src/libsystemd-terminal/term-screen.c index ccfb9a450c..3f7ef1cf3c 100644 --- a/src/libsystemd-terminal/term-screen.c +++ b/src/libsystemd-terminal/term-screen.c @@ -2944,31 +2944,9 @@ static int screen_SGR(term_screen *screen, const term_seq *seq) { if (i >= seq->n_args || seq->args[i] < 0) break; + dst->ccode = TERM_CCODE_256; code = seq->args[i]; - if (code < 16) { - dst->ccode = code; - } else if (code < 232) { - static const uint8_t bval[] = { - 0x00, 0x5f, 0x87, - 0xaf, 0xd7, 0xff, - }; - - dst->ccode = TERM_CCODE_256; - dst->c256 = code; - code -= 16; - dst->blue = bval[code % 6]; - code /= 6; - dst->green = bval[code % 6]; - code /= 6; - dst->red = bval[code % 6]; - } else if (code < 256) { - dst->ccode = TERM_CCODE_256; - dst->c256 = code; - code = (code - 232) * 10 + 8; - dst->red = code; - dst->green = code; - dst->blue = code; - } + dst->c256 = code < 256 ? code : 0; break; } diff --git a/src/libsystemd-terminal/term.h b/src/libsystemd-terminal/term.h index 5228ce0601..8efd48b263 100644 --- a/src/libsystemd-terminal/term.h +++ b/src/libsystemd-terminal/term.h @@ -97,6 +97,8 @@ struct term_attr { unsigned int hidden : 1; /* hidden */ }; +void term_attr_to_argb32(const term_attr *attr, uint32_t *fg, uint32_t *bg, const uint8_t *palette); + /* * UTF-8 */